super_module 1.2.2 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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
|
[](http://badge.fury.io/rb/super_module)
|
3
|
+
[](https://travis-ci.org/AndyObtiva/super_module)
|
3
4
|
[](https://coveralls.io/r/AndyObtiva/super_module?branch=master)
|
4
5
|
[](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
|