system_navigation 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d0877e736a201bde13a94bb2fd54462777c30878
4
- data.tar.gz: 0f072306d97e8b2d3f2697c3601e279bc5b6ffdd
3
+ metadata.gz: b9f601aa746ea3e24e18be9512a868d9da486641
4
+ data.tar.gz: d73ce895b246cf274edefb11849d0d28a92637a1
5
5
  SHA512:
6
- metadata.gz: 4d9106cdc061404ae1ab11859ce526994ed3e478d4f21fc200304af7cf56bff513bb4c6b2fb1485ddb6963048d7eaa722e257920269a50f971eee7c92a30fc4a
7
- data.tar.gz: 41cb455b27e76c9bb4b54d5197d25a17f7823dc5f661ddebd3cdc5a15dc513834afb347279dda38f16f5da338bc709d30d174bf5166ab23a151c08f0c8085d16
6
+ metadata.gz: 6b54b78bbe3cb2bdb37ca96e13b0cd2a45bf4acff23cf0a453a450f46d9b4622f600775fed775d93eedaf5d7452c1544040d9fb2883119c26eb4e60c74e53004
7
+ data.tar.gz: 43f8a64c12f39bf151260de576446b2ac445b081160eb6fd806cd79d0960582688324f55a6924ce39bbd93a1f642d0334b8903905de38bdd4ea1feeb1f43016e
@@ -1,6 +1,10 @@
1
1
  System Navigation changelog
2
2
  ===========================
3
3
 
4
+ ### v0.2.0 (June 19, 2015)
5
+
6
+ * Initial release (for real now)
7
+
4
8
  ### v0.1.0 (June 11, 2015)
5
9
 
6
10
  * Initial release (sorta, not ready yet)
data/README.md CHANGED
@@ -1,16 +1,39 @@
1
1
  SystemNavigation
2
2
  ==
3
3
 
