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 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
@@ -0,0 +1,4 @@
1
+ /.idea
2
+ /Gemfile.lock
3
+ .bundle
4
+ /coverage
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.9.3"
4
+ - "2.0.0"
5
+ - "2.1.0"
6
+ before_install:
7
+ - gem update --system
8
+ - gem --version
9
+ script: bundle exec rake test
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
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
+ [![Code Climate](https://codeclimate.com/github/ebollens/ruby-extend_method.png)](https://codeclimate.com/github/ebollens/ruby-extend_method) [![Build Status](https://travis-ci.org/ebollens/ruby-extend_method.png)](https://travis-ci.org/ebollens/ruby-extend_method) [![Coverage Status](https://coveralls.io/repos/ebollens/ruby-extend_method/badge.png)](https://coveralls.io/r/ebollens/ruby-extend_method) [![Dependency Status](https://gemnasium.com/ebollens/ruby-extend_method.png)](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,6 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.test_files = FileList['test/init.rb', 'test/**/*.rb']
6
+ end
@@ -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
@@ -0,0 +1,7 @@
1
+ require 'simplecov'
2
+ require 'coveralls'
3
+
4
+ SimpleCov.formatter = Coveralls::SimpleCov::Formatter
5
+ SimpleCov.start do
6
+ add_filter '/test/'
7
+ end
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