extend_method 1.0.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 +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +4 -0
- data/.travis.yml +9 -0
- data/Gemfile +3 -0
- data/LICENSE +27 -0
- data/README.md +129 -0
- data/Rakefile +6 -0
- data/extend_method.gemspec +19 -0
- data/lib/extend_method.rb +140 -0
- data/test/extend_method.rb +98 -0
- data/test/init.rb +7 -0
- metadata +85 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 372070b56fd8127c90f489f2f0720fe675d36a6c
|
4
|
+
data.tar.gz: 1dd64e082d8b67b2720eac751f377d480fbf137d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b90664ac5e96dc8c532c2da30488a37725ef88ad56e6bf160ea55eee9080f8e0d1c2011b9b2b55eba3cc84326a23d498ddbdd68bd93fd1b2bf10ee1f8e035f8b
|
7
|
+
data.tar.gz: ceec20dd2ce2743334999499cf507242d2abcff721da06c32b512ce66f80ffb6a20f53b94a8d1b7c89f3323f321ed19b5d4a37839739d8d873d823f4d0234855
|
data/.coveralls.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
service_name: travis-ci
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Copyright (c) 2014, Eric Bollens.
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without modification,
|
5
|
+
are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
* Redistributions of source code must retain the above copyright notice,
|
8
|
+
this list of conditions and the following disclaimer.
|
9
|
+
|
10
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
this list of conditions and the following disclaimer in the documentation
|
12
|
+
and/or other materials provided with the distribution.
|
13
|
+
|
14
|
+
* Neither the name of the University of California nor the names of its
|
15
|
+
contributors may be used to endorse or promote products derived from this
|
16
|
+
software without specific prior written permission.
|
17
|
+
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
19
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
20
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
21
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
22
|
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
23
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
24
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
25
|
+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
26
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
27
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
# extend_method
|
2
|
+
|
3
|
+
Dry and simple gem for method overriding in non-inheritance contexts.
|
4
|
+
|
5
|
+
## Status
|
6
|
+
|
7
|
+
This implementation is a **release candidate**.
|
8
|
+
|
9
|
+
[](https://codeclimate.com/github/ebollens/ruby-extend_method) [](https://travis-ci.org/ebollens/ruby-extend_method) [](https://coveralls.io/r/ebollens/ruby-extend_method) [](https://gemnasium.com/ebollens/ruby-extend_method)
|
10
|
+
|
11
|
+
Please report issues in the [issue tracker](https://github.com/ebollens/ruby-extend_method/issues). [Pull requests](https://github.com/ebollens/ruby-extend_method/pulls) are welcome.
|
12
|
+
|
13
|
+
## License
|
14
|
+
|
15
|
+
The extend_method library is **open-source software** licensed under the BSD 3-clause license. The full text of the license may be found in the [LICENSE](https://github.com/ebollens/ruby-extend_method/blob/master/LICENSE) file.
|
16
|
+
|
17
|
+
## Credits
|
18
|
+
|
19
|
+
This library is written and maintained by Eric Bollens.
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
Require the gem:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
require 'extend_method'
|
27
|
+
```
|
28
|
+
|
29
|
+
The `extend_method` class method can then be included as:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
class Example
|
33
|
+
|
34
|
+
class << self
|
35
|
+
include ExtendMethod
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
```
|
40
|
+
|
41
|
+
A motivating example showing some of the major features:
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
module Base
|
45
|
+
|
46
|
+
def set val
|
47
|
+
@val = val
|
48
|
+
end
|
49
|
+
|
50
|
+
def get
|
51
|
+
@val
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
class Extended
|
57
|
+
|
58
|
+
include Base
|
59
|
+
|
60
|
+
class << self
|
61
|
+
include ExtendMethod
|
62
|
+
end
|
63
|
+
|
64
|
+
extend_method :set do |val|
|
65
|
+
parent_method "#{val}!"
|
66
|
+
end
|
67
|
+
|
68
|
+
extend_method :get do
|
69
|
+
"foo#{parent_method}"
|
70
|
+
end
|
71
|
+
|
72
|
+
extend_method :other do
|
73
|
+
if has_parent_method?
|
74
|
+
parent_method
|
75
|
+
else
|
76
|
+
# do something else
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
example = new Extended
|
83
|
+
example.set 'bar'
|
84
|
+
assert 'foobar!' == example.get
|
85
|
+
```
|
86
|
+
|
87
|
+
The `extend_method` class method takes a symbol for the method name and a block that extends the method, exposing the previous version of the method as `parent_method`:
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
class Example
|
91
|
+
|
92
|
+
def basic_example
|
93
|
+
# ..
|
94
|
+
end
|
95
|
+
|
96
|
+
extend_method :basic_example do
|
97
|
+
# ..
|
98
|
+
val = parent_method
|
99
|
+
# ..
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
```
|
104
|
+
|
105
|
+
To determine if a parent method exists, use the `has_parent_method?` method:
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
class Example
|
109
|
+
|
110
|
+
extend_method :optional_parent_method_example do |*args|
|
111
|
+
# ..
|
112
|
+
parent_method(*args) if has_parent_method?
|
113
|
+
# ..
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
```
|
118
|
+
|
119
|
+
The block accepts arguments for what's passed to the method:
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
class Example
|
123
|
+
|
124
|
+
extend_method :argument_example do |val|
|
125
|
+
"foo#{parent_method val}"
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
|
3
|
+
s.name = 'extend_method'
|
4
|
+
s.version = '1.0.0'
|
5
|
+
s.summary = 'Simple mechanism for extending an existing method'
|
6
|
+
s.authors = ['Eric Bollens']
|
7
|
+
s.email = ['ebollens@ucla.edu']
|
8
|
+
s.homepage = 'https://github.com/ebollens/ruby-extend_method'
|
9
|
+
s.license = 'BSD'
|
10
|
+
|
11
|
+
s.files = `git ls-files`.split($/)
|
12
|
+
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
13
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
14
|
+
s.require_paths = ['lib']
|
15
|
+
|
16
|
+
s.add_development_dependency 'rake'
|
17
|
+
s.add_development_dependency 'coveralls'
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
module ExtendMethod
|
2
|
+
|
3
|
+
def extend_method method_name, &block
|
4
|
+
|
5
|
+
ExtendMethod::Strategy.new(self, method_name, block).execute!
|
6
|
+
|
7
|
+
end
|
8
|
+
|
9
|
+
class Strategy
|
10
|
+
|
11
|
+
attr_reader :method_name
|
12
|
+
attr_reader :previous_method_name
|
13
|
+
attr_reader :has_previous_method_name
|
14
|
+
attr_reader :previous_method
|
15
|
+
attr_reader :block
|
16
|
+
|
17
|
+
def initialize klass, method_name, block
|
18
|
+
|
19
|
+
@class = klass
|
20
|
+
@method_name = method_name
|
21
|
+
@previous_method_name = :parent_method
|
22
|
+
@has_previous_method_name = :has_parent_method?
|
23
|
+
@block = block
|
24
|
+
|
25
|
+
begin
|
26
|
+
@previous_method = @class.instance_method(@method_name)
|
27
|
+
rescue NameError => e
|
28
|
+
@previous_method = e
|
29
|
+
end
|
30
|
+
|
31
|
+
@instance = nil
|
32
|
+
@previous_method_original = nil
|
33
|
+
@has_previous_method_original = nil
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
def bind! instance
|
38
|
+
|
39
|
+
@instance = instance
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
def bound?
|
44
|
+
|
45
|
+
!@instance.nil?
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
def unbind!
|
50
|
+
|
51
|
+
@instance = nil
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
def save_state!
|
56
|
+
|
57
|
+
raise 'Instance state already saved' if saved_state?
|
58
|
+
|
59
|
+
if @instance.class.method_defined?(previous_method_name)
|
60
|
+
@previous_method_original = @instance.class.send(:instance_method, previous_method_name)
|
61
|
+
else
|
62
|
+
@previous_method_original = false
|
63
|
+
end
|
64
|
+
|
65
|
+
if @instance.class.method_defined?(has_previous_method_name)
|
66
|
+
@has_previous_method_original = @instance.class.send(:instance_method, has_previous_method_name)
|
67
|
+
else
|
68
|
+
@has_previous_method_original = false
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
def saved_state?
|
74
|
+
|
75
|
+
raise 'No bound instance' unless bound?
|
76
|
+
|
77
|
+
!@previous_method_original.nil?
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
def restore_state!
|
82
|
+
|
83
|
+
raise 'Instance state not saved' unless saved_state?
|
84
|
+
|
85
|
+
@instance.class.send(:remove_method, previous_method_name)
|
86
|
+
@instance.class.send(:define_method, previous_method_name, @previous_method_original) if @previous_method_original
|
87
|
+
@previous_method_original = nil
|
88
|
+
|
89
|
+
@instance.class.send(:remove_method, has_previous_method_name)
|
90
|
+
@instance.class.send(:define_method, has_previous_method_name, @has_previous_method_original) if @has_previous_method_original
|
91
|
+
@has_previous_method_original = nil
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
def run! *args
|
96
|
+
|
97
|
+
@instance.instance_exec *args, &block
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
def prepare!
|
102
|
+
|
103
|
+
method_name = @method_name
|
104
|
+
previous_method = @previous_method
|
105
|
+
|
106
|
+
@instance.class.send(:define_method, previous_method_name) do |*args|
|
107
|
+
unless previous_method.is_a? NameError
|
108
|
+
previous_method.bind(self).call(*args)
|
109
|
+
else
|
110
|
+
raise NameError.new "undefined method `previous' in `extend_method' because `#{method_name}' is not previously defined for class `#{self.class.name}'"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
@instance.class.send(:define_method, has_previous_method_name) do |*args|
|
115
|
+
!previous_method.is_a?(NameError)
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
def execute!
|
121
|
+
|
122
|
+
strategy = self
|
123
|
+
|
124
|
+
@class.send(:define_method, method_name) do |*args|
|
125
|
+
|
126
|
+
strategy.bind! self
|
127
|
+
strategy.save_state!
|
128
|
+
strategy.prepare!
|
129
|
+
return_value = strategy.run! *args
|
130
|
+
strategy.restore_state!
|
131
|
+
strategy.unbind!
|
132
|
+
return_value
|
133
|
+
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'extend_method'
|
3
|
+
|
4
|
+
class TestExtendMethod < Test::Unit::TestCase
|
5
|
+
|
6
|
+
class Base
|
7
|
+
attr_reader :val
|
8
|
+
attr_accessor :attr
|
9
|
+
def initialize val = nil
|
10
|
+
@val = val
|
11
|
+
@attr = nil
|
12
|
+
end
|
13
|
+
def set_val val
|
14
|
+
@val = val
|
15
|
+
end
|
16
|
+
def get_val
|
17
|
+
@val
|
18
|
+
end
|
19
|
+
def parent_method
|
20
|
+
"bar"
|
21
|
+
end
|
22
|
+
def with_parent_method
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Extended < Base
|
28
|
+
class << self
|
29
|
+
include ExtendMethod
|
30
|
+
end
|
31
|
+
extend_method :initialize do |value = nil|
|
32
|
+
parent_method value ? "initialize:#{value}" : nil
|
33
|
+
end
|
34
|
+
extend_method :set_val do |value|
|
35
|
+
parent_method "set:#{value}"
|
36
|
+
end
|
37
|
+
extend_method :attr= do |val|
|
38
|
+
parent_method "attr:#{val}"
|
39
|
+
end
|
40
|
+
extend_method :get_val do
|
41
|
+
"get:#{parent_method}"
|
42
|
+
end
|
43
|
+
extend_method :with_parent_method do
|
44
|
+
has_parent_method?
|
45
|
+
end
|
46
|
+
extend_method :without_parent_method do
|
47
|
+
has_parent_method?
|
48
|
+
end
|
49
|
+
extend_method :parent_method do
|
50
|
+
"foo#{parent_method}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_extend_method
|
55
|
+
val = 'test'
|
56
|
+
base = Base.new
|
57
|
+
assert base.val == nil
|
58
|
+
base.set_val val
|
59
|
+
assert base.val == val
|
60
|
+
extended = Extended.new
|
61
|
+
assert extended.val == nil
|
62
|
+
extended.set_val val
|
63
|
+
assert extended.val == "set:#{val}"
|
64
|
+
assert extended.get_val == "get:set:#{val}"
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_extend_method_initialize
|
68
|
+
val = 'test'
|
69
|
+
base = Base.new val
|
70
|
+
assert base.val == val
|
71
|
+
extended = Extended.new val
|
72
|
+
assert extended.val == "initialize:#{val}"
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_extend_method_attr_accessor
|
76
|
+
val = 'test'
|
77
|
+
base = Base.new
|
78
|
+
assert base.attr == nil
|
79
|
+
base.attr = val
|
80
|
+
assert base.attr == val
|
81
|
+
extended = Extended.new
|
82
|
+
assert extended.attr == nil
|
83
|
+
extended.attr = val
|
84
|
+
assert extended.attr == "attr:#{val}"
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_extend_method_has_parent_method?
|
88
|
+
extended = Extended.new
|
89
|
+
assert extended.with_parent_method
|
90
|
+
assert !extended.without_parent_method
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_extend_method_parent_method
|
94
|
+
extended = Extended.new
|
95
|
+
assert 'foobar' == extended.parent_method
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
data/test/init.rb
ADDED
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: extend_method
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Eric Bollens
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-02-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: coveralls
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description:
|
42
|
+
email:
|
43
|
+
- ebollens@ucla.edu
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- .coveralls.yml
|
49
|
+
- .gitignore
|
50
|
+
- .travis.yml
|
51
|
+
- Gemfile
|
52
|
+
- LICENSE
|
53
|
+
- README.md
|
54
|
+
- Rakefile
|
55
|
+
- extend_method.gemspec
|
56
|
+
- lib/extend_method.rb
|
57
|
+
- test/extend_method.rb
|
58
|
+
- test/init.rb
|
59
|
+
homepage: https://github.com/ebollens/ruby-extend_method
|
60
|
+
licenses:
|
61
|
+
- BSD
|
62
|
+
metadata: {}
|
63
|
+
post_install_message:
|
64
|
+
rdoc_options: []
|
65
|
+
require_paths:
|
66
|
+
- lib
|
67
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - '>='
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
requirements: []
|
78
|
+
rubyforge_project:
|
79
|
+
rubygems_version: 2.0.14
|
80
|
+
signing_key:
|
81
|
+
specification_version: 4
|
82
|
+
summary: Simple mechanism for extending an existing method
|
83
|
+
test_files:
|
84
|
+
- test/extend_method.rb
|
85
|
+
- test/init.rb
|