motion_blender 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4e0a26f4930010d2e1105308d10fb471cc3e3892
4
- data.tar.gz: f537cd46e5096820fc007f634aa127384a7d9bfe
3
+ metadata.gz: 96d8bca0eabd7579ef143fb1d513f19f3107b96d
4
+ data.tar.gz: 0400cc6dc5ed4eddf57120ddcdc4a444ac0ce710
5
5
  SHA512:
6
- metadata.gz: bafb18c7858a0d59a7c341ebc1ed6ae809622b3b86c99d9435e0992cc36cead2e819251249798fbca5db20b162363bb01c45d16332fe150c9cdf463d76fb6a0f
7
- data.tar.gz: 9948b5186e33231b7d286d6b6b9cd013a3ae3c7a008e6954c4cc4ab13a70bb15a5475d14c64bf572088c416524fe0a3fd0ddd8c55c617b42290d468940e2841f
6
+ metadata.gz: 48421b7fba3cb60c261a3958d60c439b0b43fe9bfadc3fb6f297b567ef647d56cb549de3fa3c367f3436034f068ec1c099cad027c4455cd55296aacf73a88463
7
+ data.tar.gz: b5f0a8a4a50c58bcea1e487d4b4d40d5e4999cc4d57e48a95f760f12ddfe8006d7375900938d66d489c49396fccde30c36f492a71fdd0bb5bf82d6648e96c14f
@@ -19,6 +19,11 @@ Metrics/ModuleLength:
19
19
  Metrics/AbcSize:
20
20
  Max: 25
21
21
 
22
+ # Cop supports --auto-correct.
23
+ Performance/RedundantBlockCall:
24
+ Exclude:
25
+ - 'motion/**/*'
26
+
22
27
  # Configuration parameters: EnforcedStyle, SupportedStyles, ProceduralMethods, FunctionalMethods, IgnoredMethods.
23
28
  Style/BlockDelimiters:
24
29
  Exclude:
@@ -73,7 +78,7 @@ Style/MultilineBlockLayout:
73
78
  - 'spec/**/*'
74
79
 
75
80
  # Cop supports --auto-correct.
76
- Style/SingleSpaceBeforeFirstArg:
81
+ Style/SpaceBeforeFirstArg:
77
82
  Enabled: false
78
83
 
79
84
  # Cop supports --auto-correct.
@@ -1,8 +1,10 @@
1
- os: osx
2
1
  language: ruby
3
- rvm:
4
- - 2.2.3
2
+ matrix:
3
+ include:
4
+ - rvm: 2.2.5
5
+ os: osx
6
+ osx_image: xcode7.3 # OSX 10.11
5
7
  before_install:
6
- - gem install bundler -v 1.10.6
8
+ - gem install bundler
7
9
  - brew update
8
10
  - brew install graphviz --with-gts
@@ -11,24 +11,48 @@ module MotionBlender
11
11
  @analyzed_files = Set.new
12
12
  @files = []
13
13
  @dependencies = {}
14
+ @autoloads = {}
15
+ @file_stack = []
14
16
  end
15
17
 
16
18
  def analyze file, backtrace = []
19
+ @file_stack.push file
20
+
17
21
  return if MotionBlender.config.excepted_files.include? file
18
22
  return if @analyzed_files.include? file
19
23
  @analyzed_files << file
20
24
 
21
- requires = parse file, backtrace
22
-
23
- if requires.present?
24
- @dependencies[file] = requires.map(&:file)
25
- @files = [*@files, file, *@dependencies[file]].uniq
26
- requires.each do |req|
27
- req.run_callbacks :require do
28
- analyze req.file, [req.trace, *backtrace]
29
- end
25
+ parser = parse file, backtrace
26
+ requires = merge parser
27
+ requires.each do |req|
28
+ req.run_callbacks :require do
29
+ analyze req.file, [req.trace, *backtrace]
30
30
  end
31
31
  end
