File: motion-require.rb

package info (click to toggle)
ruby-motion-require 0.2.0-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 140 kB
  • sloc: ruby: 157; makefile: 7
file content (144 lines) | stat: -rw-r--r-- 4,355 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
require 'ripper'
require 'pathname'

module Motion
  module Require

    class << self
      attr_accessor :require_relative_enabled
    end

    class RequireBuilder < Ripper::SexpBuilder
      REQUIREMENT_TOKENS = %w[motion_require require_relative]

      attr_accessor :requires

      def requires
        @requires ||= []
      end

      def on_command(command, args) # scanner event
        type, name, position = command
        if valid_require_command(name)
          file = parse_args(args)
          requires << file
        end
      end

      def valid_require_command(name)
        if name == 'require_relative' && !Motion::Require.require_relative_enabled
          raise NoMethodError, 'require_relative is not enabled, see Motion::Require.require_relative_enabled'
        end
        REQUIREMENT_TOKENS.include?(name)
      end

      def parse_args(args)
        value = nil
        args.each do |arg|
          if arg.is_a?(Array)
            type = arg.first
            if type == :@tstring_content
              return arg[1]
            end

            value = parse_args(arg)
          end
        end
        value
      end
    end

    module_function
    def dependencies_for(files)
      dependencies = {}
      files.each do |file_path|
        requires = requires_in(file_path)
        if !requires.empty?
          dependencies[file_path] = requires.map { |required|
            required_path = resolve_path(file_path, required)
            if !File.exist?(required_path) && File.extname(required) != ".rb"
              required_path += ".rb"
            end

            if !File.exist?(required_path)
              # TODO: Report line number of failing require
              raise LoadError, "ERROR! In `#{file_path}', could not require `#{required}', file not found."
            end

            required_path
          }
          dependencies[file_path].unshift ext_file
        end
      end
      dependencies
    end

    def requires_in(file)
      parser = Motion::Require::RequireBuilder.new(File.read(file))
      parser.parse
      parser.requires
    end

    # Join `required` to directory containing `source`.
    # Preserves relative/absolute nature of source
    def resolve_path(source, required)
      Pathname.new(source).dirname.join(required.to_str).cleanpath.to_path
    end

    # Scan specified files. When nil, fallback to RubyMotion's default (app/**/*.rb).
    def all(files=nil, options={})
      # if you want the default 'app.files', you can just pass in the options
      if files.is_a?(Hash) && options == {}
        options = files
        files = nil
      end

      check_platform = options.fetch(:platform, nil)
      current_platform = App.respond_to?(:template) ? App.template : :ios
      return unless Motion::Require.check_platform(current_platform, check_platform)

      Motion::Project::App.setup do |app|
        app.exclude_from_detect_dependencies << ext_file

        if files.nil? || files.empty?
          app.files.push ext_file
          app.exclude_from_detect_dependencies += app.files
          app.files_dependencies dependencies_for(app.files.flatten)
        else
        # Place files prior to those in ./app, otherwise at the end.
          preceding_app = app.files.index { |f| f =~ %r(^(?:\./)?app/) } || -1
          required = Array(files).map { |f| explicit_relative(f) }
          app.exclude_from_detect_dependencies += required
          app.files.insert(preceding_app, ext_file, *required)
          app.files.uniq! # Prevent redundancy

          app.files_dependencies dependencies_for(required)
        end
      end
    end

    def check_platform(current_platform, check_platform)
      case check_platform
      when nil
        true
      when Array
        check_platform.include?(current_platform)
      when Symbol
        current_platform == check_platform
      else
        puts "Unrecognized value for 'check_platform': #{check_platform.inspect}"
        false
      end
    end

    # RubyMotion prefers relative paths to be explicitly prefixed with ./
    def explicit_relative(path)
      # Paths that do not start with "/", "./", or "../" will be prefixed with ./
      path.sub(%r(^(?!\.{0,2}/)), './')
    end

    def ext_file
      File.expand_path(File.join(File.dirname(__FILE__), "../motion/ext.rb"))
    end
  end
end