system_navigation 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d0877e736a201bde13a94bb2fd54462777c30878
4
+ data.tar.gz: 0f072306d97e8b2d3f2697c3601e279bc5b6ffdd
5
+ SHA512:
6
+ metadata.gz: 4d9106cdc061404ae1ab11859ce526994ed3e478d4f21fc200304af7cf56bff513bb4c6b2fb1485ddb6963048d7eaa722e257920269a50f971eee7c92a30fc4a
7
+ data.tar.gz: 41cb455b27e76c9bb4b54d5197d25a17f7823dc5f661ddebd3cdc5a15dc513834afb347279dda38f16f5da338bc709d30d174bf5166ab23a151c08f0c8085d16
@@ -0,0 +1,6 @@
1
+ System Navigation changelog
2
+ ===========================
3
+
4
+ ### v0.1.0 (June 11, 2015)
5
+
6
+ * Initial release (sorta, not ready yet)
@@ -0,0 +1,19 @@
1
+ Copyright (C) 2015 Kyrylo Silin
2
+
3
+ This software is provided 'as-is', without any express or implied
4
+ warranty. In no event will the authors be held liable for any damages
5
+ arising from the use of this software.
6
+
7
+ Permission is granted to anyone to use this software for any purpose,
8
+ including commercial applications, and to alter it and redistribute it
9
+ freely, subject to the following restrictions:
10
+
11
+ 1. The origin of this software must not be misrepresented; you must not
12
+ claim that you wrote the original software. If you use this software
13
+ in a product, an acknowledgment in the product documentation would be
14
+ appreciated but is not required.
15
+
16
+ 2. Altered source versions must be plainly marked as such, and must not be
17
+ misrepresented as being the original software.
18
+
19
+ 3. This notice may not be removed or altered from any source distribution.
@@ -0,0 +1,70 @@
1
+ SystemNavigation
2
+ ==
3
+
4
+ * Repository: [https://github.com/kyrylo/system_navigation/](https://github.com/kyrylo/system_navigation/)
5
+
6
+ Description
7
+ -----------
8
+
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.
12
+
13
+ Sneak peek:
14
+
15
+ ```ruby
16
+ module M
17
+ def increment
18
+ @num + 1
19
+ end
20
+ end
21
+
22
+ class A
23
+ include M
24
+
25
+ attr_reader :num
26
+
27
+ def initialize(num)
28
+ @num = num
29
+ end
30
+ end
31
+
32
+ sn = SystemNavigation.default
33
+
34
+ sn.all_accesses(to: :@num, from: A)
35
+ #=> [#<UnboundMethod: A#num>, #<UnboundMethod: A(M)#increment>, #<UnboundMethod: A#initialize>]
36
+ ```
37
+
38
+ And many more...
39
+
40
+ Installation
41
+ ------------
42
+
43
+ All you need is to install the gem.
44
+
45
+ gem install system_navigation
46
+
47
+ Synopsis
48
+ ---
49
+
50
+ ### Precaution
51
+
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.
55
+
56
+ ### API
57
+
58
+ See the [API.md](/docs/API.md) file.
59
+
60
+ Limitations
61
+ -----------
62
+
63
+ Supports *only* CRuby.
64
+
65
+ * CRuby 2.2.2 and higher
66
+
67
+ License
68
+ -------
69
+
70
+ The project uses Zlib License. See LICENCE.txt file for more information.
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,374 @@
1
+ require 'fast_method_source'
2
+
3
+ require 'forwardable'
4
+ require 'strscan'
5
+ require 'ripper'
6
+
7
+ require_relative 'system_navigation/array_refinement'
8
+ require_relative 'system_navigation/module_refinement'
9
+ require_relative 'system_navigation/ruby_environment'
10
+ require_relative 'system_navigation/instruction_stream'
11
+ require_relative 'system_navigation/expression_tree'
12
+ require_relative 'system_navigation/method_query'
13
+ require_relative 'system_navigation/compiled_method'
14
+ require_relative 'system_navigation/method_hash'
15
+ require_relative 'system_navigation/ancestor_method_finder'
16
+ require_relative 'system_navigation/instruction_stream/decoder'
17
+ require_relative 'system_navigation/instruction_stream/instruction'
18
+ require_relative 'system_navigation/instruction_stream/instruction/attr_instruction'
19
+
20
+ class SystemNavigation
21
+ # The VERSION file must be in the root directory of the library.
22
+ VERSION_FILE = File.expand_path('../../VERSION', __FILE__)
23
+
24
+ VERSION = File.exist?(VERSION_FILE) ?
25
+ File.read(VERSION_FILE).chomp : '(could not find VERSION file)'
26
+
27
+ using ModuleRefinement
28
+
29
+ extend Forwardable
30
+ def_delegators :@environment,
31
+ :all_behaviors, :all_classes, :all_classes_and_modules,
32
+ :all_modules, :all_objects
33
+
34
+ def self.default
35
+ self.new
36
+ end
37
+
38
+ def initialize
39
+ @environment = SystemNavigation::RubyEnvironment.new
40
+ end
41
+
42
+ ##
43
+ # Query methods for instance variables in descending (subclasses) and
44
+ # ascending (superclasses) fashion.
45
+ #
46
+ # @example Global
47
+ # class A
48
+ # def initialize
49
+ # @foo = 1
50
+ # end
51
+ # end
52
+ #
53
+ # class B
54
+ # attr_reader :foo
55
+ # end
56
+ #
57
+ # sn.all_accesses(to: :@foo)
58
+ # #=> [#<UnboundMethod: A#initialize>, #<UnboundMethod: B#foo>]
59
+ #
60
+ # @example Local
61
+ # class A
62
+ # def initialize
63
+ # @foo = 1
64
+ # end
65
+ # end
66
+ #
67
+ # class B
68
+ # attr_reader :foo
69
+ # end
70
+ #
71
+ # sn.all_accesses(to: :@foo, from: B)
72
+ # #=> [#<UnboundMethod: B#foo>]
73
+ #
74
+ # @example Only get invokations
75
+ # class A
76
+ # def initialize
77
+ # @foo = 1
78
+ # end
79
+ # end
80
+ #
81
+ # class B
82
+ # attr_reader :foo
83
+ # end
84
+ #
85
+ # sn.all_accesses(to: :@foo, only_get: true)
86
+ # #=> [#<UnboundMethod: B#foo>]
87
+ #
88
+ # @param to [Symbol] The name of the instance variable to search for
89
+ # @param from [Class] The class that limits the scope of the query. Optional.
90
+ # If omitted, performs the query starting from the top of the object
91
+ # hierarchy (BasicObject)
92
+ # @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+
94
+ # @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
97
+ # the given scope
98
+ # @note This is a very costly operation, if you don't provide the +from+
99
+ # argument
100
+ def all_accesses(to:, from: nil, only_get: nil, only_set: nil)
101
+ if only_set && only_get
102
+ fail ArgumentError, 'both only_get and only_set were provided'
103
+ end
104
+
105
+ if from && !from.instance_of?(Class)
106
+ fail TypeError, "from must be a Class (#{from.class} given)"
107
+ end
108
+
109
+ (from || BasicObject).with_all_sub_and_superclasses.flat_map do |klass|
110
+ klass.select_methods_that_access(to, only_get, only_set)
111
+ end
112
+ end
113
+
114
+ ##
115
+ # Query methods for literals they call.
116
+ #
117
+ # @example Global
118
+ # class A
119
+ # def foo
120
+ # :hello
121
+ # end
122
+ # end
123
+ #
124
+ # class B
125
+ # def bar
126
+ # :hello
127
+ # end
128
+ # end
129
+ #
130
+ # sn.all_calls(on: :hello)
131
+ # #=> [#<UnboundMethod: A#foo>, #<UnboundMethod: B#bar>]
132
+ #
133
+ # @example Local
134
+ # class A
135
+ # def foo
136
+ # :hello
137
+ # end
138
+ # end
139
+ #
140
+ # class B
141
+ # def bar
142
+ # :hello
143
+ # end
144
+ # end
145
+ #
146
+ # sn.all_calls(on: :hello, from: A)
147
+ # #=> [#<UnboundMethod: A#foo>]
148
+ #
149
+ # @example Gem
150
+ # sn.all_calls(on: :singleton, gem: 'system_navigation')
151
+ # #=> [...]
152
+ #
153
+ # @param on [Boolean, Integer, Float, String, Symbol, Array, Hash, Range,
154
+ # Regexp] The literal to search for
155
+ # @param from [Class, Module] The behaviour that limits the scope of the
156
+ # query. If it's present, the search will be performed from top to bottom
157
+ # (only subclasses). Optional
158
+ # @param gem [String] Limits the scope of the query only to methods
159
+ # that are defined in the RubyGem +gem+ classes and modules. Optional.
160
+ # @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
165
+ def all_calls(on:, from: nil, gem: nil)
166
+ if from && gem
167
+ fail ArgumentError, 'both from and gem were provided'
168
+ end
169
+
170
+ subject = if from
171
+ from.with_all_subclasses
172
+ elsif gem
173
+ self.all_classes_and_modules_in_gem_named(gem)
174
+ else
175
+ self.all_classes_and_modules
176
+ end
177
+
178
+ subject.flat_map { |behavior| behavior.select_methods_that_refer_to(on) }
179
+ end
180
+
181
+ ##
182
+ # Query classes for the methods they implement.
183
+ #
184
+ # @example
185
+ # sn.all_classes_implementing(:~)
186
+ # #=> [Regexp, Bignum, Fixnum]
187
+ #
188
+ # @!macro [new] selector.param
189
+ # @param selector [Symbol] the name of the method to be searched for
190
+ # @return [Array<Class>] classes that implement +selector+
191
+ def all_classes_implementing(selector)
192
+ self.all_classes.select { |klass| klass.includes_selector?(selector) }
193
+ end
194
+
195
+ ##
196
+ # Query modules for the methods they implement.
197
+ #
198
+ # @example
199
+ # sn.all_classes_implementing(:select)
200
+ # #=> [Enumerable, Kernel, #<Module:0x007f56daf92918>]
201
+ #
202
+ # @!macro selector.param
203
+ # @return [Array<Class>] modules that implement +selector+
204
+ def all_modules_implementing(selector)
205
+ self.all_modules.select { |mod| mod.includes_selector?(selector) }
206
+ end
207
+
208
+ ##
209
+ # Query classes and modules for the methods they implement.
210
+ #
211
+ # @example
212
+ # sn.all_implementors_of(:select)
213
+ # #=> [Enumerator::Lazy, IO, ..., #<Class:Kernel>]
214
+ #
215
+ # @!macro selector.param
216
+ # @return [Array<Class, Module>] classes and modules that implement +selector+
217
+ def all_implementors_of(selector)
218
+ self.all_classes_and_modules.select do |klass|
219
+ klass.includes_selector?(selector)
220
+ end
221
+ end
222
+
223
+ ##
224
+ # Query gems for classes they implement.
225
+ #
226
+ # @example
227
+ # sn.all_classes_in_gem_named('system_navigation')
228
+ # #=> [SystemNavigation::AncestorMethodFinder, ..., SystemNavigation]
229
+ #
230
+ # @!macro [new] gem.param
231
+ # @param gem [String] The name of the gem. Case sensitive
232
+ # @return [Array<Class>] classes that were defined by +gem+
233
+ def all_classes_in_gem_named(gem)
234
+ self.all_classes.select { |klass| klass.belongs_to?(gem) }
235
+ end
236
+
237
+ ##
238
+ # Query gems for modules they implement.
239
+ #
240
+ # @example
241
+ # sn.all_modules_in_gem_named('pry-theme')
242
+ # #=> [PryTheme::Theme::DefaultAttrs, ..., PryTheme]
243
+ #
244
+ # @!macro [new] gem.param
245
+ # @return [Array<Class>] modules that were defined by +gem+
246
+ def all_modules_in_gem_named(gem)
247
+ self.all_modules.select { |mod| mod.belongs_to?(gem) }
248
+ end
249
+
250
+ ##
251
+ # Query gems for classes and modules they implement.
252
+ #
253
+ # @example
254
+ # sn.all_classes_and_modules_in_gem_named('pry-theme')
255
+ # #=> [PryTheme::Preview, ..., PryTheme::Color256]
256
+ #
257
+ # @!macro [new] gem.param
258
+ # @return [Array<Class, Module>] classes and modules that were defined by
259
+ # +gem+
260
+ def all_classes_and_modules_in_gem_named(gem)
261
+ self.all_classes_and_modules.select { |klassmod| klassmod.belongs_to?(gem) }
262
+ end
263
+
264
+ ##
265
+ # Get all methods defined in current Ruby process.
266
+ #
267
+ # @example
268
+ # sn.all_methods
269
+ # #=> [#<UnboundMethod: Gem::Dependency#name>, ...]
270
+ #
271
+ # @return [Array<UnboundMethod>] all methods that exist
272
+ def all_methods
273
+ self.all_classes_and_modules.map do |klassmod|
274
+ klassmod.own_methods.as_array
275
+ end.flatten
276
+ end
277
+
278
+ ##
279
+ # Search for a string in all classes and modules including their comments and
280
+ # names.
281
+ #
282
+ # @example
283
+ # class A
284
+ # def foo
285
+ # :hello_hi
286
+ # end
287
+ # end
288
+ #
289
+ # class B
290
+ # def bar
291
+ # 'hello_hi'
292
+ # end
293
+ # end
294
+ #
295
+ # module M
296
+ # # hello_hi
297
+ # def baz
298
+ # end
299
+ # end
300
+ #
301
+ #
302
+ # sn.all_methods_with_source(string: 'hello_hi')
303
+ # #=> [#<UnboundMethod: B#bar>, #<UnboundMethod: A#foo>, #<UnboundMethod: M#foo>]
304
+ #
305
+ # @param string [String] The string to be searched for
306
+ # @param match_case [Boolean] Whether to match case or not. Optional
307
+ # @return [Array<UnboundMethod>] methods that matched +string+
308
+ # @note This is a very costly operation
309
+ def all_methods_with_source(string:, match_case: true)
310
+ return [] if string.empty?
311
+
312
+ self.all_classes_and_modules.flat_map do |klassmod|
313
+ klassmod.select_matching_methods(string, match_case)
314
+ end
315
+ end
316
+
317
+ ##
318
+ # Get all methods implemented in C.
319
+ #
320
+ # @example
321
+ # sn.all_c_methods
322
+ # #=> [#<UnboundMethod: #<Class:Etc>#getlogin>, ...]
323
+ #
324
+ # @return [Array<UnboundMethod>] all methods that were implemented in C
325
+ def all_c_methods
326
+ self.all_classes_and_modules.flat_map do |klassmod|
327
+ klassmod.select_c_methods
328
+ end
329
+ end
330
+
331
+ ##
332
+ # Get all methods implemented in Ruby.
333
+ #
334
+ # @example
335
+ # sn.all_rb_methods
336
+ # #=> [#<UnboundMethod: Gem::Dependency#name>, ...]
337
+ #
338
+ # @return [Array<UnboundMethod>] all methods that were implemented in Ruby
339
+ def all_rb_methods
340
+ self.all_classes_and_modules.flat_map do |klassmod|
341
+ klassmod.select_rb_methods
342
+ end
343
+ end
344
+
345
+ ##
346
+ # Get all methods that implement +message+.
347
+ #
348
+ # @example
349
+ # sn.all_senders_of(:puts)
350
+ # #=> []
351
+ #
352
+ # @param message [Symbol] The name of the method you're interested in
353
+ # @return [Array<UnboundMethod>] all methods that send +message
354
+ def all_senders_of(message)
355
+ self.all_classes_and_modules.flat_map do |klassmod|
356
+ klassmod.select_senders_of(message)
357
+ end
358
+ end
359
+
360
+ ##
361
+ # Get all messages that all methods send.
362
+ #
363
+ # @example
364
+ # sn.all_sent_messages
365
+ # #=> [:name, :hash, ..., :type]
366
+ #
367
+ # @return [Array<Symbol>] all unique messages
368
+ # @note This is a very costly operation
369
+ def all_sent_messages
370
+ self.all_classes_and_modules.flat_map do |klassmod|
371
+ klassmod.all_messages.as_array
372
+ end.uniq
373
+ end
374
+ end