32
+ ensure
33
+ @file_stack.pop
34
+ end
35
+
36
+ def pick_autoloads parser
37
+ parser.requires.select(&:autoload?).each do |req|
38
+ @autoloads[req.autoload_const_name] ||= []
39
+ @autoloads[req.autoload_const_name] << req
40
+ end
41
+ end
42
+
43
+ def merge parser
44
+ pick_autoloads parser
45
+
46
+ reqs = parser.dependent_requires(autoloads: @autoloads)
47
+ reqs = reqs.reject { |req| @file_stack.include? req.file }
48
+ if reqs.present?
49
+ files = reqs.map(&:file)
50
+ @dependencies[parser.file] = files
51
+ @files = [*@files, parser.file, *files].uniq
52
+ reqs
53
+ else
54
+ []
55
+ end
32
56
  end
33
57
 
34
58
  def parse file, backtrace
@@ -39,7 +63,7 @@ module MotionBlender
39
63
  err.set_backtrace [parser.last_trace, *backtrace].compact
40
64
  raise err
41
65
  end
42
- parser.requires
66
+ parser
43
67
  end
44
68
  end
45
69
  end
@@ -2,7 +2,7 @@ module MotionBlender
2
2
  class Analyzer
3
3
  class Cache
4
4
  attr_reader :file, :hit
5
- alias_method :hit?, :hit
5
+ alias hit? hit
6
6
 
7
7
  def initialize file
8
8
  @file = Pathname.new(file)
@@ -6,11 +6,13 @@ module MotionBlender
6
6
  attr_reader :source
7
7
  attr_reader :trace, :requires
8
8
  attr_reader :dynamic
9
- alias_method :dynamic?, :dynamic
9
+ alias dynamic? dynamic
10
+ attr_reader :done
11
+ alias done? done
10
12
 
11
13
  def initialize source
12
14
  @source = source
13
- @trace = "#{source.file}:#{source.line}:in `#{source.method}'"
15
+ @trace = source.to_s
14
16
  @requires = []
15
17
  @dynamic = false
16
18
  end
@@ -19,19 +21,22 @@ module MotionBlender
19
21
  return if @source.evaluated?
20
22
  @source.evaluated!
21
23
 
22
- @requires = Collector.collect_requires(@source)
24
+ @requires = Collector.new(@source).collect_requires
23
25
  @requires.each do |req|
24
26
  req.trace = @trace
25
27
  end
28
+ @done = true
26
29
  self
27
30
  rescue StandardError, ScriptError => err
28
31
  recover_from_error err
29
32
  end
30
33
 
34
+ private
35
+
31
36
  def recover_from_error err
32
37
  @source = @source.parent
33
38
  @source = @source.parent if @source && @source.type.rescue?
34
- fail LoadError, err.message unless @source
39
+ raise LoadError, err.message unless @source
35
40
  @dynamic = true
36
41
  run
37
42
  end
@@ -10,7 +10,7 @@ module MotionBlender
10
10
  include ActiveSupport::Callbacks
11
11
  define_callbacks :parse
12
12
 
13
- attr_reader :file, :evaluators, :cache
13
+ attr_reader :file, :evaluators, :cache, :referring_constants
14
14
 
15
15
  def initialize file
16
16
  @file = file.to_s
@@ -18,17 +18,22 @@ module MotionBlender
18
18
  end
19
19
 
20
20
  def parse
21
- srcs = cache.fetch do
21
+ cached = cache.fetch do
22
22
  run_callbacks :parse do
23
- traverse(Source.parse_file(@file))
24
- @evaluators.map(&:source).map(&:attributes)
23
+ root = Source.parse_file(@file)
24
+ traverse(root)
25
+ {
26
+ sources: processed_sources.map(&:attributes),
27
+ referring_constants: root.referring_constants
28
+ }
25
29
  end
26
30
  end
27
- if srcs && cache.hit?
28
- srcs.each do |attrs|
31
+ if cached && cache.hit?
32
+ cached[:sources].each do |attrs|
29
33
  evaluate Source.new(attrs)
