super_module 1.2.2 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +35 -0
- data/README.md +33 -53
- data/VERSION +1 -1
- data/lib/super_module.rb +3 -41
- data/lib/super_module/v1.rb +1 -1
- data/lib/super_module/v1/singleton_method_definition_store.rb +5 -4
- data/spec/lib/super_module_spec.rb +195 -158
- data/spec/support/v1.rb +1 -1
- data/spec/support/v1/bar.rb +3 -4
- data/spec/support/v1/baz.rb +4 -5
- data/spec/support/v1/fake_active_model.rb +1 -1
- data/spec/support/v1/foo.rb +4 -5
- data/super_module.gemspec +9 -21
- metadata +19 -21
- data/examples/reddit-readers/banister/foo.rb +0 -7
- data/examples/reddit-readers/banister/world.rb +0 -5
- data/ruby187.Gemfile +0 -18
- data/spec/support/bar.rb +0 -52
- data/spec/support/baz.rb +0 -27
- data/spec/support/fake_active_model.rb +0 -16
- data/spec/support/foo.rb +0 -88
- data/spec/support/v2.rb +0 -6
- data/spec/support/v2/bar.rb +0 -52
- data/spec/support/v2/baz.rb +0 -27
- data/spec/support/v2/fake_active_model.rb +0 -14
- data/spec/support/v2/foo.rb +0 -87
- data/spec/support/v2_alt.rb +0 -6
- data/spec/support/v2_alt/bar.rb +0 -52
- data/spec/support/v2_alt/baz.rb +0 -27
- data/spec/support/v2_alt/fake_active_model.rb +0 -14
- data/spec/support/v2_alt/foo.rb +0 -87
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 70513ad566652aaf58d9da92fe22c598be383f3df25700a25245b0862e9b5580
|
4
|
+
data.tar.gz: 3002cb8ce8d0e853b1228f29f952034a7f9c2520c9635806860bde0959fe448b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e20a35408bb4be154120310d932fe679d0ddc1224817886c1235cfdcb89750a6fd9655d9b0432e9f5441504122d714042da22527ac8ebb1316f3f3e6b1359021
|
7
|
+
data.tar.gz: 04db18d5c5991a8342d868a56aa8143b71b4775dd7f7f4a4cb919f9fb0c1073775602441fe41e0d19630bac5f690f52ce9c4cca5170d58bc3af1fafda87a862e
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# Change Log
|
2
|
+
|
3
|
+
## 1.3.0
|
4
|
+
|
5
|
+
- Dropped support for SuperModule 2 Beta syntax, reverting to V1 syntax as the default
|
6
|
+
- Added `included_super_module` method to allow modules to call it if they need to redefine `self.included` for meta-programming.
|
7
|
+
|
8
|
+
## 1.2.2
|
9
|
+
|
10
|
+
* Relaxed dependency on `method_source` gem version
|
11
|
+
|
12
|
+
## v2 Beta (v1.2.1)
|
13
|
+
|
14
|
+
* Standalone super module usage (e.g. direct class method invocation)
|
15
|
+
|
16
|
+
## v2 Beta (v1.2.0)
|
17
|
+
|
18
|
+
* New `super_module(name)` syntax
|
19
|
+
* Much simpler implementation with guaranteed correctness and no performance hit
|
20
|
+
* Less memory footprint by not requiring method_source Ruby gem for v2 syntax
|
21
|
+
* Backwards compatibility with v1 syntax
|
22
|
+
|
23
|
+
## v1.1.1
|
24
|
+
|
25
|
+
* Added support for private and protected methods
|
26
|
+
* Added many more RSpec test cases, including testing of empty and comment containing singleton methods
|
27
|
+
|
28
|
+
## v1.1.0
|
29
|
+
|
30
|
+
* Brand new `self`-friendly algorithm that ensures true mixing of super module singleton methods into the including base class or module, thus always returning the actual base class or module `self` when invoking a super module inherited singleton method (thanks to [Banister](https://github.com/banister) for [reporting previous limitation on Reddit and providing suggestions](http://www.reddit.com/r/ruby/comments/30j66y/step_aside_activesupportconcern_supermodule_is/))
|
31
|
+
* New `included_super_modules` inherited singleton method that provides developer with a list of all included super modules similar to the Ruby `included_modules` method.
|
32
|
+
* No more use for method_missing (Thanks to Marc-André Lafortune for bringing up as a previous limitation in [AirPair article reviews](https://www.airpair.com/ruby/posts/step-aside-activesupportconcern-supermodule-is-the-new-sheriff-in-town))
|
33
|
+
* New dependency on [Banister](https://github.com/banister)'s [method_source](https://github.com/banister/method_source) library to have the self-friendly algorithm eval inherited class method sources into the including base class or module.
|
34
|
+
* Refactorings, including break-up of the original SuperModule into 3 modules in separate files
|
35
|
+
* More RSpec test coverage, including additional method definition scenarios, such as when adding dynamically via `class_eval` and `define_method`
|
data/README.md
CHANGED
@@ -1,5 +1,6 @@
|
|
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 1.3
|
2
2
|
[![Gem Version](https://badge.fury.io/rb/super_module.svg)](http://badge.fury.io/rb/super_module)
|
3
|
+
[![Build Status](https://api.travis-ci.org/AndyObtiva/super_module.svg?branch=master)](https://travis-ci.org/AndyObtiva/super_module)
|
3
4
|
[![Coverage Status](https://coveralls.io/repos/AndyObtiva/super_module/badge.svg?branch=master)](https://coveralls.io/r/AndyObtiva/super_module?branch=master)
|
4
5
|
[![Code Climate](https://codeclimate.com/github/AndyObtiva/super_module.svg)](https://codeclimate.com/github/AndyObtiva/super_module)
|
5
6
|
|
@@ -9,7 +10,9 @@ Ruby offers one workaround in the form of implementing the hook method [`Module.
|
|
9
10
|
|
10
11
|
Another workaround is [`ActiveSupport::Concern`](http://api.rubyonrails.org/classes/ActiveSupport/Concern.html), a 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 helps improve readability a bit, it adds even more boilerplate idiom cruft, thus feeling no more than putting a band-aid on the problem.
|
11
12
|
|
12
|
-
But do not fear, [SuperModule](https://rubygems.org/gems/super_module) comes to the rescue! By declaring your module as a
|
13
|
+
But do not fear, [SuperModule](https://rubygems.org/gems/super_module) comes to the rescue! By declaring your module as a SuperModule, it will simply behave as one would expect and automatically include class methods along with instance methods, without any further work needed.
|
14
|
+
|
15
|
+
Used in my other project: [Glimmer](https://github.com/AndyObtiva/Glimmer) (Ruby Desktop GUI Library)
|
13
16
|
|
14
17
|
## Introductory Comparison
|
15
18
|
|
@@ -73,7 +76,8 @@ A step forward that addresses the boiler-plate repetitive code concern, but is o
|
|
73
76
|
#### 3) [SuperModule](https://github.com/AndyObtiva/super_module)
|
74
77
|
|
75
78
|
```ruby
|
76
|
-
|
79
|
+
module UserIdentifiable
|
80
|
+
include SuperModule
|
77
81
|
include ActiveModel::Model
|
78
82
|
|
79
83
|
belongs_to :user
|
@@ -88,28 +92,19 @@ super_module :UserIdentifiable do
|
|
88
92
|
end
|
89
93
|
end
|
90
94
|
```
|
91
|
-
|
95
|
+
By including `SuperModule` (following Ruby's basic convention of relying on a 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.
|
92
96
|
|
93
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.
|
94
98
|
|
95
99
|
In other words, [SuperModule](https://rubygems.org/gems/super_module) furthers Ruby's goal of making programmers happy.
|
96
100
|
|
97
|
-
By the way, SuperModule 2 Beta supports an alternate syntax as well:
|
98
|
-
|
99
|
-
```ruby
|
100
|
-
UserIdentifiable = super_module do
|
101
|
-
end
|
102
|
-
```
|
103
|
-
|
104
|
-
|
105
|
-
|
106
101
|
## Instructions
|
107
102
|
|
108
103
|
#### 1) Install and require gem
|
109
104
|
|
110
105
|
<b>Using [Bundler](http://bundler.io/)</b>
|
111
106
|
|
112
|
-
Add the following to Gemfile: <pre>gem 'super_module', '1.
|
107
|
+
Add the following to Gemfile: <pre>gem 'super_module', '1.3.0'</pre>
|
113
108
|
|
114
109
|
And run the following command: <pre>bundle</pre>
|
115
110
|
|
@@ -123,10 +118,11 @@ Run the following command: <pre>gem install super_module</pre>
|
|
123
118
|
|
124
119
|
Add the following at the top of your [Ruby](https://www.ruby-lang.org/en/) file: <pre>require 'super_module'</pre>
|
125
120
|
|
126
|
-
#### 2)
|
121
|
+
#### 2) Simply include SuperModule in your module (just like you would do any other Ruby module)
|
127
122
|
|
128
123
|
```ruby
|
129
|
-
|
124
|
+
module UserIdentifiable
|
125
|
+
include SuperModule
|
130
126
|
include ActiveModel::Model
|
131
127
|
|
132
128
|
belongs_to :user
|
@@ -151,7 +147,8 @@ end
|
|
151
147
|
class CourseEnrollment < ActiveRecord::Base
|
152
148
|
include UserIdentifiable
|
153
149
|
end
|
154
|
-
|
150
|
+
module Accountable
|
151
|
+
include SuperModule
|
155
152
|
include UserIdentifiable
|
156
153
|
end
|
157
154
|
class Activity < ActiveRecord::Base
|
@@ -194,7 +191,8 @@ Create a ruby file called super_module_irb_example.rb with the following content
|
|
194
191
|
require 'rubygems' # to be backwards compatible with Ruby 1.8.7
|
195
192
|
require 'super_module'
|
196
193
|
|
197
|
-
|
194
|
+
module RequiresAttributes
|
195
|
+
include SuperModule
|
198
196
|
|
199
197
|
def self.requires(*attributes)
|
200
198
|
attributes.each {|attribute| required_attributes << attribute}
|
@@ -267,58 +265,35 @@ V2 has a much simpler algorithm than V1 that goes as follows:
|
|
267
265
|
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
|
268
266
|
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`
|
269
267
|
|
270
|
-
##
|
268
|
+
## Warnings
|
269
|
+
|
270
|
+
1) [SuperModule](https://rubygems.org/gems/super_module) by definition has been designed to be used only in the initial code declaration of a module, not later mixing or re-opening of a module.
|
271
271
|
|
272
|
-
|
272
|
+
2) Given [SuperModule](https://rubygems.org/gems/super_module)'s implementation relies on `self.included(base)`, sub-modules must not hook into it.
|
273
273
|
|
274
|
-
|
274
|
+
In very rare occasions when an including module needs to redefine <code>self.included(base)</code> for meta-programming purposes, you may do so at your own peril by first invoking <code>self.included_super_module(base)</code> like in this example:
|
275
275
|
```ruby
|
276
|
-
|
276
|
+
module AdminIdentifiable
|
277
|
+
include SuperModule
|
277
278
|
include UserIdentifiable
|
278
279
|
|
279
280
|
class << self
|
280
|
-
alias included_super_module included
|
281
281
|
def included(base)
|
282
282
|
included_super_module(base)
|
283
|
-
# do some extra work
|
283
|
+
# do some extra rare meta-programming work
|
284
284
|
# like conditional inclusion of other modules
|
285
285
|
# or conditional definition of methods
|
286
286
|
end
|
287
287
|
end
|
288
288
|
end
|
289
289
|
```
|
290
|
-
|
290
|
+
This does not work for all cases (like multiple levels of super module nesting), and is not recommended, likely causing problems.
|
291
291
|
|
292
|
-
|
292
|
+
Avoid hooking into `self.included(base)` at all costs.
|
293
293
|
|
294
|
-
|
294
|
+
## Change Log
|
295
295
|
|
296
|
-
|
297
|
-
|
298
|
-
### v2 Beta (v1.2.1)
|
299
|
-
|
300
|
-
* Standalone super module usage (e.g. direct class method invocation)
|
301
|
-
|
302
|
-
### v2 Beta (v1.2.0)
|
303
|
-
|
304
|
-
* New `super_module(name)` syntax
|
305
|
-
* Much simpler implementation with guaranteed correctness and no performance hit
|
306
|
-
* Less memory footprint by not requiring method_source Ruby gem for v2 syntax
|
307
|
-
* Backwards compatibility with v1 syntax
|
308
|
-
|
309
|
-
### v1.1.1
|
310
|
-
|
311
|
-
* Added support for private and protected methods
|
312
|
-
* Added many more RSpec test cases, including testing of empty and comment containing singleton methods
|
313
|
-
|
314
|
-
### v1.1.0
|
315
|
-
|
316
|
-
* Brand new `self`-friendly algorithm that ensures true mixing of super module singleton methods into the including base class or module, thus always returning the actual base class or module `self` when invoking a super module inherited singleton method (thanks to [Banister](https://github.com/banister) for [reporting previous limitation on Reddit and providing suggestions](http://www.reddit.com/r/ruby/comments/30j66y/step_aside_activesupportconcern_supermodule_is/))
|
317
|
-
* New `included_super_modules` inherited singleton method that provides developer with a list of all included super modules similar to the Ruby `included_modules` method.
|
318
|
-
* No more use for method_missing (Thanks to Marc-André Lafortune for bringing up as a previous limitation in [AirPair article reviews](https://www.airpair.com/ruby/posts/step-aside-activesupportconcern-supermodule-is-the-new-sheriff-in-town))
|
319
|
-
* New dependency on [Banister](https://github.com/banister)'s [method_source](https://github.com/banister/method_source) library to have the self-friendly algorithm eval inherited class method sources into the including base class or module.
|
320
|
-
* Refactorings, including break-up of the original SuperModule into 3 modules in separate files
|
321
|
-
* More RSpec test coverage, including additional method definition scenarios, such as when adding dynamically via `class_eval` and `define_method`
|
296
|
+
[CHANGELOG.md](https://raw.githubusercontent.com/AndyObtiva/super_module/master/CHANGELOG.md)
|
322
297
|
|
323
298
|
## Feedback and Contribution
|
324
299
|
|
@@ -332,6 +307,11 @@ The library is quite new and can use all the feedback and help it can get. So, p
|
|
332
307
|
* 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)
|
333
308
|
* 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)
|
334
309
|
|
310
|
+
## TODO
|
311
|
+
|
312
|
+
- Fix issue where class methods cannot get called from super_module directly (when used with "CONSTANT = super_module do" approach)
|
313
|
+
- Fix issue where a super module (declared with super_module) can get included in one class only where class methods work, but the next class that includes the super module does not have class methods work
|
314
|
+
|
335
315
|
## Copyright
|
336
316
|
|
337
317
|
Copyright (c) 2014-2016 Andy Maleh. See LICENSE.txt for
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.3.0
|
data/lib/super_module.rb
CHANGED
@@ -8,50 +8,12 @@
|
|
8
8
|
|
9
9
|
# Avoiding require_relative for backwards compatibility with Ruby 1.8.7
|
10
10
|
|
11
|
+
require_relative 'super_module/v1'
|
12
|
+
|
11
13
|
module SuperModule
|
12
14
|
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
15
|
def included(original_base)
|
22
|
-
|
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)
|
39
|
-
end
|
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
|
-
super_module_body = new_super_module.super_module_body
|
54
|
-
new_super_module.class_eval(&super_module_body)
|
16
|
+
original_base.send(:include, SuperModule::V1)
|
55
17
|
end
|
56
18
|
end
|
57
19
|
end
|
data/lib/super_module/v1.rb
CHANGED
@@ -26,7 +26,8 @@ module SuperModule
|
|
26
26
|
:dbg_print, #debugger library friendly exclusion
|
27
27
|
:dbg_puts, #debugger library friendly exclusion
|
28
28
|
:define,
|
29
|
-
:included,
|
29
|
+
:included,
|
30
|
+
:included_super_module,
|
30
31
|
:included_super_modules,
|
31
32
|
:singleton_method_added,
|
32
33
|
:super_module_body
|
@@ -36,7 +37,7 @@ module SuperModule
|
|
36
37
|
def __super_module_singleton_methods
|
37
38
|
@__super_module_singleton_methods ||= []
|
38
39
|
end
|
39
|
-
|
40
|
+
|
40
41
|
def __singleton_method_body_for(super_module, method_name)
|
41
42
|
super_module.__super_module_singleton_methods.detect {|sm_method_name, sm_method_body| sm_method_name == method_name}[1]
|
42
43
|
end
|
@@ -78,7 +79,7 @@ module SuperModule
|
|
78
79
|
|
79
80
|
def __singleton_method_body(method_name)
|
80
81
|
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
|
+
super_module_having_method ? __singleton_method_body_for(super_module_having_method, method_name) : __build_singleton_method_body_source(method_name)
|
82
83
|
end
|
83
84
|
|
84
85
|
def __overwrite_singleton_method_from_current_super_module(method_name, method_body)
|
@@ -91,7 +92,7 @@ module SuperModule
|
|
91
92
|
def singleton_method_added(method_name)
|
92
93
|
unless __super_module_singleton_methods_excluded_from_base_definition.include?(method_name)
|
93
94
|
method_body = __singleton_method_body(method_name)
|
94
|
-
__super_module_singleton_methods << [method_name, method_body]
|
95
|
+
__super_module_singleton_methods << [method_name, method_body]
|
95
96
|
__overwrite_singleton_method_from_current_super_module(method_name, method_body)
|
96
97
|
end
|
97
98
|
end
|
@@ -1,219 +1,256 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
|
3
|
+
class V1::FakeActiveRecord
|
4
|
+
include ::V1::FakeActiveModel
|
5
|
+
end
|
4
6
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
].each do |version|
|
9
|
-
Object.send(:remove_const, :SupportVersion) rescue nil
|
10
|
-
SupportVersion = Support.const_get(version)
|
11
|
-
|
12
|
-
Object.send(:remove_const, :FakeActiveRecord) rescue nil
|
13
|
-
class FakeActiveRecord
|
14
|
-
include SupportVersion::FakeActiveModel
|
15
|
-
end
|
7
|
+
class V1::FooActiveRecord < V1::FakeActiveRecord
|
8
|
+
include ::V1::Foo
|
9
|
+
end
|
16
10
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
11
|
+
class V1::BarActiveRecord < V1::FakeActiveRecord
|
12
|
+
include ::V1::Bar
|
13
|
+
end
|
14
|
+
|
15
|
+
class V1::BazActiveRecord < V1::FakeActiveRecord
|
16
|
+
include ::V1::Baz
|
17
|
+
end
|
18
|
+
|
19
|
+
module V1::SummarizedActiveModel
|
20
|
+
include SuperModule
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
def self.included(klass)
|
23
|
+
included_super_module(klass)
|
24
|
+
if klass.name.split(/::/).last.start_with?('Fake')
|
25
|
+
klass.extend(FakeClassMethods)
|
25
26
|
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.validates(attribute, options)
|
30
|
+
super # test that singleton class inheritance works
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.validations
|
34
|
+
super # test that singleton class inheritance works
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.summary
|
38
|
+
validations.flatten.map(&:to_s).join("/")
|
39
|
+
end
|
26
40
|
|
27
|
-
|
28
|
-
|
29
|
-
|
41
|
+
module FakeClassMethods
|
42
|
+
def fake_summary
|
43
|
+
'This is a fake summary.'
|
30
44
|
end
|
45
|
+
end
|
46
|
+
end
|
31
47
|
|
32
|
-
|
48
|
+
class V1::SummarizedActiveRecord < V1::FakeActiveRecord
|
49
|
+
include ::V1::SummarizedActiveModel
|
50
|
+
end
|
33
51
|
|
34
|
-
|
35
|
-
|
52
|
+
class V1::FakeSummarizedActiveRecord < V1::FakeActiveRecord
|
53
|
+
include ::V1::SummarizedActiveModel
|
54
|
+
end
|
36
55
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
56
|
+
describe SuperModule do
|
57
|
+
context V1 do
|
58
|
+
context "standalone module usage" do
|
59
|
+
subject { V1::FakeActiveModel }
|
41
60
|
|
61
|
+
it 'allows invoking class methods' do
|
62
|
+
subject.validates 'foo', {:presence => true}
|
63
|
+
expect(subject.validations).to include(['foo', {:presence => true}])
|
42
64
|
end
|
65
|
+
end
|
43
66
|
|
44
|
-
|
67
|
+
context "included by a module (Foo) that is included by a class (FooActiveRecord)" do
|
45
68
|
|
46
|
-
|
69
|
+
subject { V1::FooActiveRecord }
|
47
70
|
|
48
|
-
|
49
|
-
|
50
|
-
|
71
|
+
it 'allows invoking class methods' do
|
72
|
+
expect(subject.validations).to include(['foo', {:presence => true}])
|
73
|
+
end
|
51
74
|
|
52
|
-
|
53
|
-
|
54
|
-
|
75
|
+
it 'includes class method declared via "self.method_name"' do
|
76
|
+
expect(subject.foo).to eq('self.foo')
|
77
|
+
end
|
55
78
|
|
56
|
-
|
57
|
-
|
58
|
-
|
79
|
+
it 'includes class method declared via "self.method_name" taking a single parameter' do
|
80
|
+
expect(subject.foo_single_param('param1_value')).to eq('self.foo(param1_value)')
|
81
|
+
end
|
59
82
|
|
60
|
-
|
61
|
-
|
62
|
-
|
83
|
+
it 'includes class method declared via "self.method_name" taking multiple parameters' do
|
84
|
+
expect(subject.foo_multi_params('param1_value', 'param2_value', 'param3_value')).to eq('self.foo(param1_value,param2_value,param3_value)')
|
85
|
+
end
|
63
86
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
87
|
+
it 'includes class method declared via "self.method_name" taking a block' do
|
88
|
+
formatter = Proc.new {|value| "Block formatted #{value}"}
|
89
|
+
expect(subject.foo_block(&formatter)).to eq('Block formatted self.foo')
|
90
|
+
end
|
68
91
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
92
|
+
it 'includes class method declared via "self.method_name" taking a single paramter and a block' do
|
93
|
+
formatter = Proc.new {|value, param1| "Block formatted #{value} with #{param1}"}
|
94
|
+
expect(subject.foo_single_param_block('param1_value', &formatter)).to eq('Block formatted self.foo with param1_value')
|
95
|
+
end
|
73
96
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
97
|
+
it 'includes class method declared via "self.method_name" taking multiple paramters and a block' do
|
98
|
+
formatter = Proc.new {|value, param1, param2, param3| "Block formatted #{value} with #{param1},#{param2},#{param3}"}
|
99
|
+
expect(subject.foo_multi_params_block('param1_value', 'param2_value', 'param3_value', &formatter)).to eq('Block formatted self.foo with param1_value,param2_value,param3_value')
|
100
|
+
end
|
78
101
|
|
79
|
-
|
80
|
-
|
81
|
-
|
102
|
+
it 'includes class method declared via "self.method_name" on one line' do
|
103
|
+
expect(subject.foo_one_line).to eq('self.foo_one_line')
|
104
|
+
end
|
82
105
|
|
83
|
-
|
84
|
-
|
85
|
-
|
106
|
+
it 'includes class method declared via "class < self"' do
|
107
|
+
expect(subject.foo_class_self).to eq('self.foo_class_self')
|
108
|
+
end
|
86
109
|
|
87
|
-
|
88
|
-
|
89
|
-
|
110
|
+
it 'includes class method declared via "class < self" using define_method' do
|
111
|
+
expect(subject.foo_class_self_define_method).to eq('self.foo_class_self_define_method')
|
112
|
+
end
|
90
113
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
114
|
+
it 'includes private class method' do
|
115
|
+
expect{subject.foo_private}.to raise_error
|
116
|
+
expect(subject.private_methods.map(&:to_s)).to include('foo_private')
|
117
|
+
expect(subject.send(:foo_private)).to eq('self.foo_private')
|
118
|
+
end
|
96
119
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
120
|
+
it 'includes protected class method (declared using protected :method_name)' do
|
121
|
+
expect{subject.foo_protected}.to raise_error
|
122
|
+
expect(subject.protected_methods.map(&:to_s)).to include('foo_protected')
|
123
|
+
expect(subject.send(:foo_protected)).to eq('self.foo_protected')
|
124
|
+
end
|
102
125
|
|
103
|
-
|
104
|
-
|
105
|
-
|
126
|
+
it 'includes empty class method' do
|
127
|
+
expect(subject.empty).to eq(nil)
|
128
|
+
end
|
106
129
|
|
107
|
-
|
108
|
-
|
109
|
-
|
130
|
+
it 'includes empty class method with one empty line' do
|
131
|
+
expect(subject.empty_one_empty_line).to eq(nil)
|
132
|
+
end
|
110
133
|
|
111
|
-
|
112
|
-
|
113
|
-
|
134
|
+
it 'includes empty class method with comment' do
|
135
|
+
expect(subject.empty_with_comment).to eq(nil)
|
136
|
+
end
|
114
137
|
|
115
|
-
|
116
|
-
|
117
|
-
|
138
|
+
it 'includes empty class method one line definition' do
|
139
|
+
expect(subject.empty_one_line_definition).to eq(nil)
|
140
|
+
end
|
118
141
|
|
119
|
-
|
120
|
-
|
121
|
-
|
142
|
+
it 'includes empty class method one line definition with spaces' do
|
143
|
+
expect(subject.empty_one_line_definition_with_spaces).to eq(nil)
|
144
|
+
end
|
122
145
|
|
123
|
-
|
124
|
-
|
146
|
+
it 'includes instance methods' do
|
147
|
+
instance = subject.new
|
125
148
|
|
126
|
-
|
127
|
-
|
149
|
+
expect(instance.foo).to eq('foo')
|
150
|
+
end
|
128
151
|
|
129
|
-
|
130
|
-
|
131
|
-
end
|
152
|
+
it 'provides class method self as the including base class as in the class method (meh)' do
|
153
|
+
expect(subject.meh).to eq(subject)
|
132
154
|
end
|
155
|
+
end
|
133
156
|
|
134
|
-
|
157
|
+
context "included by a module (Foo) that is included by a second module (Bar) that is included by a class (BarActiveRecord)" do
|
135
158
|
|
136
|
-
|
159
|
+
subject { V1::BarActiveRecord }
|
137
160
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
161
|
+
it 'allows invoking class methods' do
|
162
|
+
expect(subject.validations).to include(['foo', {:presence => true}])
|
163
|
+
expect(subject.validations).to include(['bar', {:presence => true}])
|
164
|
+
end
|
142
165
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
166
|
+
it 'includes class methods declared via "class << self"' do
|
167
|
+
expect(subject.foo).to eq('self.foo')
|
168
|
+
expect(subject.bar).to eq('self.bar')
|
169
|
+
end
|
147
170
|
|
148
|
-
|
149
|
-
|
171
|
+
it 'includes instance methods' do
|
172
|
+
instance = subject.new
|
150
173
|
|
151
|
-
|
152
|
-
|
153
|
-
|
174
|
+
expect(instance.foo).to eq('foo')
|
175
|
+
expect(instance.bar).to eq('bar')
|
176
|
+
end
|
154
177
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
178
|
+
it 'can include a basic module (Forwardable) into singleton class by placing in class << self' do
|
179
|
+
instance = subject.new
|
180
|
+
expect(instance.length).to eq(3)
|
181
|
+
end
|
159
182
|
|
160
|
-
|
161
|
-
|
162
|
-
|
183
|
+
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
|
184
|
+
expect(subject.barrable).to eq(true)
|
185
|
+
end
|
163
186
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
187
|
+
it 'can include a basic module (Comparable)' do
|
188
|
+
now = Time.now
|
189
|
+
allow(Time).to receive(:now).and_return(now)
|
190
|
+
instance = subject.new
|
191
|
+
allow(Time).to receive(:now).and_return(now + 100)
|
192
|
+
instance2 = subject.new
|
170
193
|
|
171
|
-
|
172
|
-
|
194
|
+
expect(instance2 > instance).to eq(true)
|
195
|
+
end
|
173
196
|
|
174
|
-
|
175
|
-
|
176
|
-
end
|
197
|
+
it 'provides class method self as the including base class as in the class method (meh)' do
|
198
|
+
expect(subject.meh).to eq(subject)
|
177
199
|
end
|
200
|
+
end
|
201
|
+
|
202
|
+
context "(with SuperModule.define alternate syntax in Baz) included by a module (Foo), included by another module (Bar), included by a third module (Baz) that is included by a class (BazActiveRecord)" do
|
178
203
|
|
179
|
-
|
204
|
+
subject { V1::BazActiveRecord }
|
180
205
|
|
181
|
-
|
206
|
+
it 'allows invoking class methods' do
|
207
|
+
expect(subject.validations).to include(['foo', {:presence => true}])
|
208
|
+
expect(subject.validations).to include(['bar', {:presence => true}])
|
209
|
+
expect(subject.validations).to include(['baz', {:presence => true}])
|
210
|
+
end
|
182
211
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
212
|
+
it 'includes class methods declared via "class << self"' do
|
213
|
+
expect(subject.foo).to eq('self.foo')
|
214
|
+
expect(subject.bar).to eq('self.bar')
|
215
|
+
expect(subject.baz).to eq('self.baz')
|
216
|
+
end
|
188
217
|
|
189
|
-
|
190
|
-
|
191
|
-
expect(subject.bar).to eq('self.bar')
|
192
|
-
expect(subject.baz).to eq('self.baz')
|
193
|
-
end
|
218
|
+
it 'includes instance methods' do
|
219
|
+
instance = subject.new(100)
|
194
220
|
|
195
|
-
|
196
|
-
|
221
|
+
expect(instance.foo).to eq('foo')
|
222
|
+
expect(instance.bar).to eq('bar')
|
223
|
+
expect(instance.baz).to eq('baz')
|
224
|
+
end
|
197
225
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
end
|
226
|
+
it 'invokes singleton method (make_barrable) from super module' do
|
227
|
+
expect(subject.barrable).to eq(true)
|
228
|
+
end
|
202
229
|
|
203
|
-
|
204
|
-
|
205
|
-
|
230
|
+
it 'can override super module behavior (<=>)' do
|
231
|
+
instance = subject.new(50)
|
232
|
+
instance2 = subject.new(7)
|
206
233
|
|
207
|
-
|
208
|
-
|
209
|
-
instance2 = subject.new(7)
|
234
|
+
expect(instance2 > instance).to eq(false)
|
235
|
+
end
|
210
236
|
|
211
|
-
|
212
|
-
|
237
|
+
it 'provides class method self as the including base class as in the class method (meh)' do
|
238
|
+
expect(subject.meh).to eq(subject)
|
239
|
+
end
|
240
|
+
end
|
213
241
|
|
214
|
-
|
215
|
-
|
216
|
-
|
242
|
+
context 'meta-programming included' do
|
243
|
+
it 'returns summary' do
|
244
|
+
V1::SummarizedActiveRecord.validates 'foo', {:presence => true}
|
245
|
+
V1::SummarizedActiveRecord.validates 'bar', {:presence => true}
|
246
|
+
expect(V1::SummarizedActiveRecord.summary).to eq('foo/{:presence=>true}/bar/{:presence=>true}')
|
247
|
+
expect{V1::SummarizedActiveRecord.fake_summary}.to raise_error
|
248
|
+
end
|
249
|
+
it 'returns fake summary' do
|
250
|
+
V1::FakeSummarizedActiveRecord.validates 'foo', {:presence => true}
|
251
|
+
V1::FakeSummarizedActiveRecord.validates 'bar', {:presence => true}
|
252
|
+
expect(V1::FakeSummarizedActiveRecord.summary).to eq('foo/{:presence=>true}/bar/{:presence=>true}')
|
253
|
+
expect(V1::FakeSummarizedActiveRecord.fake_summary).to eq('This is a fake summary.')
|
217
254
|
end
|
218
255
|
end
|
219
256
|
end
|