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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: def423de958bb4f6b3d2c6b3edb60908d42bbe86
4
- data.tar.gz: 962866a97336aaeeebd16386e1cf37417e1d2aa9
3
+ metadata.gz: 5ed999facd85a8593e0e0af6a036c625a68f51ae
4
+ data.tar.gz: 27219be2a1966b18e253d839b76232e118d27824
5
5
  SHA512:
6
- metadata.gz: 99668ddec0c2e332493f7cda830428e4915d65eaf35bde9b5a0336233396425c458746734082917028a310b8fa0e4a01d0b430f0f3a78a31b7cd5afdd541e5c1
7
- data.tar.gz: fb848a9f6979fe0f079ddc9d3967eb600b85186659ce6457d220ec5ce55be0c460928fe09e5403cfe0689009a4c93ae9a3ce4102e1b07ac27785027f525e7b02
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 and using complex libraries like ActiveSupport::Concern to accomplish that goal?
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
- Well, worry no more! SuperModule comes to the rescue!
101
+ ## Instructions
11
102
 
12
- ![SuperModule](https://raw.githubusercontent.com/AndyObtiva/super_module/master/SuperModule.jpg)
103
+ #### 1) Install and require gem
13
104
 
14
- SuperModule allows defining class methods and method invocations the same way a super class does without using def included(base).
105
+ <b>Using [Bundler](http://bundler.io/)</b>
15
106
 
16
- This succeeds ActiveSupport::Concern by offering lighter syntax and simpler module dependency support.
107
+ Add the following to Gemfile: <pre>gem 'super_module', '1.0.0'</pre>
17
108
 
18
- ## Instructions
109
+ And run the following command: <pre>bundle</pre>
19
110
 
20
- ### 1) Install and require gem
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 Bundler</b>
113
+ <b>Using [RubyGem](https://rubygems.org/gems/super_module) Directly</b>
23
114
 
24
- Add the following to Gemfile: <pre>gem 'super_module', '1.0.0'</pre>
25
- Run: <code>bundle</code>
26
-
27
- It will automatically get required in the application when loading with bundler (e.g. in a Rails application)
28
-
29
- <b>Using RubyGem Directly</b>
30
-
31
- Run: <pre>gem install super_module</pre>
32
- (add <code>--no-ri --no-rdoc</code> if you wish to skip downloading them for a faster install)
33
-
34
- Add <code>require 'super_module'</code> at the top of your Ruby file
35
-
36
- ### 2) Include SuperModule at the top of the module
37
-
38
- > module UserIdentifiable
39
- > include SuperModule
40
- >
41
- > belongs_to :user
42
- > validates :user_id, presence: true
43
- >
44
- > def self.most_active_user
45
- > User.find_by_id(select('count(id) as head_count, user_id').group('user_id').order('count(id) desc').first.user_id)
46
- > end
47
- >
48
- > def slug
49
- > "#{self.class.name}_#{user_id}"
50
- > end
51
- > end
52
-
53
- ### 3) Mix newly defined module into a class or another super module
54
-
55
- > class ClubParticipation < ActiveRecord::Base
56
- > include UserIdentifiable
57
- > end
58
- > class CourseEnrollment < ActiveRecord::Base
59
- > include UserIdentifiable
60
- > end
61
- > module Accountable
62
- > include SuperModule
63
- > include UserIdentifiable
64
- > end
65
- > class Activity < ActiveRecord::Base
66
- > include Accountable
67
- > end
68
-
69
- ### 4) Start using by invoking class methods or instance methods
70
-
71
- > CourseEnrollment.most_active_user
72
- > ClubParticipation.most_active_user
73
- > Activity.last.slug
74
- > ClubParticipation.create(club_id: club.id, user_id: user.id).slug
75
- > CourseEnrollment.new(course_id: course.id).valid?
76
-
77
- ## Example
78
-
79
- > require 'super_module'
80
- >
81
- > module Foo
82
- > include SuperModule
83
- >
84
- > validates :credit_card_id, presence: true
85
- >
86
- > def foo
87
- > puts 'foo'
88
- > 'foo'
89
- > end
90
- >
91
- > def self.foo
92
- > puts 'self.foo'
93
- > 'self.foo'
94
- > end
95
- > end
96
- >
97
- > module Bar
98
- > include SuperModule
99
- > include Foo
100
- >
101
- > validates :user_id, presence: true
102
- >
103
- > def bar
104
- > puts 'bar'
105
- > 'bar'
106
- > end
107
- >
108
- > def self.bar
109
- > puts 'self.bar'
110
- > 'self.bar'
111
- > end
112
- > end
113
- >
114
- > class MediaAuthorization < ActiveRecord::Base
115
- > include Bar
116
- > end
117
- >
118
- > MediaAuthorization.create.errors.messages.inspect
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
- > MediaAuthorization.new.foo
234
+ ```ruby
235
+ MediaAuthorization.new.foo
236
+ ```
123
237
 
124
238
  => "foo"
125
239
 
126
- > MediaAuthorization.new.bar
240
+ ```ruby
241
+ MediaAuthorization.new.bar
242
+ ```
127
243
 
128
244
  => "bar"
129
245
 
130
- > MediaAuthorization.foo
246
+ ```ruby
247
+ MediaAuthorization.foo
248
+ ```
131
249
 
132
250
  => "self.foo"
133
251
 
134
- > MediaAuthorization.bar
252
+ ```ruby
253
+ MediaAuthorization.bar
254
+ ```
135
255
 
136
256
  => "self.bar"
137
257
 
138
- ## Design Limitations
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
- This has been designed to be used only in the code definition of a module.
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.0.0
1
+ 1.1.0
@@ -0,0 +1,8 @@
1
+ require_relative '../../../lib/super_module'
2
+
3
+ module Foo
4
+ include SuperModule
5
+ def self.hello
6
+ self
7
+ end
8
+ end
@@ -0,0 +1,5 @@
1
+ require_relative 'foo'
2
+
3
+ class World
4
+ include Foo
5
+ end
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
- module SuperModule
10
- EXCLUDED_SINGLETON_METHODS = [
11
- :__super_module_class_methods,
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
- def __super_module_class_method_calls
27
- @__super_module_class_method_calls ||= []
28
- end
29
-
30
- def __super_module_class_methods
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
- def __invoke_super_module_class_method_calls(base)
44
- __super_module_class_method_calls.each do |method_name, args, block|
45
- base.class_eval do
46
- send(method_name, *args, &block)
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 __define_super_module_class_methods(base)
52
- __super_module_class_methods.each do |method_name, method|
53
- base.class_eval do
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
- __invoke_super_module_class_method_calls(base)
61
- __define_super_module_class_methods(base)
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
- def self.foo
9
- 'self.foo'
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
- def foo
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
- class << self
21
- def bar
22
- 'self.bar'
23
- end
33
+ it 'includes instance methods' do
34
+ instance = subject.new
35
+
36
+ expect(instance.foo).to eq('foo')
24
37
  end
25
38
 
26
- def bar
27
- 'bar'
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
- class FakeActiveRecord
31
- class << self
32
- def validates(attribute, options)
33
- validations << [attribute, options]
34
- end
35
- def validations
36
- @validations ||= []
37
- end
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
- context "a base class includes a module enhanced as a super module" do
42
- before do
43
- FakeActiveRecord.send(:include, Foo)
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 'allows invoking class methods in the including base class body' do
47
- FakeActiveRecord.validations.should include(['foo', {presence: true}])
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
- instance.foo.should == 'foo'
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
- it 'includes class methods in the including base class' do
55
- FakeActiveRecord.foo.should == 'self.foo'
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 base class includes a base module enhanced as a super module that includes another module enhanced as a super module" do
60
- before do
61
- Bar.send(:include, Foo)
62
- FakeActiveRecord.send(:include, Bar)
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 'allows invoking class methods in the including base class body' do
66
- FakeActiveRecord.validations.should include(['foo', {presence: true}])
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
- instance.foo.should == 'foo'
73
- instance.bar.should == 'bar'
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
- it 'includes class methods in the including base class' do
76
- FakeActiveRecord.foo.should == 'self.foo'
77
- FakeActiveRecord.bar.should == 'self.bar'
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
 
@@ -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
+
@@ -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
+
@@ -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
+
@@ -0,0 +1,4 @@
1
+ require File.join(File.dirname(__FILE__), 'fake_active_model')
2
+ require File.join(File.dirname(__FILE__), 'foo')
3
+ require File.join(File.dirname(__FILE__), 'bar')
4
+ require File.join(File.dirname(__FILE__), 'baz')
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.0.0 ruby lib
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.0.0"
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 = "2014-03-27"
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.2.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.add_development_dependency(%q<jeweler>, ["~> 2.0"])
40
- s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
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<jeweler>, ["~> 2.0"])
43
- s.add_dependency(%q<rdoc>, ["~> 3.12"])
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<jeweler>, ["~> 2.0"])
47
- s.add_dependency(%q<rdoc>, ["~> 3.12"])
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.0.0
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: 2014-03-27 00:00:00.000000000 Z
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: '2.0'
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: '2.0'
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: '3.12'
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: '3.12'
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.2.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