30
34
  end
31
35
  end
36
+ @referring_constants = cached[:referring_constants]
32
37
  self
33
38
  end
34
39
 
@@ -40,7 +45,7 @@ module MotionBlender
40
45
  if Collector.requirable?(source)
41
46
  evaluate source
42
47
  elsif Collector.acceptable?(source)
43
- source.children.each { |src| traverse src }
48
+ source.children.compact.each { |src| traverse src }
44
49
  end
45
50
  end
46
51
 
@@ -53,9 +58,31 @@ module MotionBlender
53
58
  @evaluators.map(&:requires).flatten
54
59
  end
55
60
 
61
+ def processed_sources
62
+ @evaluators.select(&:done?).map(&:source)
63
+ end
64
+
56
65
  def last_trace
57
66
  @evaluators.last.try :trace
58
67
  end
68
+
69
+ def autoloads_with autoloads
70
+ referring_constants.map do |mods, const|
71
+ key =
72
+ mods
73
+ .length.downto(0)
74
+ .map { |i| [*mods.take(i), const].join('::') }
75
+ .find { |k| autoloads.key?(k) }
76
+ autoloads[key]
77
+ end.flatten.compact
78
+ end
79
+
80
+ def dependent_requires opts = {}
81
+ autoloads = opts[:autoloads]
82
+ reqs = requires.reject(&:autoload?)
83
+ reqs += autoloads_with(autoloads) if autoloads
84
+ reqs.uniq(&:file)
85
+ end
59
86
  end
60
87
  end
61
88
  end
@@ -22,55 +22,81 @@ module MotionBlender
22
22
  (source.children.first.code != 'MotionBlender.raketime')
23
23
  end
24
24
 
25
- def collect_requires source
26
- collector = new(source, interpreters)
27
- interpreters.each do |interpreter|
28
- get_refinement_for(interpreter.receiver).module_eval do
29
- define_method interpreter.method do |*args, &proc|
30
- collector.interpreters[interpreter.key].interpret(*args, &proc)
31
- end
32
- end
25
+ def refinements
26
+ @refinements ||= Hash.new do |hash, key|
27
+ hash[key] = Module.new { key.prepend self }
33
28
  end
34
- Object.new.instance_eval(source.code, source.file, source.line)
35
- collector.requires
36
- ensure
37
- clear_refinements
38
29
  end
30
+ end
39
31
 
40
- private
32
+ attr_accessor :source, :interpreters, :requires
41
33
 
42
- def refinements
43
- @refinements ||= {}
34
+ def initialize source
35
+ @source = source
36
+ @interpreters = self.class.interpreters.map do |interpreter|
37
+ [interpreter.key, interpreter.new(self)]
38
+ end.to_h
39
+ @requires = []
40
+ end
41
+
42
+ def collect_requires
43
+ obj = evaluating_object
44
+ if obj.is_a? Module
45
+ with_refinements do
46
+ obj.module_eval(source.code, source.file, source.line)
47
+ end
48
+ else
49
+ with_refinements do
50
+ obj.instance_eval(source.code, source.file, source.line)
51
+ end
44
52
  end
53
+ requires
54
+ end
45
55
 
46
- def get_refinement_for klass
47
- refinements[klass] ||=
48
- begin
49
- Module.new do
50
- klass.prepend self
51
- end
52
- end
56
+ private
57
+
58
+ delegate :refinements, to: :class
59
+
60
+ def evaluating_object
61
+ obj = Object.new
62
+ constants = @source.global_constants.select do |c|
63
+ Object.const_defined? c.to_sym
53
64
  end
65
+ constants.each { |c| obj.instance_eval "#{c} = ::#{c}" }
66
+ if @source.wrapping_modules.present?
67
+ ms = @source.wrapping_modules.map { |p| p.join(' ') }
68
+ s = [*ms, 'self', *Array.new(ms.length, 'end')].join(';')
69
+ obj = obj.instance_eval s
70
+ end
71
+ obj
72
+ end
54
73
 