4
- * Repository: [https://github.com/kyrylo/system_navigation/](https://github.com/kyrylo/system_navigation/)
4
+ * [Repository](https://github.com/kyrylo/system_navigation/)
5
+ * [Documentation](http://www.rubydoc.info/gems/system_navigation)
5
6
 
6
7
  Description
7
8
  -----------
8
9
 
9
- SystemNavigation is a Ruby library that provides support for the navigation and
10
- introspection of Ruby programs. It is inspired by the eponymous class in
11
- Pharo/Squeak.
10
+ SystemNavigation is a Ruby library that provides additional introspection
11
+ capabilities for Ruby programs. The library defines a number of useful methods
12
+ that allow querying:
12
13
 
13
- Sneak peek:
14
+ * methods for instance, class or global variables
15
+ * methods for literals such as numbers, strings, symbols, etc.
16
+ * methods for finding a specific string (method source search)
17
+ * classes and modules that implement given methods
18
+ * classes and modules that send messages
19
+ * all classes and modules in gems
20
+ * and many more...
21
+
22
+ For the complete list of features please read the documentation or _read the
23
+ tests_. All interaction with the library is done via the `SystemNavigation`
24
+ class and its class methods. The description of the methods can be found in
25
+ these places:
26
+
27
+ * [SystemNavigation](http://www.rubydoc.info/gems/system_navigation/SystemNavigation)
28
+ * [SystemNavigation::RubyEnvironment](http://www.rubydoc.info/gems/system_navigation/SystemNavigation/RubyEnvironment)
29
+
30
+ Examples (full list in the documentation)
31
+ --
32
+
33
+ ##### #all_accesses
34
+
35
+ Retrieve all methods that access instance variables in the given class/module
36
+ including its ancestors and children.
14
37
 
15
38
  ```ruby
16
39
  module M
@@ -35,27 +58,48 @@ sn.all_accesses(to: :@num, from: A)
35
58
  #=> [#<UnboundMethod: A#num>, #<UnboundMethod: A(M)#increment>, #<UnboundMethod: A#initialize>]
36
59
  ```
37
60
 
38
- And many more...
61
+ ##### #all_implementors_of
39
62
 
40
- Installation
41
- ------------
63
+ Find all classes and modules that implement the given message.
42
64
 
43
- All you need is to install the gem.
65
+ ```ruby
66
+ sn = SystemNavigation.default
44
67
 
45
- gem install system_navigation
68
+ sn.all_implementors_of(:puts)
69
+ #=> [ARGF.class, IO, Kernel, ..., YARD::Logger]
70
+ ```
46
71
 
47
- Synopsis
48
- ---
72
+ ##### #all_calls
49
73
 
50
- ### Precaution
74
+ Find all methods in Bundler that invoke the `1` literal.
51
75
 
52
- The library is very young and its API is far from stable. Some behaviours
53
- might be unexpected or bugged. Feel free to file issues if you feel like the
54
- result you expect isn't what you actually get.
76
+ ```ruby
77
+ require 'bundler'
78
+
79
+ sn = SystemNavigation.default
80
+
81
+ sn.all_calls(on: 1, gem: 'bundler')
82
+ #=> [#<UnboundMethod: #<Class:Bundler>#with_clean_env>, #<UnboundMethod: #<Class:Bundler>#eval_gemspec>]
83
+ ```
55
84
 
56
- ### API
85
+ ##### #all_objects
57
86
 
58
- See the [API.md](/docs/API.md) file.
87
+ Retrieve all objects defined in the system.
88
+
89
+ ```ruby
90
+ sn = SystemNavigation.default
91
+
92
+ sn.all_objects.map(&:class).uniq.count #=> 158
93
+ ```
94
+
95
+ And many more...
96
+
97
+ Installation
98
+ ------------
99
+
100
+ All you need is to install the gem.
101
+
102
+ gem install system_navigation
59
103
 
60
104
  Limitations
61
105
  -----------
@@ -64,6 +108,11 @@ Supports *only* CRuby.
64
108
 
65
109
  * CRuby 2.2.2 and higher
66
110
 
111
+ Credits
112
+ -------
113
+
114
+ * Inspired by Smalltalk
115
+
67
116
  License
68
117
  -------
69
118
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.2.0
@@ -17,6 +17,12 @@ require_relative 'system_navigation/instruction_stream/decoder'
17
17
  require_relative 'system_navigation/instruction_stream/instruction'
18
18
  require_relative 'system_navigation/instruction_stream/instruction/attr_instruction'
19
19
 
20
+ # SystemNavigation is a class that provides some introspection capabilities. It
21
+ # is based on a Smalltalk class with a similar name. This is the only public
22
+ # class in this library.
23
+ #
24
+ # @api public
25
+ # @since 0.1.0
20
26
  class SystemNavigation
21
27
  # The VERSION file must be in the root directory of the library.
22
28
  VERSION_FILE = File.expand_path('../../VERSION', __FILE__)
@@ -31,19 +37,35 @@ class SystemNavigation
31
37
  :all_behaviors, :all_classes, :all_classes_and_modules,
32
38
  :all_modules, :all_objects
33
39
 
40
+ ##
41
+ # Creates a new instance of SystemNavigation. It is added for compatibility
42
+ # with Smalltalk users.
43
+ #
44
+ # @example
45
+ # require 'system_navigation'
46
+ #
47
+ # sn = SystemNavigation.default
34
48
  def self.default
35
49
  self.new
36
50
  end
37
51
 
52
+ VAR_TEMPLATE = /\A[\$@]@?.+/
53
+ private_constant :VAR_TEMPLATE
54
+
38
55
  def initialize
39
56
  @environment = SystemNavigation::RubyEnvironment.new
40
57
  end
41
58
 
42
59
  ##
43
- # Query methods for instance variables in descending (subclasses) and
44
- # ascending (superclasses) fashion.
60
+ # Query methods for instance/global/class variables in descending (subclasses)
61
+ # and ascending (superclasses) fashion.
45
62
  #
46
- # @example Global
63
+ # @note This is a very costly operation, if you don't provide the +from+
64
+ # argument
65
+ # @note This method _does_ _not_ perform _global_ queries,
66
+ # only relative to +from+
67
+ #
68
+ # @example Global scope (start search from BasicObject)
47
69
  # class A
48
70
  # def initialize
49
71
  # @foo = 1
@@ -57,7 +79,7 @@ class SystemNavigation
57
79
  # sn.all_accesses(to: :@foo)
58
80
  # #=> [#<UnboundMethod: A#initialize>, #<UnboundMethod: B#foo>]
59
81
  #
60
- # @example Local
82
+ # @example Local scope
61
83
  # class A
62
84
  # def initialize
63
85
  # @foo = 1
@@ -83,20 +105,45 @@ class SystemNavigation
83
105
  # end
84
106
  #
85
107
  # sn.all_accesses(to: :@foo, only_get: true)
86
- # #=> [#<UnboundMethod: B#foo>]
108
+ # #=> [#<UnboundMethod: B#initialize>]
109
+ #
110
+ # @example Only set invokations
111
+ # class A
112
+ # def initialize
113
+ # @foo = 1
114
+ # end
115
+ # end
116
+ #
117
+ # class B
118
+ # attr_reader :foo
119
+ # end
120
+ #
121
+ # sn.all_accesses(to: :@foo, only_set: true)
122
+ # #=> [#<UnboundMethod: A#initialize>]
87
123
  #
88
- # @param to [Symbol] The name of the instance variable to search for
124
+ # @example Accesses to global variables
125
+ # sn.all_accesses(to: :$DEBUG)
126
+ # #=> [#<UnboundMethod: Gem::Specification#inspect>, ...]
127
+ #
128
+ # @example Accesses to class variables
129
+ # sn.all_accesses(to: :@@required_attributes)
130
+ # #=> [#<UnboundMethod: Gem::Specification#validate>, ...]
131
+ #
132
+ # @param to [Symbol] The name of the instance/global/class variable to search
133
+ # for
89
134
  # @param from [Class] The class that limits the scope of the query. Optional.
90
135
  # If omitted, performs the query starting from the top of the object
91
136
  # hierarchy (BasicObject)
92
137
  # @param only_get [Boolean] Limits the scope of the query only to methods that
93
- # write into the +ivar+. Optional. Mutually exclusive with +only_set+
138
+ # write into the +var+. Optional. Mutually exclusive with +only_set+
94
139
  # @param only_set [Boolean] Limits the scope of the query only to methods that
95
- # read from the +ivar+. Optional. Mutually exclusive with +only_get+
96
- # @return [Array<UnboundMethod>] methods that access the +ivar+ according to
140
+ # read from the +var+. Optional. Mutually exclusive with +only_get+
141
+ # @return [Array<UnboundMethod>] methods that access the +var+ according to
97
142
  # the given scope
98
- # @note This is a very costly operation, if you don't provide the +from+
99
- # argument
143
+ # @raise [ArgumentError] if both +:only_get+ and +:only_set+ were provided
144
+ # @raise [TypeError] if +:from+ is not a class
145
+ # @raise [ArgumentError] if +:to+ is not a Symbol representing either of
146
+ # these: class variable, instance variable, global variable
100
147
  def all_accesses(to:, from: nil, only_get: nil, only_set: nil)
101
148
  if only_set && only_get
102
149
  fail ArgumentError, 'both only_get and only_set were provided'
@@ -106,15 +153,32 @@ class SystemNavigation
106
153
  fail TypeError, "from must be a Class (#{from.class} given)"
107
154
  end
108
155
 
156
+ unless to.match(VAR_TEMPLATE)
157
+ fail ArgumentError, 'invalid argument for the `to:` attribute'
158
+ end
159
+
109
160
  (from || BasicObject).with_all_sub_and_superclasses.flat_map do |klass|
110
161
  klass.select_methods_that_access(to, only_get, only_set)
111
162
  end
112
163
  end
113
164
 
114
165
  ##
115
- # Query methods for literals they call.
166
+ # Query methods for literals they call. The supported literals:
167
+ # * Hashes (only simple Hashes that consist of literals itself)
168
+ # * Arrays (only simple Arrays that consist of literals itself)
169
+ # * +true+, +false+ and +nil+
170
+ # * Integers (same Integers represented with different notations are treated
171
+ # as the same number)
172
+ # * Floats
173
+ # * Strings
174
+ # * Ranges
116
175
  #
117
- # @example Global
176
+ # @note This is a very costly operation, if you don't provide the +from+
177
+ # argument
178
+ # @note The list of supported literals can be found here:
179
+ # http://ruby-doc.org/core-2.2.2/doc/syntax/literals_rdoc.html
180
+ #
181
+ # @example Global scope (every behaviour in this process)
118
182
  # class A
119
183
  # def foo
120
184
  # :hello
@@ -130,7 +194,7 @@ class SystemNavigation
130
194
  # sn.all_calls(on: :hello)
131
195
  # #=> [#<UnboundMethod: A#foo>, #<UnboundMethod: B#bar>]
132
196
  #
133
- # @example Local
197
+ # @example Local scope
134
198
  # class A
135
199
  # def foo
136
200
  # :hello
@@ -158,10 +222,7 @@ class SystemNavigation
158
222
  # @param gem [String] Limits the scope of the query only to methods
159
223
  # that are defined in the RubyGem +gem+ classes and modules. Optional.
160
224
  # @return [Array<UnboundMethod>] methods that call the given +literal+
161
- # @note This is a very costly operation, if you don't provide the +from+
162
- # argument
163
- # @note The list of supported literals can be found here:
164
- # http://ruby-doc.org/core-2.2.2/doc/syntax/literals_rdoc.html
225
+ # @raise [ArgumentError] if both keys (+from:+ and +gem:+) are given
165
226
  def all_calls(on:, from: nil, gem: nil)
166
227
  if from && gem
167
228
  fail ArgumentError, 'both from and gem were provided'
@@ -179,7 +240,7 @@ class SystemNavigation
179
240
  end
180
241
 
181
242
  ##
182
- # Query classes for the methods they implement.
243
+ # Query classes for methods they implement.
183
244
  #
184
245
  # @example
185
246
  # sn.all_classes_implementing(:~)
@@ -223,6 +284,9 @@ class SystemNavigation
223
284
  ##
224
285
  # Query gems for classes they implement.
225
286
  #
287
+ # @note This method is not precise. If a class/method in the given gem does
288
+ # not implement any methods, it won't be included in the result.
289
+ #
226
290
  # @example
227
291
  # sn.all_classes_in_gem_named('system_navigation')
228
292
  # #=> [SystemNavigation::AncestorMethodFinder, ..., SystemNavigation]
@@ -237,6 +301,9 @@ class SystemNavigation
237
301
  ##
238
302
  # Query gems for modules they implement.
239
303
  #
304
+ # @note This method is not precise. If a class/method in the given gem does
305
+ # not implement any methods, it won't be included in the result.
306
+ #
240
307
  # @example
241
308
  # sn.all_modules_in_gem_named('pry-theme')
242
309
  # #=> [PryTheme::Theme::DefaultAttrs, ..., PryTheme]
@@ -250,6 +317,9 @@ class SystemNavigation
250
317
  ##
251
318
  # Query gems for classes and modules they implement.
252
319
  #
320
+ # @note This method is not precise. If a class/method in the given gem does
321
+ # not implement any methods, it won't be included in the result.
322
+ #
253
323
  # @example
254
324
  # sn.all_classes_and_modules_in_gem_named('pry-theme')
255
325
  # #=> [PryTheme::Preview, ..., PryTheme::Color256]
@@ -262,7 +332,7 @@ class SystemNavigation
262
332
  end
263
333
 
264
334
  ##
265
- # Get all methods defined in current Ruby process.
335
+ # Get all methods defined in the current Ruby process.
266
336
  #
267
337
  # @example
268
338
  # sn.all_methods
@@ -298,9 +368,8 @@ class SystemNavigation
298
368
  # end
299
369
  # end
300
370
  #
301
- #
302
- # sn.all_methods_with_source(string: 'hello_hi')
303
- # #=> [#<UnboundMethod: B#bar>, #<UnboundMethod: A#foo>, #<UnboundMethod: M#foo>]
371
+ # sn.all_methods_with_source(string: 'hello_hi')
372
+ # #=> [#<UnboundMethod: B#bar>, #<UnboundMethod: A#foo>, #<UnboundMethod: M#foo>]
304
373
  #
305
374
  # @param string [String] The string to be searched for
306
375
  # @param match_case [Boolean] Whether to match case or not. Optional
@@ -4,6 +4,12 @@ class SystemNavigation
4
4
  self.new(method).compile
5
5
  end
6
6
 
7
+ IVAR = /\A@[^@]/
8
+ CVAR = /\A@@/
9
+ GVAR = /\A\$/
10
+
11
+ attr_reader :source
12
+
7
13
  def initialize(method)
8
14
  @method = method
9
15
  @scanner = SystemNavigation::InstructionStream.on(method)
@@ -47,12 +53,30 @@ class SystemNavigation
47
53
  exptree.includes?(literal)
48
54
  end
49
55
 
50
- def reads_field?(ivar)
51
- self.scan_for { @decoder.ivar_read_scan(ivar) }
56
+ def reads_field?(var)
57
+ case var
58
+ when IVAR
59
+ self.scan_for { @decoder.ivar_read_scan(var) }
60
+ when CVAR
61
+ self.scan_for { @decoder.cvar_read_scan(var) }
62
+ when GVAR
63
+ self.scan_for { @decoder.gvar_read_scan(var) }
64
+ else
65
+ raise ArgumentError, "unknown argument #{var}"
66
+ end
52
67
  end
53
68
 
54
- def writes_field?(ivar)
55
- self.scan_for { @decoder.ivar_write_scan(ivar) }
69
+ def writes_field?(var)
70
+ case var
71
+ when IVAR
72
+ self.scan_for { @decoder.ivar_write_scan(var) }
73
+ when CVAR
74
+ self.scan_for { @decoder.cvar_write_scan(var) }
75
+ when GVAR
76
+ self.scan_for { @decoder.gvar_write_scan(var) }
77
+ else
78
+ raise ArgumentError, "unknown argument #{var}"
79
+ end
56
80
  end
57
81
 
58
82
  def sends_message?(message)
@@ -60,6 +60,8 @@ class SystemNavigation
60
60
  end
61
61
 
62
62
  def unify(node)
63
+ return unless node
64
+
63
65
  node.each do |n|
64
66
  if n.instance_of?(Array)
65
67
  if n.size == 2 && n.all? { |num| num.instance_of?(Fixnum) }
@@ -42,6 +42,30 @@ class SystemNavigation
42
42
  end
43
43
  end
44
44
 
45
+ def gvar_read_scan(gvar)
46
+ self.select_instructions(literal: gvar) do |_prev_prev, prev, instruction|
47
+ next instruction if instruction.reads_gvar?(gvar)
48
+ end
49
+ end
50
+
51
+ def gvar_write_scan(gvar)
52
+ self.select_instructions(literal: gvar) do |prev_prev, prev, instruction|
53
+ next instruction if instruction.writes_gvar?(gvar)
54
+ end
55
+ end
56
+
57
+ def cvar_read_scan(cvar)
58
+ self.select_instructions(literal: cvar) do |_prev_prev, prev, instruction|
59
+ next instruction if instruction.reads_cvar?(cvar)
60
+ end
61
+ end
62
+
63
+ def cvar_write_scan(cvar)
64
+ self.select_instructions(literal: cvar) do |prev_prev, prev, instruction|
65
+ next instruction if instruction.writes_cvar?(cvar)
66
+ end
67
+ end
68
+
45
69
  def literal_scan(literal)
46
70
  name = @scanner.method.original_name
47
71
 
@@ -14,6 +14,8 @@ class SystemNavigation
14
14
  @lineno = nil
15
15
  @op_id = nil
16
16
  @ivar = nil
17
+ @gvar = nil
18
+ @cvar = nil
17
19
  @service_instruction = false
18
20
  end
19
21
 
@@ -25,7 +27,7 @@ class SystemNavigation
25
27
  parse_operand
26
28
  parse_lineno
27
29
  parse_op_id
28
- parse_ivar
30
+ parse_var
29
31
 
30
32
  self
31
33
  end
@@ -57,7 +59,7 @@ class SystemNavigation
57
59
  elsif @raw.check(%r{/})
58
60
  @operand = @raw.scan(%r{/.*/})
59
61
  else
60
- @operand = @raw.scan(/-?[0-9a-zA-Z:@_=.]+/)
62
+ @operand = @raw.scan(/-?[0-9a-zA-Z:@_=.$]+/)
61
63
 
62
64
  if @raw.peek(1) == ','
63
65
  @operand << @raw.scan(/[^\(]*/).rstrip
@@ -83,18 +85,51 @@ class SystemNavigation
83
85
  callinfo.terminate
84
86
  end
85
87
 
88
+ def parse_var
89
+ parse_ivar
90
+ parse_gvar
91
+ parse_cvar
92
+ end
93
+
86
94
  def parse_ivar
87
95
  return unless accessing_ivar?
88
96
 
89
97
  ivar = StringScanner.new(@operand)
90
98
  @ivar = ivar.scan(/:[^,]+/)[1..-1].to_sym
91
99
  ivar.terminate
100
+ @ivar
101
+ end
102
+
103
+ def parse_gvar
104
+ return unless accessing_gvar?
105
+
106
+ gvar = StringScanner.new(@operand)
107
+ @gvar = gvar.scan(/[^,]+/).to_sym
108
+ gvar.terminate
109
+ @gvar
110
+ end
111
+
112
+ def parse_cvar
113
+ return unless accessing_cvar?
114
+
115
+ cvar = StringScanner.new(@operand)
116
+ @cvar = cvar.scan(/:[^,]+/)[1..-1].to_sym
117
+ cvar.terminate
118
+ @cvar
92
119
  end
93
120
 
94
121
  def accessing_ivar?
95
122
  @opcode == 'getinstancevariable' || @opcode == 'setinstancevariable'
96
123
  end
97
124
 
125
+ def accessing_gvar?
126
+ @opcode == 'getglobal' || @opcode == 'setglobal'
127
+ end
128
+
129
+ def accessing_cvar?
130
+ @opcode == 'getclassvariable' || @opcode == 'setclassvariable'
131
+ end
132
+
98
133
  def vm_operative?
99
134
  @service_instruction == false
100
135
  end
@@ -108,19 +143,35 @@ class SystemNavigation
108
143
  end
109
144
 
110
145
  def dynamically_reads_ivar?
111
- @op_id == 'instance_variable_get'
146
+ self.op_id == 'instance_variable_get'
112
147
  end
113
148
 
114
149
  def dynamically_writes_ivar?
115
150
  @op_id == 'instance_variable_set'
116
151
  end
117
152
 
153
+ def reads_gvar?(gvar)
154
+ @opcode == 'getglobal' && @gvar == gvar
155
+ end
156
+
157
+ def writes_gvar?(gvar)
158
+ @opcode == 'setglobal' && @gvar == gvar
159
+ end
160
+
161
+ def reads_cvar?(cvar)
162
+ @opcode == 'getclassvariable' && @cvar == cvar
163
+ end
164
+
165
+ def writes_cvar?(cvar)
166
+ @opcode == 'setclassvariable' && @cvar == cvar
167
+ end
168
+
118
169
  def evals?
119
- @op_id == 'eval'
170
+ self.op_id == 'eval'
120
171
  end
121
172
 
122
173
  def putstrings?(str)
123
- return false unless @opcode == 'putstring'
174
+ return false unless self.opcode == 'putstring'
124
175
 
125
176
  s = str.inspect
126
177
 
@@ -137,29 +188,32 @@ class SystemNavigation
137
188
  def putobjects?(str)
138
189
  return false unless @opcode == 'putobject'
139
190
 
140
- return true if @operand.match(/(?::#{str}\z|\[.*:#{str},.*\])/)
191
+ s = (str.instance_of?(String) ? Regexp.escape(str) : str)
192
+
193
+ return true if @operand.match(/(?::#{s}\z|\[.*:#{s},.*\])/)
141
194
  return true if @operand == str.inspect
142
195
 
143
196
  false
144
197
  end
145
198
 
146
199
  def putnils?(str)
147
- return false unless @opcode == 'putnil'
200
+ return false unless self.opcode == 'putnil'
148
201
  @operand == str.inspect
149
202
  end
150
203
 
151
204
  def duparrays?(str)
152
- !!(@opcode == 'duparray' && @operand.match(/:#{str}[,\]]/))
205
+ s = case str
206
+ when Array, Hash then Regexp.escape(str.inspect)
207
+ else str
208
+ end
209
+
210
+ !!(self.opcode == 'duparray' && @operand.match(/:#{s}[,\]]/))
153
211
  end
154
212
 
155
213
  def sends_msg?(message)
156
214
  !!(sending? && @op_id == message.to_s)
157
215
  end
158
216
 
159
- def operand
160
- @operand
161
- end
162
-
163
217
  def evaling_str
164
218
  @evaling_str ||= @operand.sub!(/\A"(.+)"/, '\1')
165
219
  end
@@ -175,6 +229,10 @@ class SystemNavigation
175
229
  def sending?
176
230
  @opcode == 'opt_send_without_block' || @opcode == 'send'
177
231
  end
232
+
233
+ protected
234
+
235
+ attr_reader :opcode, :op_id
178
236
  end
179
237
  end
180
238
  end
@@ -44,17 +44,17 @@ class SystemNavigation
44
44
  ).as_array.uniq.count == 1
45
45
  end
46
46
 
47
- def find_accessing_methods(ivar:, only_set:, only_get:)
47
+ def find_accessing_methods(var:, only_set:, only_get:)
48
48
  self.instance_and_singleton_do(
49
49
  for_all: proc { |_scope, _selectors, method|
50
50
  compiled_method = CompiledMethod.compile(method)
51
51
  if only_set
52
- compiled_method.unwrap if compiled_method.writes_field?(ivar)
52
+ compiled_method.unwrap if compiled_method.writes_field?(var)
53
53
  elsif only_get
54
- compiled_method.unwrap if compiled_method.reads_field?(ivar)
54
+ compiled_method.unwrap if compiled_method.reads_field?(var)
55
55
  else
56
- if compiled_method.reads_field?(ivar) ||
57
- compiled_method.writes_field?(ivar)
56
+ if compiled_method.reads_field?(var) ||
57
+ compiled_method.writes_field?(var)
58
58
  compiled_method.unwrap
59
59
  end
60
60
  end
@@ -35,7 +35,7 @@ class SystemNavigation
35
35
  end
36
36
  end
37
37
 
38
- def select_methods_that_access(ivar, only_get, only_set)
38
+ def select_methods_that_access(var, only_get, only_set)
39
39
  own_methods = self.own_methods
40
40
  if ancestor_methods.any?
41
41
  ancestor_methods.each do |methods|
@@ -48,7 +48,7 @@ class SystemNavigation
48
48
  MethodQuery.execute(
49
49
  collection: own_methods,
50
50
  query: :find_accessing_methods,
51
- ivar: ivar,
51
+ var: var,
52
52
  only_get: only_get,
53
53
  only_set: only_set,
54
54
  behavior: self).as_array
@@ -207,10 +207,10 @@ class SystemNavigation
207
207
  query: :select_sent_messages)
208
208
  end
209
209
 
210
- def which_selectors_store_into(ivar)
210
+ def which_selectors_store_into(var)
211
211
  self.selectors.select do |sel|
212
212
  meth = self.instance_method(sel)
213
- meth.writes_field?(ivar)
213
+ meth.writes_field?(var)
214
214
  end
215
215
  end
216
216
 
@@ -1,6 +1,12 @@
1
1
  class SystemNavigation
2
+ # @api private
3
+ # @since 0.1.0
2
4
  class RubyEnvironment
3
- # Execute block on each class, metaclass, module and module's metaclass.
5
+ ##
6
+ # Execute +block+ on each class, metaclass, module and module's metaclass.
7
+ #
8
+ # @return [Enumerator] if +block+ was given
9
+ # @return [Enumerator] if +block+ is missing
4
10
  def all_behaviors(&block)
5
11
  enum = Enumerator.new do |y|
6
12
  ObjectSpace.each_object(Module) do |klass|
@@ -16,6 +22,11 @@ class SystemNavigation
16
22
  end
17
23
  end
18
24
 
25
+ ##
26
+ # Execute +block+ on each class (but not its metaclass).
27
+ #
28
+ # @return [Enumerator] if +block+ was given
29
+ # @return [Enumerator] if +block+ is missing
19
30
  def all_classes(&block)
20
31
  enum = Enumerator.new do |y|
21
32
  ObjectSpace.each_object(Class) do |klass|
@@ -30,6 +41,11 @@ class SystemNavigation
30
41
  end
31
42
  end
32
43
 
44
+ ##
45
+ # Execute +block+ on each class and module (but not their metaclasses).
46
+ #
47
+ # @return [Enumerator] if +block+ was given
48
+ # @return [Enumerator] if +block+ is missing
33
49
  def all_classes_and_modules(&block)
34
50
  enum = Enumerator.new do |y|
35
51
  ObjectSpace.each_object(Module) do |klass|
@@ -44,6 +60,11 @@ class SystemNavigation
44
60
  end
45
61
  end
46
62
 
63
+ ##
64
+ # Execute +block+ on each module (but not its metaclass).
65
+ #
66
+ # @return [Enumerator] if +block+ was given
67
+ # @return [Enumerator] if +block+ is missing
47
68
  def all_modules(&block)
48
69
  enum = Enumerator.new do |y|
49
70
  self.all_classes_and_modules.each do |klass|
@@ -58,10 +79,15 @@ class SystemNavigation
58
79
  end
59
80
  end
60
81
 
82
+ ##
83
+ # Execute +block+ on each object.
84
+ #
85
+ # @return [Enumerator] if +block+ was given
86
+ # @return [Enumerator] if +block+ is missing
61
87
  def all_objects(&block)
62
88
  enum = Enumerator.new do |y|
63
- ObjectSpace.each_object do |klass|
64
- y.yield klass
89
+ ObjectSpace.each_object do |obj|
90
+ y.yield obj
65
91
  end
66
92
  end
67
93
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: system_navigation
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kyrylo Silin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-11 00:00:00.000000000 Z
11
+ date: 2015-06-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fast_method_source
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0.1'
19
+ version: '0'
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.1'
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement