overridable 0.3.0 → 0.3.1
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.
- data/README.markdown +40 -26
- data/VERSION +1 -1
- data/lib/overridable.rb +39 -7
- data/test/modulemixin_test.rb +82 -3
- metadata +2 -2
data/README.markdown
CHANGED
@@ -4,36 +4,36 @@ Overridable is a pure ruby library which helps you to make your methods which ar
|
|
4
4
|
|
5
5
|
## Why?
|
6
6
|
|
7
|
-
|
7
|
+
Some people are overusing `alias_method_chain` in their codes like what wycats mentioned in [his post][post]. One of the reasons that people like using `alias_method_chain` is because it's impossible for modules to override methods that are defined in classes when they are included. For example:
|
8
8
|
class Thing
|
9
9
|
def foo
|
10
10
|
'Thing.foo'
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
-
module
|
14
|
+
module Extension
|
15
15
|
def foo
|
16
|
-
'
|
16
|
+
'Extension.foo'
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
Thing.send :include,
|
21
|
-
Thing.new.foo #=> Thing.foo # not
|
20
|
+
Thing.send :include, Extension
|
21
|
+
Thing.new.foo #=> Thing.foo # not Extension.foo
|
22
22
|
|
23
|
-
|
24
|
-
module
|
23
|
+
In order to achieve that goal, some will write the Extension module like this:
|
24
|
+
module Extension
|
25
25
|
def foo_with_redef
|
26
|
-
'
|
26
|
+
'Extension.foo'
|
27
27
|
end
|
28
28
|
# you can also do this by: alias_method_chain :foo, :redef, if you use ActiveSupport.
|
29
29
|
alias foo_without_redef foo
|
30
30
|
alias foo foo_with_redef
|
31
31
|
end
|
32
32
|
|
33
|
-
Thing.send :include,
|
34
|
-
Thing.new.foo #=>
|
33
|
+
Thing.send :include, Extension
|
34
|
+
Thing.new.foo #=> Extension.foo
|
35
35
|
|
36
|
-
|
36
|
+
But according to the [post][post], this is a bad practice. And *overridable* is a gem that provides a neat mean to resolve this problem.
|
37
37
|
|
38
38
|
## How?
|
39
39
|
|
@@ -41,17 +41,17 @@ There are two ways to do this with *overridable*: in class or in module.
|
|
41
41
|
|
42
42
|
### In class
|
43
43
|
|
44
|
-
Remember Thing and
|
44
|
+
Remember Thing and Extension in our first example? Let's make some changes:
|
45
45
|
require 'overridable'
|
46
46
|
|
47
47
|
Thing.class_eval {
|
48
48
|
include Overridable
|
49
49
|
overrides :foo
|
50
50
|
|
51
|
-
include
|
51
|
+
include Extension
|
52
52
|
}
|
53
53
|
|
54
|
-
Thing.new.foo #=>
|
54
|
+
Thing.new.foo #=> Extension.foo
|
55
55
|
That's it! You can specify which methods can be overrided by `overrides` method. One more example based on the previous one:
|
56
56
|
Thing.class_eval {
|
57
57
|
def bar; 'Thing.bar' end
|
@@ -61,26 +61,26 @@ That's it! You can specify which methods can be overrided by `overrides` method.
|
|
61
61
|
overrides :bar, :baz
|
62
62
|
}
|
63
63
|
|
64
|
-
|
65
|
-
def bar; '
|
66
|
-
def baz; '
|
67
|
-
def id; '
|
64
|
+
Extension.module_eval {
|
65
|
+
def bar; 'Extension.bar' end
|
66
|
+
def baz; 'Extension baz' end
|
67
|
+
def id; 'Extension' end
|
68
68
|
}
|
69
69
|
|
70
70
|
thing = Thing.new
|
71
|
-
thing.bar #=> '
|
72
|
-
thing.baz #=> '
|
71
|
+
thing.bar #=> 'Extension.bar'
|
72
|
+
thing.baz #=> 'Extension.baz'
|
73
73
|
thing.id #=> 'Thing'
|
74
|
-
Of course it's not the end of our story ;) How could I call this *override* if we cannot use `super`?
|
75
|
-
|
74
|
+
Of course it's not the end of our story ;) How could I call this *override* if we cannot use `super`? Go on with our example:
|
75
|
+
Extension.module_eval {
|
76
76
|
def bar
|
77
77
|
parent = super
|
78
|
-
me = '
|
78
|
+
me = 'Extension.bar'
|
79
79
|
"I'm #{me} and I overrided #{parent}"
|
80
80
|
end
|
81
81
|
}
|
82
82
|
|
83
|
-
Thing.new.bar => I'm
|
83
|
+
Thing.new.bar => I'm Extension.bar and I overrided Thing.bar
|
84
84
|
|
85
85
|
### In module
|
86
86
|
|
@@ -92,7 +92,7 @@ If you have many methods in your module and find that it's too annoying to use `
|
|
92
92
|
def method_n; ... end
|
93
93
|
end
|
94
94
|
|
95
|
-
module
|
95
|
+
module Extension
|
96
96
|
include Overridable::ModuleMixin
|
97
97
|
|
98
98
|
def method_one; ... end
|
@@ -101,7 +101,19 @@ If you have many methods in your module and find that it's too annoying to use `
|
|
101
101
|
def method_n; ... end
|
102
102
|
end
|
103
103
|
|
104
|
-
Thing.send :include,
|
104
|
+
Thing.send :include, Extension #=> method_one, method_two, ..., method_n are all overrided.
|
105
|
+
|
106
|
+
Since version 0.3.1, you can use `overrides` in your module to specify which method should be overrided and which should not. Let's rewrite the Extension module above:
|
107
|
+
module Extension
|
108
|
+
include Overridable::ModuleMixin
|
109
|
+
overrides :only => [:method_one, :method_two]
|
110
|
+
|
111
|
+
# define methods here...
|
112
|
+
end
|
113
|
+
|
114
|
+
Thing.send :include, Extension #=> only method_one and method_two will be overrided.
|
115
|
+
|
116
|
+
You can also use `overrides :except => [:method_one, :method_two]` to tell the module not to override method_one and method_two in the classes which include it.
|
105
117
|
|
106
118
|
## Install
|
107
119
|
|
@@ -145,3 +157,5 @@ These features **only** will be added when they are asked for.
|
|
145
157
|
## Copyright
|
146
158
|
|
147
159
|
Copyright (c) 2009 梁智敏. See LICENSE for details.
|
160
|
+
|
161
|
+
[post]: http://yehudakatz.com/2009/03/06/alias_method_chain-in-models/
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.3.
|
1
|
+
0.3.1
|
data/lib/overridable.rb
CHANGED
@@ -90,13 +90,45 @@ module Overridable
|
|
90
90
|
def append_features mod #:nodoc:
|
91
91
|
# these must be done in `append_features`, not `included`
|
92
92
|
mod.send :include, Overridable
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
93
|
+
|
94
|
+
methods =
|
95
|
+
if @override_options && !@override_options[:only].empty?
|
96
|
+
@override_options[:only]
|
97
|
+
else
|
98
|
+
all_methods =
|
99
|
+
public_instance_methods +
|
100
|
+
protected_instance_methods +
|
101
|
+
private_instance_methods
|
102
|
+
|
103
|
+
if @override_options && !@override_options[:except].empty?
|
104
|
+
all_methods - @override_options[:except]
|
105
|
+
else
|
106
|
+
all_methods
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
mod.overrides *methods
|
98
111
|
super
|
99
112
|
end
|
113
|
+
|
114
|
+
# Whitelist and blacklist for to-be-overrided methods.
|
115
|
+
# If this method with the same options is called multiple times within the same module,
|
116
|
+
# only the last call will work.
|
117
|
+
# @param [Hash] options the options describe methods are to be or not to be overrided.
|
118
|
+
# @option options [Symbol, Array<Symbol>] :only only methods specified in this option will be overrided.
|
119
|
+
# @option options [Symbol, Array<Symbol>] :except methods specified in this option will not be overrided.
|
120
|
+
# @example
|
121
|
+
# overrides :except => [:foo, :bar]
|
122
|
+
# overrides :except => :baz
|
123
|
+
# overrides :only => [:foo, :bar]
|
124
|
+
def overrides options = {}
|
125
|
+
raise ArgumentError, "Only :only and :except options are accepted." unless
|
126
|
+
options.keys.all? { |k| [:only, :except].include? k }
|
127
|
+
@override_options ||= {:only => [], :except => []}
|
128
|
+
@override_options[:only] = [options[:only]].flatten.compact if options[:only]
|
129
|
+
@override_options[:except] = [options[:except]].flatten.compact if options[:except]
|
130
|
+
@override_options[:only] = @override_options[:only] - @override_options[:except]
|
131
|
+
end
|
100
132
|
end
|
101
133
|
end
|
102
134
|
|
@@ -137,9 +169,9 @@ module Overridable
|
|
137
169
|
end
|
138
170
|
}
|
139
171
|
$VERBOSE = old_verbose
|
140
|
-
|
141
|
-
nil
|
142
172
|
end
|
173
|
+
|
174
|
+
nil
|
143
175
|
end
|
144
176
|
end
|
145
177
|
|
data/test/modulemixin_test.rb
CHANGED
@@ -9,9 +9,15 @@ context "A module which includes Overridable::ModuleMixin has some methods defin
|
|
9
9
|
'SomeModule.foo'
|
10
10
|
end
|
11
11
|
|
12
|
+
protected
|
12
13
|
def bar a
|
13
14
|
super + 'SomeModule.bar'
|
14
15
|
end
|
16
|
+
|
17
|
+
private
|
18
|
+
def baz
|
19
|
+
super + 'SomeModule.baz'
|
20
|
+
end
|
15
21
|
end
|
16
22
|
|
17
23
|
SomeModule
|
@@ -24,10 +30,13 @@ context "A module which includes Overridable::ModuleMixin has some methods defin
|
|
24
30
|
'SomeClass.foo'
|
25
31
|
end
|
26
32
|
|
27
|
-
private
|
28
33
|
def bar a
|
29
34
|
'SomeClass.bar'
|
30
35
|
end
|
36
|
+
|
37
|
+
def baz
|
38
|
+
'SomeClass.baz'
|
39
|
+
end
|
31
40
|
end
|
32
41
|
|
33
42
|
SomeClass
|
@@ -39,10 +48,80 @@ context "A module which includes Overridable::ModuleMixin has some methods defin
|
|
39
48
|
asserts("foo should be overrided.") {
|
40
49
|
topic.new.foo
|
41
50
|
}.equals('SomeModule.foo')
|
51
|
+
|
52
|
+
asserts("bar should be overrided.") {
|
53
|
+
topic.new.send :bar, :whatever
|
54
|
+
}.equals('SomeClass.bar' + 'SomeModule.bar')
|
55
|
+
|
56
|
+
asserts("baz should be overrided.") {
|
57
|
+
topic.new.send :baz
|
58
|
+
}.equals('SomeClass.baz' + 'SomeModule.baz')
|
59
|
+
end
|
60
|
+
|
61
|
+
context "We specify which methods can be overrided." do
|
62
|
+
setup {
|
63
|
+
SomeModule.module_eval {
|
64
|
+
overrides :only => [:foo, :bar]
|
65
|
+
}
|
66
|
+
topic.send :include, SomeModule
|
67
|
+
topic
|
68
|
+
}
|
69
|
+
|
70
|
+
asserts("foo should be overrided.") {
|
71
|
+
topic.new.foo
|
72
|
+
}.equals('SomeModule.foo')
|
73
|
+
|
74
|
+
asserts("bar should be overrided.") {
|
75
|
+
topic.new.send :bar, :whatever
|
76
|
+
}.equals('SomeClass.bar' + 'SomeModule.bar')
|
77
|
+
|
78
|
+
asserts("baz should not be overrided.") {
|
79
|
+
topic.new.baz
|
80
|
+
}.equals('SomeClass.baz')
|
81
|
+
end
|
82
|
+
|
83
|
+
context "We specify which methods cannot be overrided." do
|
84
|
+
setup {
|
85
|
+
SomeModule.module_eval {
|
86
|
+
overrides :except => :baz
|
87
|
+
}
|
88
|
+
topic.send :include, SomeModule
|
89
|
+
topic
|
90
|
+
}
|
91
|
+
|
92
|
+
asserts("foo should be overrided.") {
|
93
|
+
topic.new.foo
|
94
|
+
}.equals('SomeModule.foo')
|
95
|
+
|
96
|
+
asserts("bar should be overrided.") {
|
97
|
+
topic.new.send :bar, :whatever
|
98
|
+
}.equals('SomeClass.bar' + 'SomeModule.bar')
|
99
|
+
|
100
|
+
asserts("baz should not be overrided.") {
|
101
|
+
topic.new.baz
|
102
|
+
}.equals('SomeClass.baz')
|
103
|
+
end
|
104
|
+
|
105
|
+
context "We specify both the whitelist and blacklist of to-be-overrided methods." do
|
106
|
+
setup {
|
107
|
+
SomeModule.module_eval {
|
108
|
+
overrides :only => [:foo, :bar, :baz], :except => :baz
|
109
|
+
}
|
110
|
+
topic.send :include, SomeModule
|
111
|
+
topic
|
112
|
+
}
|
113
|
+
|
114
|
+
asserts("foo should be overrided.") {
|
115
|
+
topic.new.foo
|
116
|
+
}.equals('SomeModule.foo')
|
117
|
+
|
42
118
|
asserts("bar should be overrided.") {
|
43
|
-
|
44
|
-
topic.new.bar :whatever
|
119
|
+
topic.new.send :bar, :whatever
|
45
120
|
}.equals('SomeClass.bar' + 'SomeModule.bar')
|
121
|
+
|
122
|
+
asserts("baz should not be overrided.") {
|
123
|
+
topic.new.baz
|
124
|
+
}.equals('SomeClass.baz')
|
46
125
|
end
|
47
126
|
end
|
48
127
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: overridable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- "\xE6\xA2\x81\xE6\x99\xBA\xE6\x95\x8F(Gimi Liang)"
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-11-
|
12
|
+
date: 2009-11-25 00:00:00 +08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|