55
- def clear_refinements
56
- refinements.each do |_, mod|
57
- mod.module_eval do
58
- instance_methods.each do |m|
59
- remove_method m
60
- end
74
+ def with_refinements
75
+ apply_refinements
76
+ yield
77
+ ensure
78
+ clear_refinements
79
+ end
80
+
81
+ def apply_refinements
82
+ interpreters.each do |_, interpreter|
83
+ refinements[interpreter.receiver].module_eval do
84
+ define_method interpreter.method do |*args, &proc|
85
+ interpreter.object = self
86
+ interpreter.interpret(*args, &proc)
61
87
  end
62
88
  end
63
89
  end
64
90
  end
65
91
 
66
- attr_accessor :source, :interpreters, :requires
67
-
68
- def initialize source, interpreters
69
- @source = source
70
- @interpreters = interpreters.map do |interpreter|
71
- [interpreter.key, interpreter.new(self)]
72
- end.to_h
73
- @requires = []
92
+ def clear_refinements
93
+ refinements.each do |_, mod|
94
+ mod.module_eval do
95
+ instance_methods.each do |m|
96
+ remove_method m
97
+ end
98
+ end
99
+ end
74
100
  end
75
101
  end
76
102
  end
@@ -4,7 +4,7 @@ module MotionBlender
4
4
  class DependencyGraph < Hash
5
5
  include TSort
6
6
 
7
- alias_method :tsort_each_node, :each_key
7
+ alias tsort_each_node each_key
8
8
 
9
9
  def tsort_each_child node, &block
10
10
  (self[node] || []).each(&block)
