durable_decorator 0.0.4

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.
@@ -0,0 +1,25 @@
1
+ *.gem
2
+ *.rbc
3
+ *.swp
4
+ .bundle
5
+ .config
6
+ coverage
7
+ InstalledFiles
8
+ .yardoc
9
+ Gemfile.lock
10
+ InstalledFiles
11
+ _yardoc
12
+ coverage
13
+ doc/
14
+ lib/bundler/man
15
+ pkg
16
+ rdoc
17
+ spec/reports
18
+ test/tmp
19
+ test/version_tmp
20
+ tmp
21
+
22
+ # YARD artifacts
23
+ .yardoc
24
+ _yardoc
25
+ doc/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format d
2
+ --color
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ rvm:
3
+ - jruby-18mode # JRuby in 1.8 mode
4
+ - 2.0.0
5
+ - 1.9.3
6
+ - rbx-18mode
7
+ - 1.9.2
8
+ - jruby-19mode # JRuby in 1.9 mode
9
+ - rbx-19mode
10
+ - 1.8.7
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'rspec'
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Denis Ivanov
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,91 @@
1
+ # DurableDecorator
2
+
3
+ ![Quick Summary](http://cdn.memegenerator.net/instances/300x300/38628144.jpg)
4
+
5
+ [![Build Status](https://travis-ci.org/jumph4x/durable_decorator.png)](https://travis-ci.org/jumph4x/durable_decorator)
6
+ [![Code Climate](https://codeclimate.com/github/jumph4x/durable_decorator.png)](https://codeclimate.com/github/jumph4x/durable_decorator)
7
+
8
+ This is a project for modifying the behavior of gems outside of your reach. You may be using a large Rails Engine and be wanting to simple decorate some existing behavior, but at the same time you want to inherit original behavior.
9
+
10
+ ## On tracking new decorators and managing fragility
11
+
12
+ After a lovely and short discussion with [Brian Quinn](https://github.com/BDQ) regarding these ideas, he mentioned we could try hashing methods to be able to raise warnings upon unexpected sources or targets (see his work on [Deface](https://github.com/spree/deface)). This project relies on another lovely meta-programming creation by [John Mair](https://github.com/banister), specifically his work on [method_source](https://github.com/banister/method_source).
13
+
14
+ Some additional background: http://stackoverflow.com/questions/4470108/when-monkey-patching-a-method-can-you-call-the-overridden-method-from-the-new-i
15
+
16
+ ## Installation
17
+
18
+ Add this line to your application's Gemfile:
19
+
20
+ gem 'durable_decorator', :github => 'jumph4x/durable_decorator'
21
+
22
+ And then execute:
23
+
24
+ $ bundle
25
+
26
+ ## Usage
27
+
28
+ ```ruby
29
+ class ExampleClass
30
+ def string_method
31
+ "original"
32
+ end
33
+ end
34
+
35
+ class ExampleClass
36
+ decorate :string_method do
37
+ string_method_old + " and new"
38
+ end
39
+ end
40
+
41
+ instance = ExampleClass.new
42
+ instance.string_method
43
+ # => "original and new"
44
+ ```
45
+
46
+ ### Working with SHAs
47
+
48
+ Furthermore, we can hash the contents of a method as it exists at inspect-time and seal it by providing extra options to the decorator. If the method definition gets tampered with, the decorator will detect this at decoration-time and raise an error for your review.
49
+
50
+ Find the SHA of the method as currently loaded into memory, works with classes as well as modules:
51
+ ```ruby
52
+ DurableDecorator::Base.determine_sha('ExampleClass#instance_method')
53
+ ```
54
+
55
+ Or for class (singleton) methods:
56
+ ```ruby
57
+ DurableDecorator::Base.determine_sha('ExampleClass.class_level_method')
58
+ ```
59
+
60
+ Armed with this knowledge, we can enforce a strict mode:
61
+ ```ruby
62
+ DurableDecorator::Base.determine_sha('ExampleClass#no_param_method')
63
+ # => 'ba3114b2d46caa684b3f7ba38d6f74b2'
64
+ ExampleClass.class_eval do
65
+ meta = {
66
+ mode: 'strict',
67
+ sha: 'WE-IGNORE-THE-ABOVE'
68
+ }
69
+
70
+ decorate :string_method, meta do
71
+ string_method_old + " and new"
72
+ end
73
+ end
74
+
75
+ DurableDecorator::TamperedDefinitionError: Method SHA mismatch, the definition has been tampered with
76
+ ```
77
+
78
+ DurableDecorator also maintains explicit versions of each method overriden by creating aliases with appended SHAs of the form ```some_method_1234abcd``` so you can always target explicit method versions without relying on ```some_method_old```.
79
+
80
+ ### No more suprise monkey patching
81
+ Once you decorate the method and seal it with its SHA, if some gem tries to come in and overwrite your work **BEFORE** decorate-time, DurableDecorator will warn you. Similarly, expect to see an exception bubble up if the definition of the original method has changed and requires a review and a re-hash.
82
+
83
+ The usefulness is for gem consumers, and their application-level specs.
84
+
85
+ ## Contributing
86
+
87
+ 1. Fork it
88
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
89
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
90
+ 4. Push to the branch (`git push origin my-new-feature`)
91
+ 5. Create new Pull Request
@@ -0,0 +1,5 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+ task :default => :spec
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'durable_decorator/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "durable_decorator"
8
+ spec.version = DurableDecorator::VERSION
9
+ spec.authors = ["Denis Ivanov"]
10
+ spec.email = ["visible.h4x@gmail.com"]
11
+ spec.description = "Allows method redefinitions while maintaining *super*"
12
+ spec.summary = "Allows method redefinitions while maintaining *super*"
13
+ spec.homepage = "https://github.com/jumph4x/durable-decorator"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "method_source"
22
+ spec.add_dependency "logging"
23
+ spec.add_dependency "activesupport"
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.3"
26
+ spec.add_development_dependency "rake"
27
+ spec.add_development_dependency "rspec"
28
+ end
@@ -0,0 +1,38 @@
1
+ # dependency for extracting method bodies and comments
2
+ require 'method_source'
3
+
4
+ # basic logging, trying to avoid pulling in rails
5
+ require 'logging'
6
+
7
+ # core
8
+ require "durable_decorator/version"
9
+ require "durable_decorator/constantizer"
10
+ require "durable_decorator/base"
11
+
12
+ # errors
13
+ require "durable_decorator/bad_arity_error"
14
+ require "durable_decorator/undefined_method_error"
15
+ require "durable_decorator/invalid_decoration_error"
16
+ require "durable_decorator/tampered_definition_error"
17
+
18
+ module DurableDecorator
19
+ end
20
+
21
+ # monkey-patching Ruby core to create an API
22
+ Object.class_eval do
23
+ class << self
24
+ def decorate method_name, meta = nil, &block
25
+ DurableDecorator::Base.redefine self, method_name, meta, &block
26
+ end
27
+
28
+ def decorate_singleton method_name, meta = nil, &block
29
+ decorate "self.#{method_name}", meta, &block
30
+ end
31
+ end
32
+ end
33
+
34
+ Module.class_eval do
35
+ def decorate method_name, meta = nil, &block
36
+ DurableDecorator::Base.redefine self, method_name, meta, &block
37
+ end
38
+ end
@@ -0,0 +1,5 @@
1
+ module DurableDecorator
2
+ class BadArityError < Exception
3
+
4
+ end
5
+ end
@@ -0,0 +1,136 @@
1
+ require 'digest/md5'
2
+
3
+ module DurableDecorator
4
+ class Base
5
+ class << self
6
+ DECORATION_MODES = ['strict']
7
+ REDEFINITIONS = {}
8
+
9
+ def redefine clazz, method_name, meta, &block
10
+ if method_name.to_s.match /^self\./
11
+ redefine_instance (class << clazz; self; end), method_name.to_s.gsub("self.",''), meta, &block
12
+ else
13
+ redefine_instance clazz, method_name, meta, &block
14
+ end
15
+ end
16
+
17
+ def redefine_instance clazz, method_name, meta, &block
18
+ return unless old_method = existing_method(clazz, method_name, meta, &block)
19
+
20
+ alias_definitions clazz, method_name, method_sha(old_method)
21
+ redefine_method clazz, method_name, &block
22
+
23
+ store_redefinition clazz, method_name, old_method, block
24
+ end
25
+
26
+ # Ensure method exists before creating new definitions
27
+ def existing_method clazz, method_name, meta, &block
28
+ return if redefined? clazz, method_name, &block
29
+
30
+ old_method = validate_existing_definition clazz, method_name
31
+ validate_method_arity clazz, method_name, old_method, &block
32
+ validate_decoration_meta clazz, method_name, old_method, meta
33
+
34
+ old_method
35
+ end
36
+
37
+ def validate_decoration_meta clazz, method_name, old_method, meta
38
+ return unless meta
39
+
40
+ chill_meta = {}
41
+ meta.each do |k,v|
42
+ chill_meta[k.to_sym] = v
43
+ end
44
+
45
+ raise InvalidDecorationError, "The hash provided to the decorator is invalid" unless DECORATION_MODES.include? chill_meta[:mode] and chill_meta[:sha] and !chill_meta[:sha].empty?
46
+
47
+ raise TamperedDefinitionError, "Method SHA mismatch, the definition has been tampered with" unless method_sha(old_method) == chill_meta[:sha]
48
+ end
49
+
50
+ def validate_method_arity clazz, method_name, old_method, &block
51
+ raise BadArityError, "Attempting to override #{clazz}'s #{method_name} with incorrect arity." if block.arity != old_method.arity and block.arity > 0 # See the #arity behavior disparity between 1.8- and 1.9+
52
+ end
53
+
54
+ def validate_existing_definition clazz, method_name
55
+ begin
56
+ clazz.instance_method(method_name)
57
+ rescue NameError => e
58
+ raise UndefinedMethodError, "#{clazz}##{method_name} is not defined."
59
+ end
60
+ end
61
+
62
+ def redefine_method clazz, method_name, &block
63
+ clazz.class_eval do
64
+ define_method(method_name.to_sym, &block)
65
+ end
66
+ end
67
+
68
+ def alias_definitions clazz, method_name, old_sha
69
+ clazz.class_eval do
70
+ alias_method("#{method_name}_#{old_sha}", method_name)
71
+ alias_method("#{method_name}_old", method_name)
72
+ end
73
+ end
74
+
75
+ def store_redefinition clazz, name, old_method, new_method
76
+ class_name = clazz.name || ''
77
+ class_name = "Meta#{clazz.superclass.to_s}" if class_name.empty?
78
+ class_index = REDEFINITIONS[class_name.to_sym] ||= {}
79
+ method_index = class_index[name.to_sym] ||= []
80
+
81
+ to_store = [new_method]
82
+ to_store.unshift(old_method) if method_index.empty?
83
+
84
+ to_store.each do |method|
85
+ method_index << method_hash(name, method)
86
+ end
87
+
88
+ true
89
+ end
90
+
91
+ def method_hash name, method
92
+ {
93
+ :name => name,
94
+ :sha => method_sha(method)
95
+ }
96
+ end
97
+
98
+ def method_sha method
99
+ Digest::MD5.hexdigest(method.source.gsub(/\s+/, ' '))
100
+ end
101
+
102
+ def redefined? clazz, method_name, &block
103
+ begin
104
+ result =
105
+ overrides = REDEFINITIONS[clazz][method_name] and
106
+ overrides.select{|o| o == method_hash(method_name)}.first and
107
+ true
108
+ rescue
109
+ false
110
+ end
111
+ end
112
+
113
+ def logger
114
+ return @logger if @logger
115
+
116
+ @logger = Logging.logger(STDOUT)
117
+ @logger.level = :warn
118
+ @logger
119
+ end
120
+
121
+ def determine_sha target
122
+ raise "Please provide a fully qualified method name: Module::Clazz#instance_method or ::clazz_method" unless target.match(/\.|#/)
123
+
124
+ class_name, separator, method_name = target.match(/(.*)(\.|#)(.*)/)[1..3]
125
+ clazz = Constantizer.constantize(class_name)
126
+ method = if separator == '#'
127
+ clazz.instance_method(method_name)
128
+ else
129
+ clazz.method(method_name)
130
+ end
131
+
132
+ method_sha(method)
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,46 @@
1
+ module DurableDecorator
2
+ # borrowed straight from Rails' ActiveSupport
3
+ # https://github.com/rails/rails/blob/9e0b3fc7cfba43af55377488f991348e2de24515/activesupport/lib/active_support/inflector/methods.rb#L213
4
+ class Constantizer
5
+ if Module.method(:const_get).arity == 1
6
+ # Tries to find a constant with the name specified in the argument string:
7
+ #
8
+ # "Module".constantize # => Module
9
+ # "Test::Unit".constantize # => Test::Unit
10
+ #
11
+ # The name is assumed to be the one of a top-level constant, no matter whether
12
+ # it starts with "::" or not. No lexical context is taken into account:
13
+ #
14
+ # C = 'outside'
15
+ # module M
16
+ # C = 'inside'
17
+ # C # => 'inside'
18
+ # "C".constantize # => 'outside', same as ::C
19
+ # end
20
+ #
21
+ # NameError is raised when the name is not in CamelCase or the constant is
22
+ # unknown.
23
+ def self.constantize(camel_cased_word)
24
+ names = camel_cased_word.split('::')
25
+ names.shift if names.empty? || names.first.empty?
26
+
27
+ constant = Object
28
+ names.each do |name|
29
+ constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
30
+ end
31
+ constant
32
+ end
33
+ else
34
+ def self.constantize(camel_cased_word) #:nodoc:
35
+ names = camel_cased_word.split('::')
36
+ names.shift if names.empty? || names.first.empty?
37
+
38
+ constant = Object
39
+ names.each do |name|
40
+ constant = constant.const_defined?(name, false) ? constant.const_get(name) : constant.const_missing(name)
41
+ end
42
+ constant
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,6 @@
1
+ module DurableDecorator
2
+ class InvalidDecorationError < Exception
3
+
4
+ end
5
+ end
6
+
@@ -0,0 +1,6 @@
1
+ module DurableDecorator
2
+ class TamperedDefinitionError < Exception
3
+
4
+ end
5
+ end
6
+
@@ -0,0 +1,5 @@
1
+ module DurableDecorator
2
+ class UndefinedMethodError < Exception
3
+
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ module DurableDecorator
2
+ VERSION = "0.0.4"
3
+ end
@@ -0,0 +1,241 @@
1
+ require 'spec_helper'
2
+
3
+ describe DurableDecorator::Base do
4
+
5
+ context 'with classes' do
6
+ # Spec uses ./example_class.rb
7
+ context 'for existing instance methods' do
8
+ it 'guarantees access to #method_old' do
9
+ ExampleClass.class_eval do
10
+ decorate :no_param_method do
11
+ no_param_method_old + " and a new string"
12
+ end
13
+ end
14
+
15
+ instance = ExampleClass.new
16
+ instance.no_param_method.should == 'original and a new string'
17
+ end
18
+
19
+ context 'with incorrect arity' do
20
+ it 'throws an error' do
21
+ lambda{
22
+ ExampleClass.class_eval do
23
+ decorate(:no_param_method){|a,b| }
24
+ end
25
+ }.should raise_error(DurableDecorator::BadArityError)
26
+ end
27
+ end
28
+
29
+ context 'for methods with parameters' do
30
+ it 'guarantees access to #method_old' do
31
+ ExampleClass.class_eval do
32
+ decorate :one_param_method do |another_string|
33
+ "#{one_param_method_old('check')} and #{another_string}"
34
+ end
35
+ end
36
+
37
+ instance = ExampleClass.new
38
+ instance.one_param_method("here we go").should == 'original: check and here we go'
39
+ end
40
+ end
41
+
42
+ context 'for double decorations' do
43
+ before do
44
+ @original_sha = DurableDecorator::Base.determine_sha("ExampleClass#one_param_method")
45
+
46
+ ExampleClass.class_eval do
47
+ decorate :one_param_method do |another_string|
48
+ "#{one_param_method_1884eb7af7abbccec3fd4048f99363a3('check')} and #{another_string}"
49
+ end
50
+ end
51
+
52
+ @redef_sha = DurableDecorator::Base.determine_sha("ExampleClass#one_param_method")
53
+ end
54
+
55
+ it 'works with explicit method version invocation' do
56
+ ExampleClass.class_eval do
57
+ decorate :one_param_method do |boolean|
58
+ if boolean
59
+ one_param_method_old("")
60
+ else
61
+ "latest"
62
+ end
63
+ end
64
+ end
65
+
66
+ instance = ExampleClass.new
67
+ instance.one_param_method(true).should == "original: check and "
68
+ instance.one_param_method(false).should == 'latest'
69
+ @original_sha.should_not == @redef_sha
70
+ end
71
+ end
72
+
73
+ context 'for strict definitions' do
74
+ context 'with the correct SHA' do
75
+ it 'guarantees access to #method_old' do
76
+ ExampleClass.class_eval do
77
+ meta = {
78
+ :mode => 'strict',
79
+ :sha => 'ba3114b2d46caa684b3f7ba38d6f74b2'
80
+ }
81
+ decorate :no_param_method, meta do
82
+ no_param_method_old + " and a new string"
83
+ end
84
+ end
85
+
86
+ instance = ExampleClass.new
87
+ instance.no_param_method.should == "original and a new string"
88
+ end
89
+ end
90
+
91
+ context 'with the wrong SHA' do
92
+ it 'raises an error' do
93
+ lambda{
94
+ ExampleClass.class_eval do
95
+ meta = {
96
+ :mode => 'strict',
97
+ :sha => '1234wrong'
98
+ }
99
+ decorate :no_param_method, meta do
100
+ no_param_method_old + " and a new string"
101
+ end
102
+ end
103
+ }.should raise_error(DurableDecorator::TamperedDefinitionError)
104
+ end
105
+ end
106
+
107
+ context 'for an invalid config' do
108
+ it 'raises an error' do
109
+ lambda{
110
+ ExampleClass.class_eval do
111
+ meta = {
112
+ :mode => 'strict'
113
+ }
114
+ decorate :no_param_method, meta do
115
+ no_param_method_old + " and a new string"
116
+ end
117
+ end
118
+ }.should raise_error(DurableDecorator::InvalidDecorationError)
119
+ end
120
+ end
121
+ end
122
+ end
123
+
124
+ context 'for methods not yet defined' do
125
+ it 'throws an error' do
126
+ lambda{
127
+ ExampleClass.class_eval do
128
+ decorate(:integer_method){ }
129
+ end
130
+ }.should raise_error(DurableDecorator::UndefinedMethodError)
131
+ end
132
+ end
133
+
134
+ context 'for existing class methods' do
135
+ it 'guarantees access to ::method_old' do
136
+ ExampleClass.class_eval do
137
+ decorate_singleton :clazz_level do
138
+ clazz_level_old + " and a new string"
139
+ end
140
+ end
141
+
142
+ ExampleClass.clazz_level.should == 'original and a new string'
143
+ end
144
+
145
+ context 'with incorrect arity' do
146
+ it 'throws an error' do
147
+ lambda{
148
+ ExampleClass.class_eval do
149
+ decorate_singleton(:clazz_level){|a,b| }
150
+ end
151
+ }.should raise_error(DurableDecorator::BadArityError)
152
+ end
153
+ end
154
+
155
+ context 'for methods with parameters' do
156
+ it 'guarantees access to ::method_old' do
157
+ ExampleClass.class_eval do
158
+ decorate_singleton :clazz_level_paramed do |another_string|
159
+ "#{clazz_level_paramed_old('check')} and #{another_string}"
160
+ end
161
+ end
162
+
163
+ ExampleClass.clazz_level_paramed("here we go").should == 'original: check and here we go'
164
+ end
165
+ end
166
+ end
167
+
168
+ context 'for methods not yet defined' do
169
+ it 'throws an error' do
170
+ lambda{
171
+ ExampleClass.class_eval do
172
+ decorate_singleton(:integer_method){ }
173
+ end
174
+ }.should raise_error(DurableDecorator::UndefinedMethodError)
175
+ end
176
+ end
177
+ end
178
+
179
+ context 'with modules' do
180
+ # Spec uses ./sample_module.rb
181
+ context 'for existing methods' do
182
+ it 'guarantees access to #method_old' do
183
+ Sample.class_eval do
184
+ decorate :module_method do
185
+ module_method_old + " and a new string"
186
+ end
187
+ end
188
+
189
+ o = Object.new
190
+ o.extend(Sample)
191
+ o.module_method.should == 'original and a new string'
192
+ end
193
+
194
+ context 'with incorrect arity' do
195
+ it 'throws an error' do
196
+ lambda{
197
+ Sample.class_eval do
198
+ decorate(:module_method){|a,b| }
199
+ end
200
+ }.should raise_error(DurableDecorator::BadArityError)
201
+ end
202
+ end
203
+ end
204
+
205
+ context 'for methods not yet defined' do
206
+ it 'throws an error' do
207
+ lambda{
208
+ Sample.class_eval do
209
+ decorate(:integer_method){ }
210
+ end
211
+ }.should raise_error(DurableDecorator::UndefinedMethodError)
212
+ end
213
+ end
214
+ end
215
+
216
+ context 'finding the sha' do
217
+ context 'when asked to find the sha' do
218
+ context 'when the target is invalid' do
219
+ it 'should raise an error' do
220
+ lambda{
221
+ DurableDecorator::Base.determine_sha 'invalid'
222
+ }.should raise_error
223
+ end
224
+ end
225
+
226
+ context 'when the target is an instance method' do
227
+ it 'should return the sha' do
228
+ DurableDecorator::Base.determine_sha('ExampleClass#no_param_method').should ==
229
+ 'ba3114b2d46caa684b3f7ba38d6f74b2'
230
+ end
231
+ end
232
+
233
+ context 'when the target is a class method' do
234
+ it 'should return the sha' do
235
+ DurableDecorator::Base.determine_sha('ExampleClass.clazz_level').should ==
236
+ 'c5a3870a3934ce8d2145b841e42a8ad4'
237
+ end
238
+ end
239
+ end
240
+ end
241
+ end
@@ -0,0 +1,17 @@
1
+ class ExampleClass
2
+ def no_param_method
3
+ "original"
4
+ end
5
+
6
+ def one_param_method param
7
+ "original: #{param}"
8
+ end
9
+
10
+ def self.clazz_level
11
+ "original"
12
+ end
13
+
14
+ def self.clazz_level_paramed param
15
+ "original: #{param}"
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Ruby' do
4
+ it 'correctly identifies the method #source_location' do
5
+ DurableDecorator::Base.method(:existing_method).source_location.join.should match(/durable_decorator\/base\.rb/)
6
+ end
7
+
8
+ it 'makes an attempt at extracting the method body' do
9
+ DurableDecorator::Base.method(:existing_method).source.should match /old_method/
10
+ end
11
+
12
+ it 'makes an attempt at extracting the method comment' do
13
+ DurableDecorator::Base.method(:existing_method).comment.should match /ensure method exists/i
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ module Sample
2
+ def module_method
3
+ "original"
4
+ end
5
+ end
@@ -0,0 +1,9 @@
1
+ require 'rspec'
2
+ require 'durable_decorator'
3
+
4
+ RSpec.configure do |config|
5
+ config.before(:each) do
6
+ load 'example_class.rb'
7
+ load 'sample_module.rb'
8
+ end
9
+ end
metadata ADDED
@@ -0,0 +1,168 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: durable_decorator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.4
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Denis Ivanov
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-06-14 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: method_source
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: logging
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: activesupport
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: bundler
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '1.3'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: '1.3'
78
+ - !ruby/object:Gem::Dependency
79
+ name: rake
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: rspec
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ description: Allows method redefinitions while maintaining *super*
111
+ email:
112
+ - visible.h4x@gmail.com
113
+ executables: []
114
+ extensions: []
115
+ extra_rdoc_files: []
116
+ files:
117
+ - .gitignore
118
+ - .rspec
119
+ - .travis.yml
120
+ - Gemfile
121
+ - LICENSE.txt
122
+ - README.md
123
+ - Rakefile
124
+ - durable_decorator.gemspec
125
+ - lib/durable_decorator.rb
126
+ - lib/durable_decorator/bad_arity_error.rb
127
+ - lib/durable_decorator/base.rb
128
+ - lib/durable_decorator/constantizer.rb
129
+ - lib/durable_decorator/invalid_decoration_error.rb
130
+ - lib/durable_decorator/tampered_definition_error.rb
131
+ - lib/durable_decorator/undefined_method_error.rb
132
+ - lib/durable_decorator/version.rb
133
+ - spec/durable_decorator_spec.rb
134
+ - spec/example_class.rb
135
+ - spec/ruby_source_spec.rb
136
+ - spec/sample_module.rb
137
+ - spec/spec_helper.rb
138
+ homepage: https://github.com/jumph4x/durable-decorator
139
+ licenses:
140
+ - MIT
141
+ post_install_message:
142
+ rdoc_options: []
143
+ require_paths:
144
+ - lib
145
+ required_ruby_version: !ruby/object:Gem::Requirement
146
+ none: false
147
+ requirements:
148
+ - - ! '>='
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ required_rubygems_version: !ruby/object:Gem::Requirement
152
+ none: false
153
+ requirements:
154
+ - - ! '>='
155
+ - !ruby/object:Gem::Version
156
+ version: '0'
157
+ requirements: []
158
+ rubyforge_project:
159
+ rubygems_version: 1.8.25
160
+ signing_key:
161
+ specification_version: 3
162
+ summary: Allows method redefinitions while maintaining *super*
163
+ test_files:
164
+ - spec/durable_decorator_spec.rb
165
+ - spec/example_class.rb
166
+ - spec/ruby_source_spec.rb
167
+ - spec/sample_module.rb
168
+ - spec/spec_helper.rb