super_module 1.1.1 → 1.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 +4 -4
- data/README.md +34 -87
- data/VERSION +1 -1
- data/examples/reddit-readers/banister/foo.rb +1 -2
- data/lib/super_module.rb +41 -26
- data/lib/super_module/v1.rb +37 -0
- data/lib/super_module/v1/module_body_method_call_recorder.rb +42 -0
- data/lib/super_module/v1/singleton_method_definition_store.rb +106 -0
- data/spec/lib/super_module_spec.rb +159 -145
- data/spec/support/baz.rb +1 -3
- data/spec/support/v1.rb +6 -0
- data/spec/support/v1/bar.rb +51 -0
- data/spec/support/v1/baz.rb +28 -0
- data/spec/support/v1/fake_active_model.rb +16 -0
- data/spec/support/v1/foo.rb +87 -0
- data/spec/support/v2.rb +6 -0
- data/spec/support/v2/bar.rb +52 -0
- data/spec/support/v2/baz.rb +27 -0
- data/spec/support/v2/fake_active_model.rb +14 -0
- data/spec/support/v2/foo.rb +87 -0
- data/spec/support/v2_alt.rb +6 -0
- data/spec/support/v2_alt/bar.rb +52 -0
- data/spec/support/v2_alt/baz.rb +27 -0
- data/spec/support/v2_alt/fake_active_model.rb +14 -0
- data/spec/support/v2_alt/foo.rb +87 -0
- data/super_module.gemspec +21 -6
- metadata +20 -5
- data/lib/super_module/module_body_method_call_recorder.rb +0 -40
- data/lib/super_module/singleton_method_definition_store.rb +0 -102
- data/spec/support/support.rb +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2417fe4ac87bcf7e975d971de2f86caa01f1f357
|
4
|
+
data.tar.gz: 305eda58e8250ce45e24af27325ba09f2f34c9f1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f4eea83f2a17b20dd8c40ab3f5e710f702c501936fea5782dbfb8295ee5c3a8a7889479d742777bbfa6151d8aa6ccfdc71ab4fdbd9431e2a13dbd4f0986f1f1a
|
7
|
+
data.tar.gz: 071b746e0e94003c5716724e9b51744986bd28c388492ed55284d9c65f8c445393d2688e2e8be0046b101bbb6bccaed75a97a472c2356c4b748012541efbad2a
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# <img src="https://raw.githubusercontent.com/AndyObtiva/super_module/master/SuperModule.jpg" alt="SuperModule" align="left" height="50" /> SuperModule
|
1
|
+
# <img src="https://raw.githubusercontent.com/AndyObtiva/super_module/master/SuperModule.jpg" alt="SuperModule" align="left" height="50" /> SuperModule 2 Beta (1.2.0)
|
2
2
|
[](http://badge.fury.io/rb/super_module)
|
3
3
|
[](https://travis-ci.org/AndyObtiva/super_module)
|
4
4
|
[](https://coveralls.io/r/AndyObtiva/super_module?branch=master)
|
@@ -8,7 +8,7 @@ Calling [Ruby](https://www.ruby-lang.org/en/)'s [`Module#include`](http://ruby-d
|
|
8
8
|
|
9
9
|
[`ActiveSupport::Concern`](http://api.rubyonrails.org/classes/ActiveSupport/Concern.html) is a popular Rails library that attempts to ease some of the boilerplate pain by offering a [DSL](http://www.infoq.com/news/2007/06/dsl-or-not) layer on top of [`Module.included(base)`](http://ruby-doc.org/core-2.2.1/Module.html#method-i-included). Unfortunately, while it improves the readability of the code needed to include class methods, it supports the same boilerplate idiom, thus feeling no more than putting a band-aid on the problem.
|
10
10
|
|
11
|
-
Fortunately, [SuperModule](https://rubygems.org/gems/super_module) comes to the rescue.
|
11
|
+
Fortunately, [SuperModule](https://rubygems.org/gems/super_module) comes to the rescue. By declaring your module as a `super_module`, it will automatically include class methods whenever it is mixed into a class or another module via [`Module#include`](http://ruby-doc.org/core-2.2.1/Module.html#method-i-include).
|
12
12
|
|
13
13
|
## Introductory Comparison
|
14
14
|
|
@@ -72,8 +72,7 @@ A step forward that addresses the boiler-plate repetitive code concern, but is o
|
|
72
72
|
#### 3) [SuperModule](https://github.com/AndyObtiva/super_module)
|
73
73
|
|
74
74
|
```ruby
|
75
|
-
|
76
|
-
include SuperModule
|
75
|
+
super_module :UserIdentifiable do
|
77
76
|
include ActiveModel::Model
|
78
77
|
|
79
78
|
belongs_to :user
|
@@ -88,13 +87,21 @@ module UserIdentifiable
|
|
88
87
|
end
|
89
88
|
end
|
90
89
|
```
|
90
|
+
Using `super_module`, developers can directly add class method invocations and definitions inside the module's body, and [`SuperModule`](https://github.com/AndyObtiva/super_module) takes care of automatically mixing them into classes that include the module.
|
91
91
|
|
92
|
-
|
93
|
-
|
94
|
-
As a result, [SuperModule](https://rubygems.org/gems/super_module) collapses the difference between extending a super class and including a super module, thus encouraging developers to write simpler code while making better Object-Oriented Design decisions.
|
92
|
+
As a result, [SuperModule](https://rubygems.org/gems/super_module) collapses the difference between extending a super class and including a super module, thus encouraging developers to write simpler code while making better Object-Oriented Design decisions.
|
95
93
|
|
96
94
|
In other words, [SuperModule](https://rubygems.org/gems/super_module) furthers Ruby's goal of making programmers happy.
|
97
95
|
|
96
|
+
By the way, SuperModule 2 Beta supports an alternate syntax as well:
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
UserIdentifiable = super_module do
|
100
|
+
end
|
101
|
+
```
|
102
|
+
|
103
|
+
|
104
|
+
|
98
105
|
## Instructions
|
99
106
|
|
100
107
|
#### 1) Install and require gem
|
@@ -115,11 +122,10 @@ Run the following command: <pre>gem install super_module</pre>
|
|
115
122
|
|
116
123
|
Add the following at the top of your [Ruby](https://www.ruby-lang.org/en/) file: <pre>require 'super_module'</pre>
|
117
124
|
|
118
|
-
#### 2)
|
125
|
+
#### 2) Call `super_module(name)` and pass it the super module body in a block
|
119
126
|
|
120
127
|
```ruby
|
121
|
-
|
122
|
-
include SuperModule
|
128
|
+
super_module :UserIdentifiable do
|
123
129
|
include ActiveModel::Model
|
124
130
|
|
125
131
|
belongs_to :user
|
@@ -144,8 +150,7 @@ end
|
|
144
150
|
class CourseEnrollment < ActiveRecord::Base
|
145
151
|
include UserIdentifiable
|
146
152
|
end
|
147
|
-
|
148
|
-
include SuperModule
|
153
|
+
super_module :Accountable do
|
149
154
|
include UserIdentifiable
|
150
155
|
end
|
151
156
|
class Activity < ActiveRecord::Base
|
@@ -188,8 +193,7 @@ Create a ruby file called super_module_irb_example.rb with the following content
|
|
188
193
|
require 'rubygems' # to be backwards compatible with Ruby 1.8.7
|
189
194
|
require 'super_module'
|
190
195
|
|
191
|
-
|
192
|
-
include SuperModule
|
196
|
+
super_module :RequiresAttributes do
|
193
197
|
|
194
198
|
def self.requires(*attributes)
|
195
199
|
attributes.each {|attribute| required_attributes << attribute}
|
@@ -255,85 +259,20 @@ media_authorization.requirements_satisfied?
|
|
255
259
|
|
256
260
|
## How Does It Work?
|
257
261
|
|
258
|
-
|
259
|
-
|
260
|
-
```ruby
|
261
|
-
def included(base)
|
262
|
-
__define_super_module_class_methods(base)
|
263
|
-
__invoke_super_module_class_method_calls(base)
|
264
|
-
end
|
265
|
-
```
|
266
|
-
|
267
|
-
#### 1) Defines super module class methods on the including base class
|
268
|
-
|
269
|
-
For example, suppose we have a super module called Addressable:
|
270
|
-
|
271
|
-
```ruby
|
272
|
-
module Addressable
|
273
|
-
include SuperModule
|
274
|
-
|
275
|
-
include Locatable
|
276
|
-
validates :city, presence: true, length: { maximum: 255 }
|
277
|
-
validates :state, presence: true, length: { is: 2 }
|
278
|
-
|
279
|
-
def self.merge_duplicates
|
280
|
-
# 1. Look through all Addressable instances in the database
|
281
|
-
# 2. Identify duplicates
|
282
|
-
# 3. Merge duplicate addressables
|
283
|
-
end
|
284
|
-
end
|
285
|
-
|
286
|
-
class Contact < ActiveRecord::Base
|
287
|
-
include Addressable
|
288
|
-
# … more code follows
|
289
|
-
end
|
290
|
-
```
|
291
|
-
|
292
|
-
This step ensures that <code>merge_duplicates</code> is included in Contact as a class method, allowing the call <code>Contact.merge_duplicates</code>
|
293
|
-
|
294
|
-
It does so by recording every class method defined using the Ruby [`self.singleton_method_added(method_name)`](http://ruby-doc.org/core-2.2.1/BasicObject.html#method-i-singleton_method_added) hook, reading class method sources using the [method_source](https://rubygems.org/gems/method_source/) gem, and finally upon invocation of `self.included(base)`, `class_eval`ing the recorded class methods on the including base class (or module).
|
295
|
-
|
296
|
-
In order to avoid interference with existing class method definitions, there is an exception list for what not to record, such as <code>:included_super_modules, :class_eval, :singleton_method_added</code> and any other "__" prefixed class methods defined in [SuperModule](https://rubygems.org/gems/super_module), such as <code>__super_module_class_method_calls</code>.
|
297
|
-
|
298
|
-
Also, the recorded class method sources are altered to handle recording of method calls as well, which is used in the second step explained next.
|
299
|
-
|
300
|
-
#### 2) Invoke super module class method calls on the including base class (or module).
|
301
|
-
|
302
|
-
For example, suppose we have a super module called `Locatable`:
|
303
|
-
|
304
|
-
```ruby
|
305
|
-
module Locatable
|
306
|
-
include SuperModule
|
307
|
-
|
308
|
-
validates :x_coordinate, numericality: true
|
309
|
-
validates :y_coordinate, numericality: true
|
310
|
-
|
311
|
-
def move(x, y)
|
312
|
-
self.x_coordinate += x
|
313
|
-
self.y_coordinate += y
|
314
|
-
end
|
315
|
-
end
|
316
|
-
|
317
|
-
class Vehicle < ActiveRecord::Base
|
318
|
-
include Locatable
|
319
|
-
# … more code follows
|
320
|
-
end
|
321
|
-
```
|
262
|
+
V2 has a much simpler algorithm than V1 that goes as follows:
|
322
263
|
|
323
|
-
|
324
|
-
|
325
|
-
|
264
|
+
1. Handle invocation of `super_module(name, &super_module_body)` method anywhere in the Ruby code where the block it receives represents the super module body, including instance methods, and class methods, and class body invocations.
|
265
|
+
2. Clone `SuperModule` and store in it the passed in `super_module_body` block
|
266
|
+
3. Assign the cloned `SuperModule` to a new constant as defined by name (e.g. 'Utilities::Printer') under a class, module, or the top-level Ruby scope
|
267
|
+
4. When calling `include` on the module later on, its stored super_module_body attribute is retrieved and run in the including class or module body via `class_eval`
|
326
268
|
|
327
269
|
## Limitations and Caveats
|
328
270
|
|
329
|
-
* [SuperModule](https://rubygems.org/gems/super_module) has been designed to be used only in the code definition of a module
|
330
|
-
|
331
|
-
* Initial Ruby runtime load of a class or module mixing in [SuperModule](https://rubygems.org/gems/super_module) will incur a very marginal performance hit (in the order of nano-to-milliseconds). However, class usage (instantiation and method invocation) will not incur any performance hit, running as fast as any other Ruby class.
|
271
|
+
* [SuperModule](https://rubygems.org/gems/super_module) has been designed to be used only in the initial code definition of a module (not supporting later re-opening of the module.)
|
332
272
|
|
333
273
|
* Given [SuperModule](https://rubygems.org/gems/super_module)'s implementation relies on `self.included(base)`, if an including super module (or a super module including another super module) must hook into <code>self.included(base)</code> for meta-programming cases that require it, such as conditional `include` statements or method definitions, it would have to alias <code>self.included(base)</code> and then invoke the aliased version in every super module that needs it like in this example:
|
334
274
|
```ruby
|
335
|
-
|
336
|
-
include SuperModule
|
275
|
+
super_module :AdminIdentifiable do
|
337
276
|
include UserIdentifiable
|
338
277
|
|
339
278
|
class << self
|
@@ -345,11 +284,19 @@ module AdminIdentifiable
|
|
345
284
|
# or conditional definition of methods
|
346
285
|
end
|
347
286
|
end
|
287
|
+
end
|
348
288
|
```
|
349
289
|
In the future, [SuperModule](https://rubygems.org/gems/super_module) could perhaps provide robust built-in facilities for allowing super modules to easily hook into <code>self.included(base)</code> without interfering with [SuperModule](https://rubygems.org/gems/super_module) behavior.
|
350
290
|
|
351
291
|
## What's New?
|
352
292
|
|
293
|
+
### v2 Beta (v1.2.0)
|
294
|
+
|
295
|
+
* New `super_module(name)` syntax
|
296
|
+
* Much simpler implementation with guaranteed correctness and no performance hit
|
297
|
+
* Less memory footprint by not requiring method_source Ruby gem for v2 syntax
|
298
|
+
* Backwards compatibility with v1 syntax
|
299
|
+
|
353
300
|
### v1.1.1
|
354
301
|
|
355
302
|
* Added support for private and protected methods
|
@@ -378,5 +325,5 @@ The library is quite new and can use all the feedback and help it can get. So, p
|
|
378
325
|
|
379
326
|
## Copyright
|
380
327
|
|
381
|
-
Copyright (c) 2014-
|
328
|
+
Copyright (c) 2014-2016 Andy Maleh. See LICENSE.txt for
|
382
329
|
further details.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.2.0
|
data/lib/super_module.rb
CHANGED
@@ -7,35 +7,50 @@
|
|
7
7
|
# This module allows defining class methods and method invocations the same way a super class does without using def included(base).
|
8
8
|
|
9
9
|
# Avoiding require_relative for backwards compatibility with Ruby 1.8.7
|
10
|
-
require File.expand_path(File.join(File.dirname(__FILE__), 'super_module', 'module_body_method_call_recorder'))
|
11
|
-
require File.expand_path(File.join(File.dirname(__FILE__), 'super_module', 'singleton_method_definition_store'))
|
12
10
|
|
13
11
|
module SuperModule
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
12
|
+
class << self
|
13
|
+
V1_LIBRARY = File.expand_path(File.join(File.dirname(__FILE__), 'super_module', 'v1'))
|
14
|
+
|
15
|
+
attr_accessor :super_module_body
|
16
|
+
|
17
|
+
def define(&super_module_body)
|
18
|
+
clone.tap { |super_module| super_module.super_module_body = super_module_body }
|
19
|
+
end
|
20
|
+
|
21
|
+
def included(original_base)
|
22
|
+
if super_module_body
|
23
|
+
original_base.class_eval(&super_module_body)
|
24
|
+
else
|
25
|
+
require V1_LIBRARY
|
26
|
+
original_base.send(:include, SuperModule::V1)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def __super_module_parent(name, ancestor)
|
31
|
+
name_tokens = name.to_s.split('::')
|
32
|
+
name_token_count = name_tokens.size
|
33
|
+
if name_token_count == 1
|
34
|
+
ancestor
|
35
|
+
else
|
36
|
+
top_ancestor = ancestor.const_get(name_tokens.first)
|
37
|
+
sub_module_name = name_tokens[1, name_token_count].join('::')
|
38
|
+
__super_module_parent(sub_module_name, top_ancestor)
|
38
39
|
end
|
39
40
|
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
def super_module(name=nil, &super_module_body)
|
47
|
+
initial_ancestor = self.class == Object ? Object : self
|
48
|
+
SuperModule.define(&super_module_body).tap do |new_super_module|
|
49
|
+
if name
|
50
|
+
parent = SuperModule.__super_module_parent(name, initial_ancestor)
|
51
|
+
module_name = name.to_s.split('::').last
|
52
|
+
parent.const_set(module_name, new_super_module)
|
53
|
+
end
|
40
54
|
end
|
41
55
|
end
|
56
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'v1', 'module_body_method_call_recorder'))
|
2
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'v1', 'singleton_method_definition_store'))
|
3
|
+
|
4
|
+
module SuperModule
|
5
|
+
module V1
|
6
|
+
class << self
|
7
|
+
def included(original_base)
|
8
|
+
original_base.class_eval do
|
9
|
+
extend SuperModule::V1::ModuleBodyMethodCallRecorder
|
10
|
+
extend SuperModule::V1::SingletonMethodDefinitionStore
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def __define_super_module_singleton_methods(base)
|
14
|
+
__super_module_singleton_methods.each do |method_name, method_body|
|
15
|
+
# The following is needed for cases where a method is declared public/protected/private after it was added
|
16
|
+
refreshed_access_level_method_body = method_body.sub(/class << self\n(public|protected|private)\n/, "class << self\n#{__singleton_method_access_level(method_name)}\n")
|
17
|
+
base.class_eval(refreshed_access_level_method_body)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def __invoke_module_body_method_calls(base)
|
22
|
+
__all_module_body_method_calls_in_definition_order.each do |method_name, args, block|
|
23
|
+
base.send(method_name, *args, &block)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def included(base)
|
28
|
+
__define_super_module_singleton_methods(base)
|
29
|
+
__invoke_module_body_method_calls(base)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module SuperModule
|
2
|
+
module V1
|
3
|
+
module ModuleBodyMethodCallRecorder
|
4
|
+
def __super_module_singleton_methods_excluded_from_call_recording
|
5
|
+
@__super_module_singleton_methods_excluded_from_call_recording ||= [
|
6
|
+
:__record_method_call,
|
7
|
+
:__method_signature
|
8
|
+
]
|
9
|
+
end
|
10
|
+
|
11
|
+
def __module_body_method_calls
|
12
|
+
@__module_body_method_calls ||= []
|
13
|
+
end
|
14
|
+
|
15
|
+
def __method_signature(method_name, args)
|
16
|
+
"#{method_name}(#{args.to_a.map(&:to_s).join(",")})"
|
17
|
+
end
|
18
|
+
|
19
|
+
#TODO handle case of a method call being passed a block (e.g. validates do custom validator end )
|
20
|
+
def __record_method_call(method_name, *args, &block)
|
21
|
+
return if self.is_a?(Class)
|
22
|
+
__module_body_method_calls << [method_name, args, block]
|
23
|
+
end
|
24
|
+
|
25
|
+
def __all_module_body_method_calls_in_definition_order
|
26
|
+
ancestor_module_body_method_calls = included_super_modules.map(&:__module_body_method_calls).flatten(1)
|
27
|
+
all_module_body_method_calls = __module_body_method_calls + ancestor_module_body_method_calls
|
28
|
+
all_module_body_method_calls.reverse
|
29
|
+
end
|
30
|
+
|
31
|
+
def __singleton_method_call_recorder(method_name, method_args)
|
32
|
+
unless __super_module_singleton_methods_excluded_from_call_recording.include?(method_name)
|
33
|
+
method_call_recorder_args = "'#{method_name}'"
|
34
|
+
method_call_recorder_args << ", #{method_args}" unless method_args.to_s.strip == ''
|
35
|
+
"self.__record_method_call(#{method_call_recorder_args})"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'method_source'
|
2
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'module_body_method_call_recorder')) # backwards compatible with Ruby 1.8.7
|
3
|
+
|
4
|
+
module SuperModule
|
5
|
+
module V1
|
6
|
+
module SingletonMethodDefinitionStore
|
7
|
+
# excluded list of singleton methods to define (perhaps give a better name)
|
8
|
+
def __super_module_singleton_methods_excluded_from_base_definition
|
9
|
+
@__super_module_singleton_methods_excluded_from_base_definition ||= [
|
10
|
+
:__all_module_body_method_calls_in_definition_order,
|
11
|
+
:__build_singleton_method_body_source,
|
12
|
+
:__define_super_module_singleton_methods,
|
13
|
+
:__invoke_module_body_method_calls,
|
14
|
+
:__module_body_method_calls,
|
15
|
+
:__overwrite_singleton_method_from_current_super_module,
|
16
|
+
:__singleton_method_args,
|
17
|
+
:__singleton_method_body,
|
18
|
+
:__singleton_method_body_for,
|
19
|
+
:__singleton_method_call_recorder,
|
20
|
+
:__singleton_method_definition_regex,
|
21
|
+
:__super_module_having_method,
|
22
|
+
:__super_module_singleton_methods,
|
23
|
+
:__super_module_singleton_methods_excluded_from_base_definition,
|
24
|
+
:__super_module_singleton_methods_excluded_from_call_recording,
|
25
|
+
:class_eval,
|
26
|
+
:dbg_print, #debugger library friendly exclusion
|
27
|
+
:dbg_puts, #debugger library friendly exclusion
|
28
|
+
:define,
|
29
|
+
:included,
|
30
|
+
:included_super_modules,
|
31
|
+
:singleton_method_added,
|
32
|
+
:super_module_body
|
33
|
+
]
|
34
|
+
end
|
35
|
+
|
36
|
+
def __super_module_singleton_methods
|
37
|
+
@__super_module_singleton_methods ||= []
|
38
|
+
end
|
39
|
+
|
40
|
+
def __singleton_method_body_for(super_module, method_name)
|
41
|
+
super_module.__super_module_singleton_methods.detect {|sm_method_name, sm_method_body| sm_method_name == method_name}[1]
|
42
|
+
end
|
43
|
+
|
44
|
+
def included_super_modules
|
45
|
+
included_modules.select {|m| m.include?(SuperModule)}
|
46
|
+
end
|
47
|
+
|
48
|
+
def __all_methods(object)
|
49
|
+
object.public_methods + object.protected_methods + object.private_methods
|
50
|
+
end
|
51
|
+
|
52
|
+
def __super_module_having_method(method_name)
|
53
|
+
included_super_modules.detect {|included_super_module| __all_methods(included_super_module).map(&:to_s).include?(method_name.to_s)}
|
54
|
+
end
|
55
|
+
|
56
|
+
def __singleton_method_definition_regex(method_name)
|
57
|
+
/(public|protected|private)?(send)?[ \t(:"']*def(ine_method)?[ \t,:"']+(self\.)?#{method_name}\)?[ \tdo{(|]*([^\n)|;]*)?[ \t)|;]*/m
|
58
|
+
end
|
59
|
+
|
60
|
+
def __singleton_method_args(method_name, method_body)
|
61
|
+
method_arg_match = method_body.match(__singleton_method_definition_regex(method_name)).to_a[5]
|
62
|
+
end
|
63
|
+
|
64
|
+
def __singleton_method_access_level(method_name)
|
65
|
+
%w(private protected public).detect do |method_access|
|
66
|
+
method_group = "#{method_access}_methods"
|
67
|
+
send(method_group).map(&:to_s).include?(method_name.to_s)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def __build_singleton_method_body_source(method_name)
|
72
|
+
method_body = self.method(method_name).source
|
73
|
+
method_args = __singleton_method_args(method_name, method_body)
|
74
|
+
method_body = "def #{method_name}\n#{method_body}\nend" if method_args.nil?
|
75
|
+
class_self_method_def_enclosure = "class << self\n#{__singleton_method_access_level(method_name)}\ndef #{method_name}(#{method_args})\n#{__singleton_method_call_recorder(method_name, method_args)}\n"
|
76
|
+
method_body.sub(__singleton_method_definition_regex(method_name), class_self_method_def_enclosure) + "\nend\n"
|
77
|
+
end
|
78
|
+
|
79
|
+
def __singleton_method_body(method_name)
|
80
|
+
super_module_having_method = __super_module_having_method(method_name)
|
81
|
+
super_module_having_method ? __singleton_method_body_for(super_module_having_method, method_name) : __build_singleton_method_body_source(method_name)
|
82
|
+
end
|
83
|
+
|
84
|
+
def __overwrite_singleton_method_from_current_super_module(method_name, method_body)
|
85
|
+
if __super_module_having_method(method_name).nil?
|
86
|
+
__super_module_singleton_methods_excluded_from_base_definition << method_name
|
87
|
+
class_eval(method_body)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def singleton_method_added(method_name)
|
92
|
+
unless __super_module_singleton_methods_excluded_from_base_definition.include?(method_name)
|
93
|
+
method_body = __singleton_method_body(method_name)
|
94
|
+
__super_module_singleton_methods << [method_name, method_body]
|
95
|
+
__overwrite_singleton_method_from_current_super_module(method_name, method_body)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.extended(base)
|
100
|
+
base.extend(SuperModule::ModuleBodyMethodCallRecorder) unless base.respond_to?(:__record_method_call)
|
101
|
+
base.singleton_method_added(:__method_signature)
|
102
|
+
base.singleton_method_added(:__record_method_call)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|