overider 0.0.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/.gitignore +4 -0
- data/Gemfile +4 -0
- data/README.mkd +50 -0
- data/Rakefile +1 -0
- data/lib/overider.rb +28 -0
- data/lib/overider/version.rb +3 -0
- data/overider.gemspec +45 -0
- data/spec/overider_spec.rb +156 -0
- metadata +68 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.mkd
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# Overider
|
2
|
+
|
3
|
+
## Background
|
4
|
+
Despite Ruby's clean design and flexibility, the language has no native feature
|
5
|
+
that allows one to over-ride a method without irrevocably losing the original binding
|
6
|
+
of the method name.
|
7
|
+
`alias` is a well-worn idiom that circumvents this limitation,
|
8
|
+
but blogger Jay Fields wrote an [interesting post](http://blog.jayfields.com/2006/12/ruby-alias-method-alternative.html)
|
9
|
+
that reminds us that `alias` comes with some less-than-desireable side-effects
|
10
|
+
that may not be significant in small scripts and projects
|
11
|
+
but may become a problem for larger projects.
|
12
|
+
|
13
|
+
In the [same post](http://blog.jayfields.com/2006/12/ruby-alias-method-alternative.html),
|
14
|
+
Jay offered an interesting alternative to `alias`,
|
15
|
+
using instead `Module#instance_method` and `Module#define_method`.
|
16
|
+
This is much better,
|
17
|
+
but I'm not a Ruby ninja yet,
|
18
|
+
and I would rather not have to remember the Baroque syntax in `x = self.instance_method(:on!)`
|
19
|
+
and `x.bind(self).call` every time I want to do this,
|
20
|
+
let alone what they mean when I look at my code several weeks later.
|
21
|
+
|
22
|
+
So I thought about how I could take Jay's example one step further
|
23
|
+
and after some trial and lots of error came up with this.
|
24
|
+
|
25
|
+
## Description
|
26
|
+
A mix-in module that allows for super-clean method over-riding without resorting to `alias`
|
27
|
+
or making unbound methods visible.
|
28
|
+
|
29
|
+
## Synopsis
|
30
|
+
class A
|
31
|
+
def hello
|
32
|
+
"hello"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Later, I want to overide class A methods
|
37
|
+
|
38
|
+
class A
|
39
|
+
extend Overider
|
40
|
+
|
41
|
+
overide (:hello) do |*a|
|
42
|
+
overiden(*a) + " overide"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
puts A.new.hello # ==> "hello overide"
|
47
|
+
|
48
|
+
## See also
|
49
|
+
* [Ruby: Alias method alternative, Jay Fields](http://blog.jayfields.com/2006/12/ruby-alias-method-alternative.html)
|
50
|
+
* <https://github.com/soveran/override>
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/overider.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require "overider/version"
|
2
|
+
|
3
|
+
module Overider
|
4
|
+
def overide(sym, &pr)
|
5
|
+
orig_unbound_method = self.instance_method(sym)
|
6
|
+
|
7
|
+
define_method(sym) do |*a, &blk|
|
8
|
+
orig_self = self
|
9
|
+
obj = Object.new
|
10
|
+
|
11
|
+
obj.define_singleton_method(:overiden) do |*a, &blk|
|
12
|
+
orig_bound_method = orig_unbound_method.bind(orig_self)
|
13
|
+
|
14
|
+
if !!blk then
|
15
|
+
orig_bound_method.call *a, &blk
|
16
|
+
else
|
17
|
+
orig_bound_method.call *a
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
if !!blk then
|
22
|
+
obj.instance_exec(*a, blk, &pr)
|
23
|
+
else
|
24
|
+
obj.instance_exec(*a, &pr)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/overider.gemspec
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "overider/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "overider"
|
7
|
+
s.version = Overider::VERSION
|
8
|
+
s.authors = ["Larry Siden, Westside Consulting LLC"]
|
9
|
+
s.email = ["lsiden@westside-consulting.com"]
|
10
|
+
s.homepage = "https://github.com/lsiden/overider"
|
11
|
+
s.summary = %q{
|
12
|
+
A mix-in module that allows for super-clean method over-riding without resorting to =alias=
|
13
|
+
or making unbound methods visible.
|
14
|
+
}
|
15
|
+
s.description = %q{
|
16
|
+
class A
|
17
|
+
def hello
|
18
|
+
"hello"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Later, I want to overide class A methods
|
23
|
+
|
24
|
+
class A
|
25
|
+
extend Overider
|
26
|
+
|
27
|
+
overide (:hello) do |*a|
|
28
|
+
overiden(*a) + " overide"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
A.new.hello # ==> "hello overide"
|
33
|
+
}
|
34
|
+
|
35
|
+
s.rubyforge_project = "overider"
|
36
|
+
|
37
|
+
s.files = `git ls-files`.split("\n")
|
38
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
39
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
40
|
+
s.require_paths = ["lib"]
|
41
|
+
|
42
|
+
# specify any dependencies here; for example:
|
43
|
+
s.add_development_dependency "rspec"
|
44
|
+
# s.add_runtime_dependency "rest-client"
|
45
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'overider'
|
3
|
+
|
4
|
+
describe Overider do
|
5
|
+
it 'over-rides method names while allowing access to original method with name "overiden"' do
|
6
|
+
|
7
|
+
class A
|
8
|
+
def hello
|
9
|
+
"hello"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Later, I want to overide class A methods
|
14
|
+
|
15
|
+
class A
|
16
|
+
extend Overider
|
17
|
+
|
18
|
+
overide (:hello) do |*a|
|
19
|
+
overiden(*a) + " overide"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
a = A.new
|
24
|
+
a.hello.should == "hello overide"
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'the name "overiden" is not overiden!' do
|
28
|
+
|
29
|
+
class A
|
30
|
+
def hello
|
31
|
+
"hello"
|
32
|
+
end
|
33
|
+
|
34
|
+
def goodby
|
35
|
+
"goodby"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Later, I want to overide class A methods
|
40
|
+
|
41
|
+
class A
|
42
|
+
extend Overider
|
43
|
+
|
44
|
+
overide (:hello) do |*a|
|
45
|
+
overiden(*a) + " overide"
|
46
|
+
end
|
47
|
+
|
48
|
+
overide (:goodby) do |*a|
|
49
|
+
overiden(*a) + " overide"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
a = A.new
|
54
|
+
a.hello.should == "hello overide"
|
55
|
+
a.goodby.should == "goodby overide"
|
56
|
+
a.hello.should == "hello overide" # "overiden" overiden!
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'works with inheritance' do
|
60
|
+
|
61
|
+
class B < A
|
62
|
+
extend Overider
|
63
|
+
|
64
|
+
overide :hello do |*a|
|
65
|
+
overiden(*a) + " B then A"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
B.new.hello.should == "hello overide B then A"
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'works with modules' do
|
73
|
+
module HelloModule
|
74
|
+
def hello
|
75
|
+
"hello"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class C
|
80
|
+
include HelloModule
|
81
|
+
extend Overider
|
82
|
+
|
83
|
+
overide :hello do |*a|
|
84
|
+
overiden(*a) + " C"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
C.new.hello.should == "hello C"
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'works with args' do
|
92
|
+
module HelloWithArgs
|
93
|
+
def hello(a, b, c)
|
94
|
+
"hello #{a}, #{b}, #{c}"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class D
|
99
|
+
include HelloWithArgs
|
100
|
+
extend Overider
|
101
|
+
|
102
|
+
overide :hello do |a, b, c|
|
103
|
+
overiden(a, b, c) + " D"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
result = D.new.hello 1, 2, 3
|
108
|
+
result.should == "hello 1, 2, 3 D"
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'plays nice with blocks' do
|
112
|
+
module HelloModule
|
113
|
+
def hello(&blk)
|
114
|
+
"hello " + blk.call
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class E
|
119
|
+
include HelloModule
|
120
|
+
extend Overider
|
121
|
+
|
122
|
+
# In the overide block, we have to declare blk as a normal block arg, not &blk.
|
123
|
+
# It must be a Proc or lambda
|
124
|
+
overide :hello do |blk|
|
125
|
+
overiden { blk.call } + " in E"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
result = E.new.hello do
|
130
|
+
"block"
|
131
|
+
end
|
132
|
+
result.should == "hello block in E"
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'plays nice with lambda' do
|
136
|
+
module HelloModule
|
137
|
+
def hello(&blk)
|
138
|
+
"hello " + blk.call
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
class F
|
143
|
+
include HelloModule
|
144
|
+
extend Overider
|
145
|
+
|
146
|
+
# In the overide block, we have to declare blk as a normal block arg, not &blk.
|
147
|
+
# It must be a Proc or lambda
|
148
|
+
overide :hello do |blk|
|
149
|
+
overiden { blk.call } + " in F"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
result = F.new.hello ->{ "block" }
|
154
|
+
result.should == "hello block in F"
|
155
|
+
end
|
156
|
+
end
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: overider
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Larry Siden, Westside Consulting LLC
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-12-23 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: &76593190 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *76593190
|
25
|
+
description: ! "\n class A\n def hello\n \"hello\"\n end\n end\n\n
|
26
|
+
\ # Later, I want to overide class A methods\n\n class A\n extend Overider\n\n
|
27
|
+
\ overide (:hello) do |*a|\n overiden(*a) + \" overide\"\n end\n
|
28
|
+
\ end\n\n A.new.hello # ==> \"hello overide\"\n "
|
29
|
+
email:
|
30
|
+
- lsiden@westside-consulting.com
|
31
|
+
executables: []
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- .gitignore
|
36
|
+
- Gemfile
|
37
|
+
- README.mkd
|
38
|
+
- Rakefile
|
39
|
+
- lib/overider.rb
|
40
|
+
- lib/overider/version.rb
|
41
|
+
- overider.gemspec
|
42
|
+
- spec/overider_spec.rb
|
43
|
+
homepage: https://github.com/lsiden/overider
|
44
|
+
licenses: []
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options: []
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ! '>='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
requirements: []
|
62
|
+
rubyforge_project: overider
|
63
|
+
rubygems_version: 1.8.10
|
64
|
+
signing_key:
|
65
|
+
specification_version: 3
|
66
|
+
summary: A mix-in module that allows for super-clean method over-riding without resorting
|
67
|
+
to =alias= or making unbound methods visible.
|
68
|
+
test_files: []
|