extend_method 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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