@@ -0,0 +1,23 @@
1
+ require 'motion_blender/interpreters/require_interpreter'
2
+
3
+ module MotionBlender
4
+ module Interpreters
5
+ class AutoloadInterpreter < RequireInterpreter
6
+ interprets :autoload
7
+
8
+ def interpret const_name, arg
9
+ req = super arg
10
+ req.autoload_const_name =
11
+ if object.is_a? Module
12
+ [*object.name.sub(/^#<.+?>::/, ''), const_name].join('::')
13
+ else
14
+ const_name.to_s
15
+ end
16
+ end
17
+ end
18
+
19
+ class ModuleAutoloadInterpreter < AutoloadInterpreter
20
+ interprets :autoload, receiver: Module
21
+ end
22
+ end
23
+ end
@@ -22,6 +22,7 @@ module MotionBlender
22
22
  end
23
23
 
24
24
  attr_reader :collector
25
+ attr_accessor :object
25
26
  delegate :method, :receiver, to: :class
26
27
  delegate :source, :requires, to: :collector
27
28
  delegate :file, to: :source
@@ -9,7 +9,7 @@ module MotionBlender
9
9
 
10
10
  def interpret
11
11
  dir = MotionBlender.config.motion_dirs.find { |d| file.start_with? d }
12
- fail 'not found in motion_dirs' unless dir
12
+ raise 'not found in motion_dirs' unless dir
13
13
  arg = Pathname.new(file).relative_path_from(Pathname.new(dir))
14
14
  @original = candidates_for(arg).find(&:file?).try(&:to_s)
15
15
  end
@@ -16,13 +16,12 @@ module MotionBlender
16
16
  req.file = resolve_path req.arg
17
17
  return if excluded_file? req.file
18
18
 
19
- yield req
20
- true
19
+ req
21
20
  end
22
21
 
23
22
  def resolve_path arg
24
23
  path = candidates(arg).find(&:file?)
25
- fail LoadError, "not found `#{arg}'" unless path
24
+ raise LoadError, "not found `#{arg}'" unless path
26
25
  explicit_relative path
27
26
  end
28
27
 
@@ -8,8 +8,10 @@ module MotionBlender
8
8
  interprets :require
9
9
 
10
10
  def interpret arg
11
- find_require(arg) do |req|
11
+ req = find_require(arg)
12
+ if req
12
13
  requires << req
14
+ req
13
15
  end
14
16
  end
15
17
 
@@ -1,17 +1,10 @@
1
- require 'motion_blender/interpreters/base'
1
+ require 'motion_blender/interpreters/require_interpreter'
2
2
 
3
3
  module MotionBlender
4
4
  module Interpreters
5
- class RequireRelativeInterpreter < Base
6
- include Requirable
5
+ class RequireRelativeInterpreter < RequireInterpreter
7
6
  interprets :require_relative
8
7
 
9
- def interpret arg
10
- find_require(arg) do |req|
11
- requires << req
12
- end
13
- end
14
-
15
8
  def candidates arg
16
9
  path = Pathname.new(file).dirname.join(arg)
17
10
  exts = path.extname.empty? ? ['', '.rb'] : ['']
@@ -7,7 +7,7 @@ module MotionBlender
7
7
  define_callbacks :require
8
8
 
9
9
  attr_reader :loader, :method, :arg
10
- attr_accessor :trace, :file
10
+ attr_accessor :trace, :file, :autoload_const_name
11
11
 
12
12
  def initialize loader, method, arg
13
13
  @loader = loader
@@ -18,5 +18,9 @@ module MotionBlender
18
18
  def match? arg_or_file
19
19
  [arg, file].compact.include?(arg_or_file)
20
20
  end
21
+
22
+ def autoload?
23
+ method == :autoload
24
+ end
21
25
  end
22
26
  end
@@ -1,8 +1,16 @@
1
1
  require 'parser/current'
2
2
  require 'motion_blender/flag_attribute'
3
+ require 'motion_blender/source/wrapping_modules'
4
+ require 'motion_blender/source/global_constants'
5
+ require 'motion_blender/source/referring_constants'
3
6
 
4
7
  module MotionBlender
5
8
  class Source
9
+ include FlagAttribute
10
+ include WrappingModules
11
+ include GlobalConstants
12
+ include ReferringConstants
13
+
6
14
  def self.parse code, attrs = {}
7
15
  attrs[:ast] = ::Parser::CurrentRuby.parse(code)
8
16
  new(attrs)
@@ -13,8 +21,6 @@ module MotionBlender
13
21
  new(ast: ast)
14
22
  end
15
23
 
16
- include FlagAttribute
17
-
18
24
  attr_reader :code, :file, :line, :parent, :type, :method, :ast
19
25
  flag_attribute :evaluated
20
26
 
@@ -42,13 +48,31 @@ module MotionBlender
42
48
 
43
49
  def children
44
50
  @children ||=
45
- if @ast
46
- @ast.children.grep(::Parser::AST::Node).map do |ast|
47
- Source.new(ast: ast, parent: self)
48
- end
49
- else
50
- []
51
- end
51
+ @ast
52
+ .try(:children).to_a
53
+ .select { |ast| ast.nil? || ast.is_a?(::Parser::AST::Node) }
54
+ .map { |ast| Source.new(ast: ast, parent: self) }
55
+ end
56
+
57
+ def child_at *args
58
+ i = args.shift
59
+ args.present? ? children[i].child_at(*args) : children[i]
60
+ end
61
+
62
+ def root?
63
+ parent.nil?
64
+ end
65
+
66
+ def root
67
+ root? ? self : parent.root
68
+ end
69
+
70
+ def ancestors
71
+ root? ? [self] : [self, *parent.ancestors]
72
+ end
73
+
74
+ def like_module?
75
+ type.module? || type.class?
52
76
  end
53
77
 
54
78
  def attributes
@@ -57,7 +81,9 @@ module MotionBlender
57
81
  'file' => @file,
58
82
  'line' => @line,
59
83
  'type' => @type.to_s,
60
- 'method' => @method.try(:to_s)
84
+ 'method' => @method.try(:to_s),
85
+ 'global_constants' => global_constants,
86
+ 'wrapping_modules' => wrapping_modules
61
87
  }
62
88
  end
63
89
  end
@@ -0,0 +1,24 @@
1
+ module MotionBlender
2
+ class Source
3
+ module GlobalConstants
4
+ def global_constants
5
+ @global_constants ||=
6
+ if root?
7
+ Array.wrap(find_global_constants).flatten.compact.uniq
8
+ else
9
+ root.global_constants
10
+ end
11
+ end
12
+
13
+ def find_global_constants
14
+ if like_module?
15
+ if children.first.type.const?
16
+ children.first.code.split('::', 2).first
17
+ end
18
+ else
19
+ children.compact.map(&:find_global_constants)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,22 @@
1
+ module MotionBlender
2
+ class Source
3
+ module ReferringConstants
4
+ def referring_constants
5
+ @referring_constants ||=
6
+ begin
7
+ child_constants =
8
+ children.map(&:referring_constants).inject(&:+).to_a
9
+ if referring_constant?
10
+ child_constants + [[wrapping_modules.map(&:last), code]]
11
+ else
12
+ child_constants.dup
13
+ end
14
+ end
15
+ end
16
+
17
+ def referring_constant?
18
+ type.const?
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,24 @@
1
+ module MotionBlender
2
+ class Source
3
+ module WrappingModules
4
+ def wrapping_modules
5
+ @wrapping_modules ||=
6
+ [*parent.try(:wrapping_modules), parent_module].compact
7
+ end
8
+
9
+ def parent_module
10
+ if module_content? || class_content?
11
+ [parent.type.to_s, parent.children.first.code]
12
+ end
13
+ end
14
+
15
+ def module_content?
16
+ parent && parent.type.module? && (parent.children[1] == self)
17
+ end
18
+
19
+ def class_content?
20
+ parent && parent.type.class? && (parent.children[2] == self)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,3 +1,3 @@
1
1
  module MotionBlender
2
- VERSION = '0.3.1'
2
+ VERSION = '0.4.0'.freeze
3
3
  end
@@ -5,6 +5,14 @@ class Object
5
5
  def require_relative *_
6
6
  end
7
7
 
8
+ def autoload *_
9
+ end
10
+
8
11
  def __ORIGINAL__ *_
9
12
  end
10
13
  end
14
+
15
+ class Module
16
+ def autoload *_
17
+ end
18
+ end
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
20
  spec.require_paths = ['lib']
21
21
 
22
- spec.add_runtime_dependency 'parser'
22
+ spec.add_runtime_dependency 'parser', '~> 2.3.1'
23
23
  spec.add_runtime_dependency 'activesupport', '~> 4.2'
24
24
  spec.add_runtime_dependency 'colorable', '~> 0.2'
25
25
  spec.add_runtime_dependency 'ruby-graphviz', '~> 1.2'
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: motion_blender
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - kayhide
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-02-09 00:00:00.000000000 Z
11
+ date: 2016-06-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parser
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 2.3.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: 2.3.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activesupport
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -268,6 +268,7 @@ files:
268
268
  - lib/motion_blender/flag_attribute.rb
269
269
  - lib/motion_blender/graph_maker.rb
270
270
  - lib/motion_blender/interpreters.rb
271
+ - lib/motion_blender/interpreters/autoload_interpreter.rb
271
272
  - lib/motion_blender/interpreters/base.rb
272
273
  - lib/motion_blender/interpreters/original_interpreter.rb
273
274
  - lib/motion_blender/interpreters/requirable.rb
@@ -276,6 +277,9 @@ files:
276
277
  - lib/motion_blender/rake_tasks.rb
277
278
  - lib/motion_blender/require.rb
278
279
  - lib/motion_blender/source.rb
280
+ - lib/motion_blender/source/global_constants.rb
281
+ - lib/motion_blender/source/referring_constants.rb
282
+ - lib/motion_blender/source/wrapping_modules.rb
279
283
  - lib/motion_blender/version.rb
280
284
  - motion/motion_blender/ext.rb
281
285
  - motion/motion_blender/ext/runtime.rb