super_module 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +332 -112
- data/VERSION +1 -1
- data/examples/reddit-readers/banister/foo.rb +8 -0
- data/examples/reddit-readers/banister/world.rb +5 -0
- data/lib/super_module.rb +19 -46
- data/lib/super_module/module_body_method_call_recorder.rb +40 -0
- data/lib/super_module/singleton_method_definition_store.rb +91 -0
- data/ruby187.Gemfile +17 -0
- data/spec/lib/super_module_spec.rb +103 -50
- data/spec/support/bar.rb +52 -0
- data/spec/support/baz.rb +29 -0
- data/spec/support/fake_active_model.rb +14 -0
- data/spec/support/foo.rb +23 -0
- data/spec/support/support.rb +4 -0
- data/super_module.gemspec +26 -10
- metadata +45 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5ed999facd85a8593e0e0af6a036c625a68f51ae
|
4
|
+
data.tar.gz: 27219be2a1966b18e253d839b76232e118d27824
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6bb3130d30bb2bf2baf04c657a828020375f2b3bb0a5504cae2d8da924ba5184deee3aace7cc4f2359c4bb8f85cc603189734e27fecdee6461b75fb873a4144c
|
7
|
+
data.tar.gz: bc3d4b819dbd682c75c8a26b5d58da614615f60f14f5e880f6128dcd2ab15feb947b571d79aed3f95a7907c3ab7fbe68d71a71f398ca72836f638ba011de5ab8
|
data/README.md
CHANGED
@@ -4,143 +4,363 @@
|
|
4
4
|
[![Coverage Status](https://coveralls.io/repos/AndyObtiva/super_module/badge.png?branch=master)](https://coveralls.io/r/AndyObtiva/super_module?branch=master)
|
5
5
|
[![Code Climate](https://codeclimate.com/github/AndyObtiva/super_module.png)](https://codeclimate.com/github/AndyObtiva/super_module)
|
6
6
|
|
7
|
-
Tired of Ruby's modules not allowing you to mix in class methods easily?
|
8
|
-
Tired of writing complex code
|
7
|
+
Tired of [Ruby](https://www.ruby-lang.org/en/)'s modules not allowing you to mix in class methods easily?
|
8
|
+
Tired of writing complex `self.included(base)` code or using over-engineered solutions like [`ActiveSupport::Concern`](http://api.rubyonrails.org/classes/ActiveSupport/Concern.html) to accomplish that goal?
|
9
|
+
|
10
|
+
Well, worry no more! [SuperModule](https://rubygems.org/gems/super_module) comes to the rescue!
|
11
|
+
|
12
|
+
[SuperModule](https://rubygems.org/gems/super_module) allows defining class methods and method invocations the same way a super class does without using [`self.included(base)`](http://ruby-doc.org/core-2.2.1/Module.html#method-i-included).
|
13
|
+
|
14
|
+
This succeeds [`ActiveSupport::Concern`](http://api.rubyonrails.org/classes/ActiveSupport/Concern.html) by offering lighter syntax and simpler module dependency support.
|
15
|
+
|
16
|
+
## Introductory Comparison
|
17
|
+
|
18
|
+
To introduce [SuperModule](https://rubygems.org/gems/super_module), here is a comparison of three different approaches for writing a
|
19
|
+
<code>UserIdentifiable</code> module.
|
20
|
+
|
21
|
+
#### 1) [self.included(base)](http://ruby-doc.org/core-2.2.1/Module.html#method-i-included)
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
module UserIdentifiable
|
25
|
+
include ActiveModel::Model
|
26
|
+
|
27
|
+
def self.included(base_klass)
|
28
|
+
base_klass.extend(ClassMethods)
|
29
|
+
base.class_eval do
|
30
|
+
belongs_to :user
|
31
|
+
validates :user_id, presence: true
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
module ClassMethods
|
36
|
+
def most_active_user
|
37
|
+
User.find_by_id(select('count(id) as head_count, user_id').group('user_id').order('count(id) desc').first.user_id)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def slug
|
42
|
+
"#{self.class.name}_#{user_id}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
```
|
46
|
+
|
47
|
+
This is a lot to think about and process for simply wanting inclusion of class method definitions (like <code>most_active_user</code>) and class method invocations (like <code>belongs_to</code> and <code>validates</code>). The unnecessary complexity gets in the way of problem-solving; slows down productivity with repetitive boiler-plate code; and breaks expectations set in other similar object-oriented languages, discouraging companies from including [Ruby](https://www.ruby-lang.org/en/) in a polyglot stack, such as [Groupon](http://www.groupon.com)'s [Rails/JVM/Node.js](https://engineering.groupon.com/2013/misc/i-tier-dismantling-the-monoliths/) stack and [SoundCloud](http://www.soundcloud.com)'s [JRuby/Scala/Clojure stack](https://developers.soundcloud.com/blog/building-products-at-soundcloud-part-3-microservices-in-scala-and-finagle).
|
48
|
+
|
49
|
+
#### 2) [ActiveSupport::Concern](http://api.rubyonrails.org/classes/ActiveSupport/Concern.html)
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
module UserIdentifiable
|
53
|
+
extend ActiveSupport::Concern
|
54
|
+
include ActiveModel::Model
|
55
|
+
|
56
|
+
included do
|
57
|
+
belongs_to :user
|
58
|
+
validates :user_id, presence: true
|
59
|
+
end
|
60
|
+
|
61
|
+
module ClassMethods
|
62
|
+
def most_active_user
|
63
|
+
User.find_by_id(select('count(id) as head_count, user_id').group('user_id').order('count(id) desc').first.user_id)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def slug
|
68
|
+
"#{self.class.name}_#{user_id}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
```
|
72
|
+
|
73
|
+
A step forward that addresses the boiler-plate repetitive code concern, but is otherwise really just lipstick on a pig. To explain more, developer problem solving and creativity flow is still disrupted by having to think about the lower-level mechanism of running code on inclusion (using `included`) and structuring class methods in an extra sub-module (`ClassMethods`) instead of simply declaring class methods like they normally would in Ruby and staying focused on the task at hand.
|
74
|
+
|
75
|
+
#### 3) [SuperModule](https://github.com/AndyObtiva/super_module)
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
module UserIdentifiable
|
79
|
+
include SuperModule
|
80
|
+
include ActiveModel::Model
|
81
|
+
|
82
|
+
belongs_to :user
|
83
|
+
validates :user_id, presence: true
|
84
|
+
|
85
|
+
def self.most_active_user
|
86
|
+
User.find_by_id(select('count(id) as head_count, user_id').group('user_id').order('count(id) desc').first.user_id)
|
87
|
+
end
|
88
|
+
|
89
|
+
def slug
|
90
|
+
"#{self.class.name}_#{user_id}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
```
|
94
|
+
|
95
|
+
With `include SuperModule` declared on top, 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.
|
96
|
+
|
97
|
+
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.
|
98
|
+
|
99
|
+
In other words, [SuperModule](https://rubygems.org/gems/super_module) furthers Ruby's goal of making programmers happy.
|
9
100
|
|
10
|
-
|
101
|
+
## Instructions
|
11
102
|
|
12
|
-
|
103
|
+
#### 1) Install and require gem
|
13
104
|
|
14
|
-
|
105
|
+
<b>Using [Bundler](http://bundler.io/)</b>
|
15
106
|
|
16
|
-
|
107
|
+
Add the following to Gemfile: <pre>gem 'super_module', '1.0.0'</pre>
|
17
108
|
|
18
|
-
|
109
|
+
And run the following command: <pre>bundle</pre>
|
19
110
|
|
20
|
-
|
111
|
+
Afterwards, [SuperModule](https://rubygems.org/gems/super_module) will automatically get required in the application (e.g. a Rails application) and be ready for use.
|
21
112
|
|
22
|
-
<b>Using
|
113
|
+
<b>Using [RubyGem](https://rubygems.org/gems/super_module) Directly</b>
|
23
114
|
|
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
|
-
|
115
|
+
Run the following command: <pre>gem install super_module</pre>
|
116
|
+
|
117
|
+
(add <code>--no-ri --no-rdoc</code> if you wish to skip downloading documentation for a faster install)
|
118
|
+
|
119
|
+
Add the following at the top of your [Ruby](https://www.ruby-lang.org/en/) file: <pre>require 'super_module'</pre>
|
120
|
+
|
121
|
+
#### 2) Include [`SuperModule`](https://rubygems.org/gems/super_module) at the top of the module
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
module UserIdentifiable
|
125
|
+
include SuperModule
|
126
|
+
include ActiveModel::Model
|
127
|
+
|
128
|
+
belongs_to :user
|
129
|
+
validates :user_id, presence: true
|
130
|
+
|
131
|
+
def self.most_active_user
|
132
|
+
User.find_by_id(select('count(id) as head_count, user_id').group('user_id').order('count(id) desc').first.user_id)
|
133
|
+
end
|
134
|
+
|
135
|
+
def slug
|
136
|
+
"#{self.class.name}_#{user_id}"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
```
|
140
|
+
|
141
|
+
#### 3) Mix newly defined module into a class or another super module
|
142
|
+
|
143
|
+
```ruby
|
144
|
+
class ClubParticipation < ActiveRecord::Base
|
145
|
+
include UserIdentifiable
|
146
|
+
end
|
147
|
+
class CourseEnrollment < ActiveRecord::Base
|
148
|
+
include UserIdentifiable
|
149
|
+
end
|
150
|
+
module Accountable
|
151
|
+
include SuperModule
|
152
|
+
include UserIdentifiable
|
153
|
+
end
|
154
|
+
class Activity < ActiveRecord::Base
|
155
|
+
include Accountable
|
156
|
+
end
|
157
|
+
```
|
158
|
+
|
159
|
+
#### 4) Start using by invoking class methods or instance methods
|
160
|
+
|
161
|
+
```ruby
|
162
|
+
CourseEnrollment.most_active_user
|
163
|
+
ClubParticipation.most_active_user
|
164
|
+
Activity.last.slug
|
165
|
+
ClubParticipation.create(club_id: club.id, user_id: user.id).slug
|
166
|
+
CourseEnrollment.new(course_id: course.id).valid?
|
167
|
+
```
|
168
|
+
|
169
|
+
## Glossary and Definitions
|
170
|
+
|
171
|
+
* SuperModule: name of the library and Ruby module that provides functionality via mixin
|
172
|
+
* Super module: any Ruby module that mixes in SuperModule
|
173
|
+
* Class method definition: Ruby class or module method declared with <code>self.method_name</code> or <code>class << self</code>
|
174
|
+
* Class method invocation: Inherited Ruby class or module method invoked in the body of a class or module (e.g. <code>validates :username, presence: true</code>)
|
175
|
+
* Code-time: Time of writing code in a Ruby file as opposed to Run-time
|
176
|
+
* Run-time: Time of executing Ruby code
|
177
|
+
|
178
|
+
## Usage Details
|
179
|
+
|
180
|
+
* SuperModule must always be included at the top of a module's body at code-time
|
181
|
+
* SuperModule inclusion can be optionally followed by other basic or super module inclusions
|
182
|
+
* A super module can only be included in a class or another super module
|
183
|
+
* SuperModule adds <b>zero cost</b> to instantiation of including classes and invocation of included methods (both class and instance)
|
184
|
+
|
185
|
+
## Another Example
|
186
|
+
|
187
|
+
Copy and paste the following code snippets in <code>irb</code> and you should get the output denoted by double arrows (<code>=></code>).
|
188
|
+
|
189
|
+
```ruby
|
190
|
+
require 'super_module'
|
191
|
+
|
192
|
+
module Foo
|
193
|
+
include SuperModule
|
194
|
+
|
195
|
+
validates :credit_card_id, presence: true
|
196
|
+
|
197
|
+
def foo
|
198
|
+
puts 'foo'
|
199
|
+
'foo'
|
200
|
+
end
|
201
|
+
|
202
|
+
def self.foo
|
203
|
+
puts 'self.foo'
|
204
|
+
'self.foo'
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
module Bar
|
209
|
+
include SuperModule
|
210
|
+
include Foo
|
211
|
+
|
212
|
+
validates :user_id, presence: true
|
213
|
+
|
214
|
+
def bar
|
215
|
+
puts 'bar'
|
216
|
+
'bar'
|
217
|
+
end
|
218
|
+
|
219
|
+
def self.bar
|
220
|
+
puts 'self.bar'
|
221
|
+
'self.bar'
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
class MediaAuthorization < ActiveRecord::Base
|
226
|
+
include Bar
|
227
|
+
end
|
228
|
+
|
229
|
+
MediaAuthorization.create.errors.messages.inspect
|
230
|
+
```
|
119
231
|
|
120
232
|
=> "{:credit_card_id=>[\"can't be blank\"], :user_id=>[\"can't be blank\"]}"
|
121
233
|
|
122
|
-
|
234
|
+
```ruby
|
235
|
+
MediaAuthorization.new.foo
|
236
|
+
```
|
123
237
|
|
124
238
|
=> "foo"
|
125
239
|
|
126
|
-
|
240
|
+
```ruby
|
241
|
+
MediaAuthorization.new.bar
|
242
|
+
```
|
127
243
|
|
128
244
|
=> "bar"
|
129
245
|
|
130
|
-
|
246
|
+
```ruby
|
247
|
+
MediaAuthorization.foo
|
248
|
+
```
|
131
249
|
|
132
250
|
=> "self.foo"
|
133
251
|
|
134
|
-
|
252
|
+
```ruby
|
253
|
+
MediaAuthorization.bar
|
254
|
+
```
|
135
255
|
|
136
256
|
=> "self.bar"
|
137
257
|
|
138
|
-
##
|
258
|
+
## How Does It Work?
|
259
|
+
|
260
|
+
Here is the general algorithm from the implementation:
|
261
|
+
|
262
|
+
```ruby
|
263
|
+
def included(base)
|
264
|
+
__invoke_super_module_class_method_calls(base)
|
265
|
+
__define_super_module_class_methods(base)
|
266
|
+
end
|
267
|
+
```
|
268
|
+
|
269
|
+
#### 1) Invoke super module class method calls on the including base class.
|
270
|
+
|
271
|
+
For example, suppose we have a super module called `Locatable`:
|
272
|
+
|
273
|
+
```ruby
|
274
|
+
module Locatable
|
275
|
+
include SuperModule
|
276
|
+
|
277
|
+
validates :x_coordinate, numericality: true
|
278
|
+
validates :y_coordinate, numericality: true
|
279
|
+
|
280
|
+
def move(x, y)
|
281
|
+
self.x_coordinate += x
|
282
|
+
self.y_coordinate += y
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
class Vehicle < ActiveRecord::Base
|
287
|
+
include Locatable
|
288
|
+
# … more code follows
|
289
|
+
end
|
290
|
+
```
|
291
|
+
|
292
|
+
This first step guarantees invocation of the two `Locatable` <code>validates</code> method calls on the `Vehicle` object class.
|
293
|
+
|
294
|
+
It does so by relying on `method_missing(method_name, *args, &block)` to record every class method call that happens in the super module class body, and later replaying those calls on the including base class during `self.included(base)` by using Ruby's `send(method_name, *args, &block)` method introspection.
|
295
|
+
|
296
|
+
#### 2) Defines super module class methods on the including base class
|
297
|
+
|
298
|
+
For example, suppose we have a super module called Addressable:
|
299
|
+
|
300
|
+
```ruby
|
301
|
+
module Addressable
|
302
|
+
include SuperModule
|
303
|
+
|
304
|
+
include Locatable
|
305
|
+
validates :city, presence: true, length: { maximum: 255 }
|
306
|
+
validates :state, presence: true, length: { is: 2 }
|
307
|
+
|
308
|
+
def self.merge_duplicates
|
309
|
+
# 1. Look through all Addressable instances in the database
|
310
|
+
# 2. Identify duplicates
|
311
|
+
# 3. Merge duplicate addressables
|
312
|
+
end
|
313
|
+
end
|
139
314
|
|
140
|
-
|
315
|
+
class Contact < ActiveRecord::Base
|
316
|
+
include Addressable
|
317
|
+
# … more code follows
|
318
|
+
end
|
319
|
+
```
|
320
|
+
|
321
|
+
The second step ensures that <code>merge_duplicates</code> is included in Contact as a class method, allowing the call <code>Contact.merge_duplicates</code>
|
322
|
+
|
323
|
+
It does so by recording every class method defined using the <code>self.singleton_method_added(method_name)</code> added hook, and then later replaying these class definitions on the including base class during invocation of <code>self.included(base)</code>.
|
324
|
+
|
325
|
+
In order to avoid interference with existing class method definitions, there is an exception list for what not to record, such as <code>:included, :method_missing, :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>.
|
326
|
+
|
327
|
+
## Limitations and Caveats
|
328
|
+
|
329
|
+
* [SuperModule](https://rubygems.org/gems/super_module) has been designed to be used only in the code definition of a module, not to be mixed in at run-time.
|
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.
|
332
|
+
|
333
|
+
* Given [SuperModule](https://rubygems.org/gems/super_module) relies on <code>self.included(base)</code> in its implementation, 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
|
+
```ruby
|
335
|
+
module AdminIdentifiable
|
336
|
+
include SuperModule
|
337
|
+
include UserIdentifiable
|
338
|
+
|
339
|
+
class << self
|
340
|
+
alias included_super_module included
|
341
|
+
def included(base)
|
342
|
+
included_super_module(base)
|
343
|
+
# do some extra work
|
344
|
+
# like conditional inclusion of other modules
|
345
|
+
# or conditional definition of methods
|
346
|
+
end
|
347
|
+
end
|
348
|
+
```
|
349
|
+
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
|
+
|
351
|
+
## Feedback and Contribution
|
352
|
+
|
353
|
+
[SuperModule](https://rubygems.org/gems/super_module) is written in a very clean and maintainable test-first approach, so you are welcome to read through the code on GitHub for more in-depth details:
|
354
|
+
https://github.com/AndyObtiva/super_module
|
355
|
+
|
356
|
+
The library is quite new and can use all the feedback and help it can get. So, please do not hesitate to add comments if you have any, and please fork [the project on GitHub](https://github.com/AndyObtiva/super_module#fork-destination-box) in order to [make contributions via Pull Requests](https://github.com/AndyObtiva/super_module/pulls).
|
357
|
+
|
358
|
+
## Articles and Blog Posts
|
359
|
+
|
360
|
+
* 2015-03-27 - [AirPair](http://www.airpair.com) Article: [Step aside ActiveSupport::Concern. SuperModule is the new sheriff in town!](https://www.airpair.com/ruby/posts/step-aside-activesupportconcern-supermodule-is-the-new-sheriff-in-town)
|
361
|
+
* 2014-03-27 - [Code Painter](http://andymaleh.blogspot.com) Blog Post: [Ruby SuperModule Comes To The Rescue!!](http://andymaleh.blogspot.ca/2014/03/ruby-supermodule-comes-to-rescue.html)
|
141
362
|
|
142
363
|
## Copyright
|
143
364
|
|
144
|
-
Copyright (c) 2014 Andy Maleh. See LICENSE.txt for
|
365
|
+
Copyright (c) 2014-2015 Andy Maleh. See LICENSE.txt for
|
145
366
|
further details.
|
146
|
-
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.1.0
|
data/lib/super_module.rb
CHANGED
@@ -1,66 +1,39 @@
|
|
1
1
|
# SuperModule allows defining class methods and method invocations the same way a super class does without using def included(base).
|
2
2
|
#
|
3
3
|
# Author:: Andy Maleh
|
4
|
-
# Copyright:: Copyright (c) 2014 Andy Maleh
|
4
|
+
# Copyright:: Copyright (c) 2014-2015 Andy Maleh
|
5
5
|
# License:: MIT License
|
6
6
|
|
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
|
-
|
10
|
-
|
11
|
-
|
12
|
-
:__invoke_super_module_class_method_calls,
|
13
|
-
:__define_super_module_class_methods,
|
14
|
-
:__restore_original_method_missing,
|
15
|
-
:included, :method_missing,
|
16
|
-
:singleton_method_added
|
17
|
-
]
|
18
|
-
def self.included(base)
|
19
|
-
base.class_eval do
|
20
|
-
class << self
|
21
|
-
|
22
|
-
def include(base, &block)
|
23
|
-
method_missing('include', base, &block)
|
24
|
-
end
|
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'))
|
25
12
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
@__super_module_class_methods ||= []
|
32
|
-
end
|
33
|
-
|
34
|
-
def singleton_method_added(method_name)
|
35
|
-
__super_module_class_methods << [method_name, method(method_name)] unless EXCLUDED_SINGLETON_METHODS.include?(method_name)
|
36
|
-
super
|
37
|
-
end
|
38
|
-
|
39
|
-
def method_missing(method_name, *args, &block)
|
40
|
-
__super_module_class_method_calls << [method_name, args, block]
|
41
|
-
end
|
13
|
+
module SuperModule
|
14
|
+
def self.included(original_base)
|
15
|
+
original_base.class_eval do
|
16
|
+
extend SuperModule::ModuleBodyMethodCallRecorder
|
17
|
+
extend SuperModule::SingletonMethodDefinitionStore
|
42
18
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
end
|
19
|
+
class << self
|
20
|
+
def __define_super_module_singleton_methods(base)
|
21
|
+
__super_module_singleton_methods.each do |method_name, method_body|
|
22
|
+
base.class_eval(method_body)
|
48
23
|
end
|
49
24
|
end
|
50
25
|
|
51
|
-
def
|
52
|
-
|
53
|
-
base.
|
54
|
-
self.class.send(:define_method, method_name, &method)
|
55
|
-
end
|
26
|
+
def __invoke_module_body_method_calls(base)
|
27
|
+
__all_module_body_method_calls_in_definition_order.each do |method_name, args, block|
|
28
|
+
base.send(method_name, *args, &block)
|
56
29
|
end
|
57
30
|
end
|
58
31
|
|
59
32
|
def included(base)
|
60
|
-
|
61
|
-
|
33
|
+
__define_super_module_singleton_methods(base)
|
34
|
+
__invoke_module_body_method_calls(base)
|
62
35
|
end
|
63
36
|
end
|
64
37
|
end
|
65
38
|
end
|
66
|
-
end
|
39
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module SuperModule
|
2
|
+
module ModuleBodyMethodCallRecorder
|
3
|
+
def __super_module_singleton_methods_excluded_from_call_recording
|
4
|
+
@__super_module_singleton_methods_excluded_from_call_recording ||= [
|
5
|
+
:__record_method_call,
|
6
|
+
:__method_signature
|
7
|
+
]
|
8
|
+
end
|
9
|
+
|
10
|
+
def __module_body_method_calls
|
11
|
+
@__module_body_method_calls ||= []
|
12
|
+
end
|
13
|
+
|
14
|
+
def __method_signature(method_name, args)
|
15
|
+
"#{method_name}(#{args.to_a.map(&:to_s).join(",")})"
|
16
|
+
end
|
17
|
+
|
18
|
+
#TODO handle case of a method call being passed a block (e.g. validates do custom validator end )
|
19
|
+
def __record_method_call(method_name, *args, &block)
|
20
|
+
return if self.is_a?(Class)
|
21
|
+
__module_body_method_calls << [method_name, args, block]
|
22
|
+
end
|
23
|
+
|
24
|
+
def __all_module_body_method_calls_in_definition_order
|
25
|
+
ancestor_module_body_method_calls = included_super_modules.map(&:__module_body_method_calls).flatten(1)
|
26
|
+
all_module_body_method_calls = __module_body_method_calls + ancestor_module_body_method_calls
|
27
|
+
all_module_body_method_calls.reverse
|
28
|
+
end
|
29
|
+
|
30
|
+
def __singleton_method_call_recorder(method_name, method_args)
|
31
|
+
unless __super_module_singleton_methods_excluded_from_call_recording.include?(method_name)
|
32
|
+
method_call_recorder_args = "'#{method_name}'"
|
33
|
+
method_call_recorder_args << ", #{method_args}" unless method_args.to_s.strip == ''
|
34
|
+
"self.__record_method_call(#{method_call_recorder_args})"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
|
@@ -0,0 +1,91 @@
|
|
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 SingletonMethodDefinitionStore
|
6
|
+
# excluded list of singleton methods to define (perhaps give a better name)
|
7
|
+
def __super_module_singleton_methods_excluded_from_base_definition
|
8
|
+
@__super_module_singleton_methods_excluded_from_base_definition ||= [
|
9
|
+
:__all_module_body_method_calls_in_definition_order,
|
10
|
+
:__build_singleton_method_body_source,
|
11
|
+
:__define_super_module_singleton_methods,
|
12
|
+
:__invoke_module_body_method_calls,
|
13
|
+
:__module_body_method_calls,
|
14
|
+
:__overwrite_singleton_method_from_current_super_module,
|
15
|
+
:__singleton_method_args,
|
16
|
+
:__singleton_method_body,
|
17
|
+
:__singleton_method_body_for,
|
18
|
+
:__singleton_method_call_recorder,
|
19
|
+
:__singleton_method_definition_regex,
|
20
|
+
:__super_module_having_method,
|
21
|
+
:__super_module_singleton_methods,
|
22
|
+
:__super_module_singleton_methods_excluded_from_base_definition,
|
23
|
+
:__super_module_singleton_methods_excluded_from_call_recording,
|
24
|
+
:class_eval,
|
25
|
+
:dbg_print, #debugger library friendly exclusion
|
26
|
+
:dbg_puts, #debugger library friendly exclusion
|
27
|
+
:included,
|
28
|
+
:included_super_modules,
|
29
|
+
:singleton_method_added
|
30
|
+
]
|
31
|
+
end
|
32
|
+
|
33
|
+
def __super_module_singleton_methods
|
34
|
+
@__super_module_singleton_methods ||= []
|
35
|
+
end
|
36
|
+
|
37
|
+
def __singleton_method_body_for(super_module, method_name)
|
38
|
+
super_module.__super_module_singleton_methods.detect {|sm_method_name, sm_method_body| sm_method_name == method_name}[1]
|
39
|
+
end
|
40
|
+
|
41
|
+
def included_super_modules
|
42
|
+
included_modules.select {|m| m.include?(SuperModule)}
|
43
|
+
end
|
44
|
+
|
45
|
+
def __super_module_having_method(method_name)
|
46
|
+
included_super_modules.detect {|included_super_module| included_super_module.methods.map(&:to_s).include?(method_name.to_s)}
|
47
|
+
end
|
48
|
+
|
49
|
+
def __singleton_method_definition_regex(method_name)
|
50
|
+
/(send)?[ \t(:"']*def(ine_method)?[ \t,:"']+(self\.)?#{method_name}\)?[ \tdo{(|]*([^\n)|;]*)?[ \t)|;]*/m
|
51
|
+
end
|
52
|
+
|
53
|
+
def __singleton_method_args(method_name, method_body)
|
54
|
+
method_arg_match = method_body.match(__singleton_method_definition_regex(method_name)).to_a[4]
|
55
|
+
end
|
56
|
+
|
57
|
+
def __build_singleton_method_body_source(method_name)
|
58
|
+
method_body = self.method(method_name).source
|
59
|
+
method_args = __singleton_method_args(method_name, method_body)
|
60
|
+
method_body = "def #{method_name}\n#{method_body}\nend" if method_args.nil?
|
61
|
+
class_self_method_def_enclosure = "class << self\ndefine_method('#{method_name}') do |#{method_args}|\n#{__singleton_method_call_recorder(method_name, method_args)}\n"
|
62
|
+
method_body.sub(__singleton_method_definition_regex(method_name), class_self_method_def_enclosure) + "\nend\n"
|
63
|
+
end
|
64
|
+
|
65
|
+
def __singleton_method_body(method_name)
|
66
|
+
super_module_having_method = __super_module_having_method(method_name)
|
67
|
+
super_module_having_method ? __singleton_method_body_for(super_module_having_method, method_name) : __build_singleton_method_body_source(method_name)
|
68
|
+
end
|
69
|
+
|
70
|
+
def __overwrite_singleton_method_from_current_super_module(method_name, method_body)
|
71
|
+
if __super_module_having_method(method_name).nil?
|
72
|
+
__super_module_singleton_methods_excluded_from_base_definition << method_name
|
73
|
+
class_eval(method_body)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def singleton_method_added(method_name)
|
78
|
+
unless __super_module_singleton_methods_excluded_from_base_definition.include?(method_name)
|
79
|
+
method_body = __singleton_method_body(method_name)
|
80
|
+
__super_module_singleton_methods << [method_name, method_body]
|
81
|
+
__overwrite_singleton_method_from_current_super_module(method_name, method_body)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.extended(base)
|
86
|
+
base.extend(SuperModule::ModuleBodyMethodCallRecorder) unless base.respond_to?(:__record_method_call)
|
87
|
+
base.singleton_method_added(:__method_signature)
|
88
|
+
base.singleton_method_added(:__record_method_call)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
data/ruby187.Gemfile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
gem "method_source", "~> 0.8.2"
|
4
|
+
|
5
|
+
group :development do
|
6
|
+
gem "jeweler", "~> 2.0.1"
|
7
|
+
gem "rdoc", "~> 4.2.0"
|
8
|
+
gem "rspec", "~> 3.2.0"
|
9
|
+
|
10
|
+
# Ruby 1.8.7 support older gems
|
11
|
+
gem "mime-types", "~> 1.25.1"
|
12
|
+
gem "netrc", "~> 0.9.0"
|
13
|
+
gem "rest-client", "~> 1.6.0"
|
14
|
+
gem "nokogiri", "~> 1.5.0"
|
15
|
+
gem "highline", "~> 1.6.21"
|
16
|
+
end
|
17
|
+
|
@@ -1,80 +1,133 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe SuperModule do
|
4
|
-
module Foo
|
5
|
-
include SuperModule
|
6
|
-
validates 'foo', presence: true
|
7
4
|
|
8
|
-
|
9
|
-
|
5
|
+
class FakeActiveRecord
|
6
|
+
include FakeActiveModel
|
7
|
+
end
|
8
|
+
|
9
|
+
class FooActiveRecord < FakeActiveRecord
|
10
|
+
include Foo
|
11
|
+
end
|
12
|
+
|
13
|
+
class BarActiveRecord < FakeActiveRecord
|
14
|
+
include Bar
|
15
|
+
end
|
16
|
+
|
17
|
+
class BazActiveRecord < FakeActiveRecord
|
18
|
+
include Baz
|
19
|
+
end
|
20
|
+
|
21
|
+
context "included by a module (Foo) that is included by a class (FooActiveRecord)" do
|
22
|
+
|
23
|
+
subject { FooActiveRecord }
|
24
|
+
|
25
|
+
it 'allows invoking class methods' do
|
26
|
+
expect(subject.validations).to include(['foo', {:presence => true}])
|
10
27
|
end
|
11
28
|
|
12
|
-
|
13
|
-
'foo'
|
29
|
+
it 'includes class methods declared via "self.method_name"' do
|
30
|
+
expect(subject.foo).to eq('self.foo')
|
14
31
|
end
|
15
|
-
end
|
16
|
-
module Bar
|
17
|
-
include SuperModule
|
18
|
-
validates 'bar', presence: true
|
19
32
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
33
|
+
it 'includes instance methods' do
|
34
|
+
instance = subject.new
|
35
|
+
|
36
|
+
expect(instance.foo).to eq('foo')
|
24
37
|
end
|
25
38
|
|
26
|
-
|
27
|
-
|
39
|
+
it 'provides class method self as the including base class as in the class method (meh)' do
|
40
|
+
expect(subject.meh).to eq(subject)
|
28
41
|
end
|
29
42
|
end
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
43
|
+
|
44
|
+
context "included by a module (Foo) that is included by a second module (Bar) that is included by a class (BarActiveRecord)" do
|
45
|
+
|
46
|
+
subject { BarActiveRecord }
|
47
|
+
|
48
|
+
it 'allows invoking class methods' do
|
49
|
+
expect(subject.validations).to include(['foo', {:presence => true}])
|
50
|
+
expect(subject.validations).to include(['bar', {:presence => true}])
|
38
51
|
end
|
39
|
-
end
|
40
52
|
|
41
|
-
|
42
|
-
|
43
|
-
|
53
|
+
it 'includes class methods declared via "class << self"' do
|
54
|
+
expect(subject.foo).to eq('self.foo')
|
55
|
+
expect(subject.bar).to eq('self.bar')
|
44
56
|
end
|
45
57
|
|
46
|
-
it '
|
47
|
-
|
58
|
+
it 'includes instance methods' do
|
59
|
+
instance = subject.new
|
60
|
+
|
61
|
+
expect(instance.foo).to eq('foo')
|
62
|
+
expect(instance.bar).to eq('bar')
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'can include a basic module (Forwardable) into singleton class by placing in class << self' do
|
66
|
+
instance = subject.new
|
67
|
+
expect(instance.length).to eq(3)
|
48
68
|
end
|
49
|
-
it 'includes instance methods in the including base class' do
|
50
|
-
instance = FakeActiveRecord.new
|
51
69
|
|
52
|
-
|
70
|
+
it 'applies super module (Bar) class method invocation (make_barrable) on including class (BarActiveRecord), whereby the method that is defined in the same super module that declares it (Bar)' do
|
71
|
+
expect(subject.barrable).to eq(true)
|
53
72
|
end
|
54
|
-
|
55
|
-
|
73
|
+
|
74
|
+
it 'can include a basic module (Comparable)' do
|
75
|
+
now = Time.now
|
76
|
+
allow(Time).to receive(:now).and_return(now)
|
77
|
+
instance = subject.new
|
78
|
+
allow(Time).to receive(:now).and_return(now + 100)
|
79
|
+
instance2 = subject.new
|
80
|
+
|
81
|
+
expect(instance2 > instance).to eq(true)
|
56
82
|
end
|
83
|
+
|
84
|
+
it 'provides class method self as the including base class as in the class method (meh)' do
|
85
|
+
expect(subject.meh).to eq(subject)
|
86
|
+
end
|
87
|
+
|
88
|
+
#TODO explicitly declare test cases in support files as it statements
|
89
|
+
#TODO test empty method definition
|
90
|
+
#TODO test empty method definition with comment
|
91
|
+
#TODO test case of method call receiving a block
|
57
92
|
end
|
58
93
|
|
59
|
-
context "a
|
60
|
-
|
61
|
-
|
62
|
-
|
94
|
+
context "included by a module (Foo), included by another module (Bar), included by a third module (Baz) that is included by a class (BazActiveRecord)" do
|
95
|
+
|
96
|
+
subject { BazActiveRecord }
|
97
|
+
|
98
|
+
it 'allows invoking class methods' do
|
99
|
+
expect(subject.validations).to include(['foo', {:presence => true}])
|
100
|
+
expect(subject.validations).to include(['bar', {:presence => true}])
|
101
|
+
expect(subject.validations).to include(['baz', {:presence => true}])
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'includes class methods declared via "class << self"' do
|
105
|
+
expect(subject.foo).to eq('self.foo')
|
106
|
+
expect(subject.bar).to eq('self.bar')
|
107
|
+
expect(subject.baz).to eq('self.baz')
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'includes instance methods' do
|
111
|
+
instance = BazActiveRecord.new(100)
|
112
|
+
|
113
|
+
expect(instance.foo).to eq('foo')
|
114
|
+
expect(instance.bar).to eq('bar')
|
115
|
+
expect(instance.baz).to eq('baz')
|
63
116
|
end
|
64
117
|
|
65
|
-
it '
|
66
|
-
|
67
|
-
FakeActiveRecord.validations.should include(['bar', {presence: true}])
|
118
|
+
it 'invokes singleton method (make_barrable) from super module' do
|
119
|
+
expect(subject.barrable).to eq(true)
|
68
120
|
end
|
69
|
-
it 'includes instance methods in the including base class' do
|
70
|
-
instance = FakeActiveRecord.new
|
71
121
|
|
72
|
-
|
73
|
-
instance
|
122
|
+
it 'can override super module behavior (<=>)' do
|
123
|
+
instance = subject.new(50)
|
124
|
+
instance2 = subject.new(7)
|
125
|
+
|
126
|
+
expect(instance2 > instance).to eq(false)
|
74
127
|
end
|
75
|
-
|
76
|
-
|
77
|
-
|
128
|
+
|
129
|
+
it 'provides class method self as the including base class as in the class method (meh)' do
|
130
|
+
expect(subject.meh).to eq(subject)
|
78
131
|
end
|
79
132
|
end
|
80
133
|
|
data/spec/support/bar.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Bar
|
4
|
+
|
5
|
+
include SuperModule
|
6
|
+
include Foo
|
7
|
+
include Comparable
|
8
|
+
validates 'bar', {:presence => true}
|
9
|
+
attr_reader :created_at
|
10
|
+
|
11
|
+
# Defines singleton methods via class << self to provide as a test case for SuperModule
|
12
|
+
class << self
|
13
|
+
include Forwardable
|
14
|
+
|
15
|
+
def barrable
|
16
|
+
@barrable
|
17
|
+
end
|
18
|
+
|
19
|
+
def barrable=(value)
|
20
|
+
@barrable = value
|
21
|
+
end
|
22
|
+
|
23
|
+
def make_barrable
|
24
|
+
self.barrable = true
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
make_barrable
|
29
|
+
def_delegators :@bar, :length
|
30
|
+
|
31
|
+
def initialize
|
32
|
+
@bar = bar
|
33
|
+
@created_at = Time.now.to_f
|
34
|
+
end
|
35
|
+
|
36
|
+
def bar
|
37
|
+
'bar'
|
38
|
+
end
|
39
|
+
|
40
|
+
# Defines singleton method via a form of eval (class_eval) to provide as a test case for SuperModule
|
41
|
+
class_eval do
|
42
|
+
def self.bar
|
43
|
+
'self.bar'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def <=>(other)
|
48
|
+
created_at <=> other.created_at
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
data/spec/support/baz.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
module Baz
|
2
|
+
|
3
|
+
include SuperModule
|
4
|
+
include Bar
|
5
|
+
make_barrable
|
6
|
+
validates 'baz', {:presence => true}
|
7
|
+
attr_reader :baz_factor
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def baz
|
11
|
+
'self.baz'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(baz_factor)
|
16
|
+
super()
|
17
|
+
@baz_factor = baz_factor
|
18
|
+
end
|
19
|
+
|
20
|
+
def baz
|
21
|
+
'baz'
|
22
|
+
end
|
23
|
+
|
24
|
+
def <=>(other)
|
25
|
+
baz_factor <=> other.baz_factor
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# Uses CRLF for line breaks to provide a test case for having SuperModule work with it
|
2
|
+
# This is done to test support for Windows Ruby files, which usually use CRLF for line breaks
|
3
|
+
module FakeActiveModel
|
4
|
+
|
5
|
+
include SuperModule
|
6
|
+
|
7
|
+
# Defines method on a single line to provide as a test case for SuperModule
|
8
|
+
def self.validates(attribute, options); validations << [attribute, options]; end
|
9
|
+
|
10
|
+
# Defines method on a single line to provide as a test case for SuperModule
|
11
|
+
def self.validations; @validations ||= []; end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
data/spec/support/foo.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module Foo
|
2
|
+
|
3
|
+
include SuperModule
|
4
|
+
include FakeActiveModel
|
5
|
+
validates 'foo', {:presence => true}
|
6
|
+
|
7
|
+
def self.foo
|
8
|
+
'self.foo'
|
9
|
+
end
|
10
|
+
|
11
|
+
# Defines singleton method via define_method to provide as a test case for SuperModule
|
12
|
+
class << self
|
13
|
+
send(:define_method, :meh) do
|
14
|
+
self
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def foo
|
19
|
+
'foo'
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
data/super_module.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: super_module 1.
|
5
|
+
# stub: super_module 1.1.0 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "super_module"
|
9
|
-
s.version = "1.
|
9
|
+
s.version = "1.1.0"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib"]
|
13
13
|
s.authors = ["Andy Maleh"]
|
14
|
-
s.date = "
|
14
|
+
s.date = "2015-04-06"
|
15
15
|
s.description = "SuperModule allows defining class methods and method invocations the same way a super class does without using def included(base). This also succeeds ActiveSupport::Concern by offering lighter syntax"
|
16
16
|
s.extra_rdoc_files = [
|
17
17
|
"LICENSE.txt",
|
@@ -23,28 +23,44 @@ Gem::Specification.new do |s|
|
|
23
23
|
"README.md",
|
24
24
|
"SuperModule.jpg",
|
25
25
|
"VERSION",
|
26
|
+
"examples/reddit-readers/banister/foo.rb",
|
27
|
+
"examples/reddit-readers/banister/world.rb",
|
26
28
|
"lib/super_module.rb",
|
29
|
+
"lib/super_module/module_body_method_call_recorder.rb",
|
30
|
+
"lib/super_module/singleton_method_definition_store.rb",
|
31
|
+
"ruby187.Gemfile",
|
27
32
|
"spec/lib/super_module_spec.rb",
|
33
|
+
"spec/support/bar.rb",
|
34
|
+
"spec/support/baz.rb",
|
35
|
+
"spec/support/fake_active_model.rb",
|
36
|
+
"spec/support/foo.rb",
|
37
|
+
"spec/support/support.rb",
|
28
38
|
"super_module.gemspec"
|
29
39
|
]
|
30
40
|
s.homepage = "http://github.com/AndyObtiva/super_module"
|
31
41
|
s.licenses = ["MIT"]
|
32
|
-
s.rubygems_version = "2.
|
42
|
+
s.rubygems_version = "2.4.6"
|
33
43
|
s.summary = "SuperModule allows defining class methods and method invocations the same way a super class does without using def included(base). This also succeeds ActiveSupport::Concern by offering lighter syntax"
|
34
44
|
|
35
45
|
if s.respond_to? :specification_version then
|
36
46
|
s.specification_version = 4
|
37
47
|
|
38
48
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
39
|
-
s.
|
40
|
-
s.add_development_dependency(%q<
|
49
|
+
s.add_runtime_dependency(%q<method_source>, ["~> 0.8.2"])
|
50
|
+
s.add_development_dependency(%q<jeweler>, ["~> 2.0.1"])
|
51
|
+
s.add_development_dependency(%q<rdoc>, ["~> 4.2.0"])
|
52
|
+
s.add_development_dependency(%q<rspec>, ["~> 3.2.0"])
|
41
53
|
else
|
42
|
-
s.add_dependency(%q<
|
43
|
-
s.add_dependency(%q<
|
54
|
+
s.add_dependency(%q<method_source>, ["~> 0.8.2"])
|
55
|
+
s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
|
56
|
+
s.add_dependency(%q<rdoc>, ["~> 4.2.0"])
|
57
|
+
s.add_dependency(%q<rspec>, ["~> 3.2.0"])
|
44
58
|
end
|
45
59
|
else
|
46
|
-
s.add_dependency(%q<
|
47
|
-
s.add_dependency(%q<
|
60
|
+
s.add_dependency(%q<method_source>, ["~> 0.8.2"])
|
61
|
+
s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
|
62
|
+
s.add_dependency(%q<rdoc>, ["~> 4.2.0"])
|
63
|
+
s.add_dependency(%q<rspec>, ["~> 3.2.0"])
|
48
64
|
end
|
49
65
|
end
|
50
66
|
|
metadata
CHANGED
@@ -1,43 +1,71 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: super_module
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andy Maleh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-04-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: method_source
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.8.2
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.8.2
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: jeweler
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
16
30
|
requirements:
|
17
31
|
- - "~>"
|
18
32
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
33
|
+
version: 2.0.1
|
20
34
|
type: :development
|
21
35
|
prerelease: false
|
22
36
|
version_requirements: !ruby/object:Gem::Requirement
|
23
37
|
requirements:
|
24
38
|
- - "~>"
|
25
39
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
40
|
+
version: 2.0.1
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: rdoc
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
30
44
|
requirements:
|
31
45
|
- - "~>"
|
32
46
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
47
|
+
version: 4.2.0
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 4.2.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 3.2.0
|
34
62
|
type: :development
|
35
63
|
prerelease: false
|
36
64
|
version_requirements: !ruby/object:Gem::Requirement
|
37
65
|
requirements:
|
38
66
|
- - "~>"
|
39
67
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
68
|
+
version: 3.2.0
|
41
69
|
description: SuperModule allows defining class methods and method invocations the
|
42
70
|
same way a super class does without using def included(base). This also succeeds
|
43
71
|
ActiveSupport::Concern by offering lighter syntax
|
@@ -53,8 +81,18 @@ files:
|
|
53
81
|
- README.md
|
54
82
|
- SuperModule.jpg
|
55
83
|
- VERSION
|
84
|
+
- examples/reddit-readers/banister/foo.rb
|
85
|
+
- examples/reddit-readers/banister/world.rb
|
56
86
|
- lib/super_module.rb
|
87
|
+
- lib/super_module/module_body_method_call_recorder.rb
|
88
|
+
- lib/super_module/singleton_method_definition_store.rb
|
89
|
+
- ruby187.Gemfile
|
57
90
|
- spec/lib/super_module_spec.rb
|
91
|
+
- spec/support/bar.rb
|
92
|
+
- spec/support/baz.rb
|
93
|
+
- spec/support/fake_active_model.rb
|
94
|
+
- spec/support/foo.rb
|
95
|
+
- spec/support/support.rb
|
58
96
|
- super_module.gemspec
|
59
97
|
homepage: http://github.com/AndyObtiva/super_module
|
60
98
|
licenses:
|
@@ -76,7 +114,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
76
114
|
version: '0'
|
77
115
|
requirements: []
|
78
116
|
rubyforge_project:
|
79
|
-
rubygems_version: 2.
|
117
|
+
rubygems_version: 2.4.6
|
80
118
|
signing_key:
|
81
119
|
specification_version: 4
|
82
120
|
summary: SuperModule allows defining class methods and method invocations the same
|