method_decorator 3.0.2 → 4.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 +4 -4
- data/.rubocop.yml +24 -0
- data/.ruby-version +1 -1
- data/.travis.yml +10 -0
- data/Gemfile +3 -1
- data/README.rdoc +4 -0
- data/Rakefile +5 -3
- data/bin/console +15 -0
- data/lib/method_decorator.rb +23 -63
- data/lib/method_decorator/errors/already_decorated_error.rb +8 -0
- data/lib/method_decorator/models/call.rb +26 -0
- data/lib/method_decorator/models/decoration.rb +28 -0
- data/lib/method_decorator/models/model.rb +23 -0
- data/lib/method_decorator/repositories/calls_repository.rb +45 -0
- data/lib/method_decorator/repositories/decorations_repository.rb +57 -0
- data/lib/method_decorator/services/decorations_service.rb +132 -0
- data/lib/method_decorator/version.rb +4 -2
- data/method_decorator.gemspec +11 -5
- data/spec/lib/method_decorator_spec.rb +93 -71
- data/spec/lib/models/call_spec.rb +53 -0
- data/spec/lib/models/decoration_spec.rb +69 -0
- data/spec/lib/models/model_spec.rb +11 -0
- data/spec/lib/repositories/calls_repository_spec.rb +42 -0
- data/spec/lib/repositories/decorations_repository_spec.rb +27 -0
- data/spec/spec_helper.rb +3 -2
- data/spec/support/dummy_class.rb +3 -5
- metadata +81 -7
- data/lib/method_decorator/decoration.rb +0 -14
- data/lib/method_decorator/repository.rb +0 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 24035e46f4f4e95da3c06a525d08c07cb0652541
|
4
|
+
data.tar.gz: 7671f1cfe76a192254cf902a8373e7a33fc6619f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ef0319cef3a7d8645a849b744b44bbb36cbd61bafdc343aa87a7a39c2e2674f6ae7d115f0dd8b7610389cead5e23ee0547681e3ee5498d74839232da3dc7ba0b
|
7
|
+
data.tar.gz: ebed4eae693f0f1a52c9d5dcd7f765735bf1e42b044fbb0a67378b191248a39cf81f0b119a7d74a4b133a335b37a48cd40b2010ec8105f157cfbf046b3d59f07
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
Documentation:
|
2
|
+
Enabled: false
|
3
|
+
|
4
|
+
Style/PredicateName:
|
5
|
+
Enabled: false
|
6
|
+
|
7
|
+
Metrics/LineLength:
|
8
|
+
Max: 99
|
9
|
+
|
10
|
+
Metrics/MethodLength:
|
11
|
+
Max: 20
|
12
|
+
|
13
|
+
Metrics/BlockLength:
|
14
|
+
ExcludedMethods: ['describe', 'context']
|
15
|
+
|
16
|
+
AllCops:
|
17
|
+
Exclude:
|
18
|
+
- 'vendor/**/*'
|
19
|
+
- 'spec/fixtures/**/*'
|
20
|
+
- 'db/**/*'
|
21
|
+
- 'config/**/*'
|
22
|
+
- 'script/**/*'
|
23
|
+
- !ruby/regexp /old_and_unused\.rb$/
|
24
|
+
TargetRubyVersion: 2.4
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
ruby-2.
|
1
|
+
ruby-2.4.0@method_decorator
|
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/README.rdoc
CHANGED
@@ -1,4 +1,8 @@
|
|
1
1
|
= Method Decorator
|
2
|
+
{<img src="https://travis-ci.org/r4z3c/method_decorator.svg?branch=master" alt="Build Status" />}[https://travis-ci.org/r4z3c/method_decorator]
|
3
|
+
{<img src="https://codeclimate.com/github/r4z3c/method_decorator/badges/gpa.svg" alt="Code Climate" />}[https://codeclimate.com/github/r4z3c/method_decorator]
|
4
|
+
{<img src="https://codeclimate.com/github/r4z3c/method_decorator/badges/coverage.svg" alt="Code Coverage" />}[https://codeclimate.com/github/r4z3c/method_decorator/coverage]
|
5
|
+
{<img src="https://codeclimate.com/github/r4z3c/method_decorator/badges/issue_count.svg" alt="Issue Count" />}[https://codeclimate.com/github/r4z3c/method_decorator]
|
2
6
|
|
3
7
|
Provides a way to dynamically override methods without losing original behavior.
|
4
8
|
|
data/Rakefile
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Rakefile for rake -*- ruby -*-
|
2
4
|
|
3
5
|
# Copyright 2003, 2004, 2005 by Jim Weirich (jim@weirichhouse.org)
|
@@ -11,7 +13,7 @@ require 'rake/testtask'
|
|
11
13
|
require 'rdoc/task'
|
12
14
|
|
13
15
|
Rake::TestTask.new(:test) do |t|
|
14
|
-
t.libs <<
|
16
|
+
t.libs << 'test'
|
15
17
|
t.verbose = true
|
16
18
|
t.test_files = FileList['test/**/test_*.rb']
|
17
19
|
end
|
@@ -27,7 +29,7 @@ end
|
|
27
29
|
require 'rspec/core/rake_task'
|
28
30
|
|
29
31
|
RSpec::Core::RakeTask.new(:spec) do |t|
|
30
|
-
t.rspec_opts = %w
|
32
|
+
t.rspec_opts = %w[--color]
|
31
33
|
end
|
32
34
|
|
33
|
-
task :
|
35
|
+
task default: :spec
|
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'method_decorator'
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require "pry"
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require 'irb'
|
15
|
+
IRB.start(__FILE__)
|
data/lib/method_decorator.rb
CHANGED
@@ -1,84 +1,44 @@
|
|
1
|
-
|
2
|
-
require 'active_support/concern'
|
3
|
-
|
4
|
-
module MethodDecorator extend ActiveSupport::Concern
|
5
|
-
|
6
|
-
included do
|
1
|
+
# frozen_string_literal: true
|
7
2
|
|
8
|
-
|
3
|
+
require 'active_support/concern'
|
4
|
+
require 'method_decorator/services/decorations_service'
|
9
5
|
|
10
|
-
|
11
|
-
|
12
|
-
end
|
6
|
+
module MethodDecorator
|
7
|
+
extend ActiveSupport::Concern
|
13
8
|
|
9
|
+
class_methods do
|
10
|
+
def decorate(method_name, &decoration)
|
11
|
+
MethodDecorator.decorate self, method_name, &decoration
|
14
12
|
end
|
15
|
-
|
16
13
|
end
|
17
14
|
|
18
15
|
class << self
|
19
|
-
|
20
|
-
|
21
|
-
raise 'target_method_name must be a symbol' unless target_method_name.is_a? Symbol
|
22
|
-
added = add_to_repository(decoration, target_class, target_method_name)
|
23
|
-
override_method(decoration, target_class, target_method_name) if added
|
24
|
-
added
|
16
|
+
def decorate(klass, method_name, &decoration)
|
17
|
+
decorations_service.decorate(klass, method_name, &decoration)
|
25
18
|
end
|
26
19
|
|
27
|
-
|
28
|
-
|
29
|
-
def add_to_repository(decoration, target_class, target_method_name)
|
30
|
-
Repository.add(
|
31
|
-
target_class,
|
32
|
-
target_method_name,
|
33
|
-
target_class.instance_method(target_method_name),
|
34
|
-
&decoration
|
35
|
-
)
|
20
|
+
def add_call(klass, method_name, *args, &block)
|
21
|
+
decorations_service.add_call(klass, method_name, *args, &block)
|
36
22
|
end
|
37
23
|
|
38
|
-
def
|
39
|
-
|
40
|
-
is_private = target_class.private_instance_methods.include? target_method_name
|
41
|
-
define_decorated_method decoration, target_class, target_method_name
|
42
|
-
target_class.instance_eval { protected target_method_name } if is_protected
|
43
|
-
target_class.instance_eval { private target_method_name } if is_private
|
24
|
+
def call_original_method(context, method_name, *args, &block)
|
25
|
+
decorations_service.call_original_method(context, method_name, *args, &block)
|
44
26
|
end
|
45
27
|
|
46
|
-
def
|
47
|
-
|
48
|
-
target_class.send :define_method, :call_args do return *args end
|
49
|
-
target_class.send :define_method, :call_block do return block end
|
50
|
-
target_class.send :define_method, :call_original do
|
51
|
-
MethodDecorator.call_original_method self, target_method_name, *call_args, &call_block
|
52
|
-
end
|
53
|
-
target_class.send :define_method, :call_original_with do |*desired_args, &desired_block|
|
54
|
-
MethodDecorator.call_original_method self, target_method_name, *desired_args, &desired_block
|
55
|
-
end
|
56
|
-
target_class.instance_eval { protected :call_args }
|
57
|
-
target_class.instance_eval { protected :call_block }
|
58
|
-
instance_eval &decoration
|
59
|
-
end
|
28
|
+
def last_call_args(context)
|
29
|
+
decorations_service.last_call_args(context)
|
60
30
|
end
|
61
31
|
|
62
|
-
|
63
|
-
|
64
|
-
def call_original_method(target_context, target_method_name, *original_args, &original_block)
|
65
|
-
target_class = target_class_from_context(target_context)
|
66
|
-
target_method = Repository.original_target_method_of(target_class, target_method_name)
|
67
|
-
target_method.bind(target_context).call(*original_args, &original_block)
|
32
|
+
def last_call_block(context)
|
33
|
+
decorations_service.last_call_block(context)
|
68
34
|
end
|
69
35
|
|
70
|
-
def
|
71
|
-
|
72
|
-
target_context.singleton_class
|
73
|
-
else
|
74
|
-
target_context.class
|
75
|
-
end
|
36
|
+
def last_call_method_name(context)
|
37
|
+
decorations_service.last_call_method_name(context)
|
76
38
|
end
|
77
39
|
|
78
|
-
def
|
79
|
-
|
40
|
+
def decorations_service
|
41
|
+
MethodDecorator::Services::DecorationsService
|
80
42
|
end
|
81
|
-
|
82
43
|
end
|
83
|
-
|
84
|
-
end
|
44
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'method_decorator/models/model'
|
4
|
+
|
5
|
+
module MethodDecorator
|
6
|
+
module Models
|
7
|
+
class Call < Model
|
8
|
+
attr_accessor :klass, :method_name, :args, :block
|
9
|
+
|
10
|
+
def initialize(klass, method_name, *args, &block)
|
11
|
+
self.klass = klass
|
12
|
+
self.method_name = method_name
|
13
|
+
self.args = *args
|
14
|
+
self.block = block
|
15
|
+
validate!
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def validate!
|
21
|
+
validate_attr!(:klass, Class)
|
22
|
+
validate_attr!(:method_name, Symbol)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'method_decorator/models/model'
|
4
|
+
|
5
|
+
module MethodDecorator
|
6
|
+
module Models
|
7
|
+
class Decoration < Model
|
8
|
+
attr_accessor :klass, :method_name, :method_instance, :decoration
|
9
|
+
|
10
|
+
def initialize(klass, method_name, method_instance)
|
11
|
+
self.klass = klass
|
12
|
+
self.method_name = method_name
|
13
|
+
self.method_instance = method_instance
|
14
|
+
self.decoration = block_given? ? Proc.new : nil
|
15
|
+
validate!
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def validate!
|
21
|
+
validate_attr!(:klass, Class)
|
22
|
+
validate_attr!(:method_name, Symbol)
|
23
|
+
validate_attr!(:method_instance, UnboundMethod)
|
24
|
+
validate_attr!(:decoration, Proc)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MethodDecorator
|
4
|
+
module Models
|
5
|
+
class Model
|
6
|
+
protected
|
7
|
+
|
8
|
+
def validate!
|
9
|
+
raise(NotImplementedError, "`#{self.class}` must override `validate!`")
|
10
|
+
end
|
11
|
+
|
12
|
+
def validate_attr!(attr, type)
|
13
|
+
value = send(attr)
|
14
|
+
invalid_attr!(attr) unless value
|
15
|
+
invalid_attr!(attr, "must be a `#{type}`") unless value.is_a?(type)
|
16
|
+
end
|
17
|
+
|
18
|
+
def invalid_attr!(attr, error = 'is required')
|
19
|
+
raise(ArgumentError, "`#{attr}` attribute #{error}")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'method_decorator/models/call'
|
4
|
+
|
5
|
+
module MethodDecorator
|
6
|
+
module Repositories
|
7
|
+
class CallsRepository
|
8
|
+
attr_writer :calls
|
9
|
+
|
10
|
+
def add(klass, method_name, *args, &block)
|
11
|
+
call = find_call(klass)
|
12
|
+
if call
|
13
|
+
update_existing_call(call, method_name, *args, &block)
|
14
|
+
else
|
15
|
+
push_new_call(klass, method_name, *args, &block)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def find_call(klass)
|
20
|
+
calls.find { |call| call.klass.eql?(klass) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def update_existing_call(call, method_name, *args, &block)
|
24
|
+
call.method_name = method_name
|
25
|
+
call.args = *args
|
26
|
+
call.block = block
|
27
|
+
call
|
28
|
+
end
|
29
|
+
|
30
|
+
def push_new_call(klass, method_name, *args, &block)
|
31
|
+
calls.push(MethodDecorator::Models::Call.new(klass, method_name, *args, &block))
|
32
|
+
end
|
33
|
+
|
34
|
+
def calls
|
35
|
+
@calls ||= []
|
36
|
+
end
|
37
|
+
|
38
|
+
class << self
|
39
|
+
def singleton
|
40
|
+
@singleton ||= new
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'method_decorator/models/decoration'
|
4
|
+
require 'method_decorator/models/call'
|
5
|
+
require 'method_decorator/errors/already_decorated_error'
|
6
|
+
|
7
|
+
module MethodDecorator
|
8
|
+
module Repositories
|
9
|
+
class DecorationsRepository
|
10
|
+
attr_writer :decorations
|
11
|
+
|
12
|
+
def add!(klass, method_name, method_instance, &decoration)
|
13
|
+
added = add(klass, method_name, method_instance, &decoration)
|
14
|
+
self.class.raise_already_decorated_error(klass, method_name) unless added
|
15
|
+
added
|
16
|
+
end
|
17
|
+
|
18
|
+
def add(klass, method_name, method_instance, &decoration)
|
19
|
+
existing = original_method_instance_of(klass, method_name)
|
20
|
+
unless existing
|
21
|
+
decorations.push(
|
22
|
+
Models::Decoration.new(klass, method_name, method_instance, &decoration)
|
23
|
+
)
|
24
|
+
end
|
25
|
+
!existing
|
26
|
+
end
|
27
|
+
|
28
|
+
def original_method_instance_of(klass, method_name)
|
29
|
+
decoration = find_decoration(klass, method_name)
|
30
|
+
decoration ? decoration.method_instance : nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def find_decoration(klass, method_name)
|
34
|
+
decorations.select do |d|
|
35
|
+
d.klass.eql?(klass) && d.method_name.eql?(method_name)
|
36
|
+
end.first
|
37
|
+
end
|
38
|
+
|
39
|
+
def decorations
|
40
|
+
@decorations ||= []
|
41
|
+
end
|
42
|
+
|
43
|
+
class << self
|
44
|
+
def raise_already_decorated_error(klass, method_name)
|
45
|
+
raise(
|
46
|
+
MethodDecorator::Errors::AlreadyDecoratedError,
|
47
|
+
"`#{klass}` already has a decorated `#{method_name}`"
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
def singleton
|
52
|
+
@singleton ||= new
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'method_decorator/repositories/decorations_repository'
|
4
|
+
require 'method_decorator/repositories/calls_repository'
|
5
|
+
require 'method_decorator/models/call'
|
6
|
+
|
7
|
+
module MethodDecorator
|
8
|
+
module Services
|
9
|
+
class DecorationsService
|
10
|
+
attr_accessor :klass, :method_name, :method_instance, :decoration
|
11
|
+
|
12
|
+
def initialize(klass, method_name, &decoration)
|
13
|
+
self.klass = klass
|
14
|
+
self.method_name = method_name
|
15
|
+
self.method_instance = klass.instance_method(method_name)
|
16
|
+
self.decoration = decoration
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def decorate
|
21
|
+
add_decoration_to_repository
|
22
|
+
override_original_klass_method
|
23
|
+
define_helper_methods
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
protected
|
28
|
+
|
29
|
+
def add_decoration_to_repository
|
30
|
+
self.class.decorations_repository.add!(klass, method_name, method_instance, &decoration)
|
31
|
+
end
|
32
|
+
|
33
|
+
def override_original_klass_method # rubocop:disable Metrics/AbcSize
|
34
|
+
is_protected = klass.protected_instance_methods.include?(method_name)
|
35
|
+
is_private = klass.private_instance_methods.include?(method_name)
|
36
|
+
define_decoration_method
|
37
|
+
method_name_ = method_name
|
38
|
+
klass.instance_eval { protected method_name_ } if is_protected
|
39
|
+
klass.instance_eval { private method_name_ } if is_private
|
40
|
+
end
|
41
|
+
|
42
|
+
def define_decoration_method
|
43
|
+
klass_ = klass
|
44
|
+
method_name_ = method_name
|
45
|
+
decoration_ = decoration
|
46
|
+
klass.send(:define_method, method_name) do |*args, &block|
|
47
|
+
MethodDecorator.add_call(klass_, method_name_, *args, &block)
|
48
|
+
instance_eval(&decoration_)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def define_helper_methods
|
53
|
+
klass.send(:define_method, :call_args) { MethodDecorator.last_call_args(self) }
|
54
|
+
klass.send(:define_method, :call_block) { MethodDecorator.last_call_block(self) }
|
55
|
+
define_original_call_helper_method
|
56
|
+
define_original_call_with_helper_method
|
57
|
+
klass.instance_eval { protected :call_args }
|
58
|
+
klass.instance_eval { protected :call_block }
|
59
|
+
end
|
60
|
+
|
61
|
+
def define_original_call_helper_method
|
62
|
+
klass.send :define_method, :call_original do
|
63
|
+
method_name = MethodDecorator.last_call_method_name(self)
|
64
|
+
MethodDecorator.call_original_method self, method_name, *call_args, &call_block
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def define_original_call_with_helper_method
|
69
|
+
klass.send :define_method, :call_original_with do |*desired_args, &desired_block|
|
70
|
+
method_name = MethodDecorator.last_call_method_name(self)
|
71
|
+
MethodDecorator.call_original_method self, method_name, *desired_args, &desired_block
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class << self
|
76
|
+
def decorate(klass, method_name, &decoration)
|
77
|
+
new(klass, method_name, &decoration).decorate
|
78
|
+
end
|
79
|
+
|
80
|
+
def add_call(klass, method_name, *args, &block)
|
81
|
+
calls_repository.add(klass, method_name, *args, &block)
|
82
|
+
end
|
83
|
+
|
84
|
+
def call_original_method(context, method_name, *args, &block)
|
85
|
+
klass = get_class_from_context(context)
|
86
|
+
method_instance = decorations_repository.original_method_instance_of(klass, method_name)
|
87
|
+
method_instance.bind(context).call(*args, &block)
|
88
|
+
end
|
89
|
+
|
90
|
+
def last_call_args(context)
|
91
|
+
call = last_call_from_context(context)
|
92
|
+
call && call.args
|
93
|
+
end
|
94
|
+
|
95
|
+
def last_call_block(context)
|
96
|
+
call = last_call_from_context(context)
|
97
|
+
call && call.block
|
98
|
+
end
|
99
|
+
|
100
|
+
def last_call_method_name(context)
|
101
|
+
call = last_call_from_context(context)
|
102
|
+
call && call.method_name
|
103
|
+
end
|
104
|
+
|
105
|
+
def last_call_from_context(context)
|
106
|
+
klass = get_class_from_context(context)
|
107
|
+
calls_repository.find_call(klass)
|
108
|
+
end
|
109
|
+
|
110
|
+
def get_class_from_context(context)
|
111
|
+
if get_type_from_context(context).eql? :singleton
|
112
|
+
context.singleton_class
|
113
|
+
else
|
114
|
+
context.class
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def get_type_from_context(context)
|
119
|
+
context.class.eql?(Class) ? :singleton : :class
|
120
|
+
end
|
121
|
+
|
122
|
+
def decorations_repository
|
123
|
+
MethodDecorator::Repositories::DecorationsRepository.singleton
|
124
|
+
end
|
125
|
+
|
126
|
+
def calls_repository
|
127
|
+
MethodDecorator::Repositories::CallsRepository.singleton
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
data/method_decorator.gemspec
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
$LOAD_PATH.push File.expand_path('../lib', __FILE__)
|
2
4
|
|
3
5
|
require 'method_decorator/version'
|
4
6
|
|
@@ -8,18 +10,22 @@ Gem::Specification.new do |s|
|
|
8
10
|
s.authors = ['r4z3c']
|
9
11
|
s.email = ['r4z3c.43@gmail.com']
|
10
12
|
s.homepage = 'https://github.com/r4z3c/method_decorator.git'
|
11
|
-
s.summary = '
|
12
|
-
s.description = 'Provides a way to dynamically
|
13
|
-
s.licenses = %w
|
13
|
+
s.summary = 'Override methods preserving the original behavior'
|
14
|
+
s.description = 'Provides a way to dynamically override methods without losing original behavior'
|
15
|
+
s.licenses = %w[MIT]
|
14
16
|
|
15
17
|
s.files = `git ls-files`.split("\n")
|
16
18
|
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
17
19
|
|
18
|
-
s.require_paths = %w
|
20
|
+
s.require_paths = %w[lib]
|
19
21
|
|
20
22
|
s.add_dependency 'bundler', '>= 1'
|
21
23
|
s.add_dependency 'activesupport', '>= 4'
|
22
24
|
|
23
25
|
s.add_development_dependency 'rspec', '>= 3'
|
24
26
|
s.add_development_dependency 'simplecov', '>= 0'
|
27
|
+
s.add_development_dependency 'codeclimate-test-reporter'
|
28
|
+
s.add_development_dependency 'rake'
|
29
|
+
s.add_development_dependency 'rubocop'
|
30
|
+
s.add_development_dependency 'byebug'
|
25
31
|
end
|
@@ -1,159 +1,181 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
require 'support/dummy_class'
|
3
5
|
|
4
6
|
describe MethodDecorator do
|
5
|
-
|
6
7
|
let(:dummy_arg) { :dummy_arg }
|
7
8
|
|
8
9
|
context 'when common class as target' do
|
9
|
-
|
10
10
|
let(:target) { DummyClass }
|
11
11
|
let(:target_instance) { target.new }
|
12
12
|
|
13
13
|
context 'when public method' do
|
14
|
-
|
15
14
|
context 'when through module singleton method' do
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
before do
|
16
|
+
expect(target_instance).to receive(:puts)
|
17
|
+
.with("a_decorated_public_instance_method_with: #{[dummy_arg]}").ordered
|
18
|
+
end
|
19
|
+
before do
|
20
|
+
expect(target_instance).to receive(:puts)
|
21
|
+
.with("a_public_instance_method_arg: #{dummy_arg}").ordered
|
22
|
+
end
|
19
23
|
|
20
24
|
it { target_instance.a_public_instance_method dummy_arg }
|
21
|
-
|
22
25
|
end
|
23
26
|
|
24
27
|
context 'when through module inclusion' do
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
+
before do
|
29
|
+
expect(target_instance).to receive(:puts)
|
30
|
+
.with("another_decorated_public_instance_method_with: #{[dummy_arg]}").ordered
|
31
|
+
end
|
32
|
+
before do
|
33
|
+
expect(target_instance).to receive(:puts)
|
34
|
+
.with("another_public_instance_method_arg: #{dummy_arg}").ordered
|
35
|
+
end
|
28
36
|
|
29
37
|
it { target_instance.another_public_instance_method dummy_arg }
|
30
|
-
|
31
38
|
end
|
32
|
-
|
33
39
|
end
|
34
40
|
|
35
41
|
context 'when protected method' do
|
36
|
-
|
37
42
|
context 'when calling original method through `call_original`' do
|
38
|
-
|
39
43
|
context 'when calling protected method deliberately' do
|
40
|
-
|
41
|
-
|
42
|
-
|
44
|
+
it do
|
45
|
+
expect { target_instance.a_protected_instance_method dummy_arg }
|
46
|
+
.to raise_error(NoMethodError)
|
47
|
+
end
|
43
48
|
end
|
44
49
|
|
45
50
|
context 'when calling protected method through `send`' do
|
46
|
-
|
47
|
-
|
48
|
-
|
51
|
+
before do
|
52
|
+
expect(target_instance).to receive(:puts)
|
53
|
+
.with("a_decorated_protected_instance_method_with: #{[dummy_arg]}").ordered
|
54
|
+
end
|
55
|
+
before do
|
56
|
+
expect(target_instance).to receive(:puts)
|
57
|
+
.with("a_protected_instance_method_arg: #{dummy_arg}").ordered
|
58
|
+
end
|
49
59
|
|
50
60
|
it { target_instance.send :a_protected_instance_method, dummy_arg }
|
51
|
-
|
52
61
|
end
|
53
|
-
|
54
62
|
end
|
55
63
|
|
56
64
|
context 'when calling original method through `call_original_with`' do
|
57
|
-
|
58
65
|
context 'when calling protected method through `send`' do
|
59
|
-
|
60
|
-
|
61
|
-
|
66
|
+
before do
|
67
|
+
expect(target_instance).to receive(:puts)
|
68
|
+
.with("another_decorated_protected_instance_method_with: #{[dummy_arg]}").ordered
|
69
|
+
end
|
70
|
+
before do
|
71
|
+
expect(target_instance).to receive(:puts)
|
72
|
+
.with('another_protected_instance_method_arg: hue').ordered
|
73
|
+
end
|
62
74
|
|
63
75
|
it { target_instance.send :another_protected_instance_method, dummy_arg }
|
64
|
-
|
65
76
|
end
|
66
|
-
|
67
77
|
end
|
68
|
-
|
69
78
|
end
|
70
79
|
|
71
80
|
context 'when private method' do
|
72
|
-
|
73
81
|
context 'when calling private method deliberately' do
|
74
|
-
|
75
|
-
|
76
|
-
|
82
|
+
it do
|
83
|
+
expect { target_instance.a_private_instance_method dummy_arg }
|
84
|
+
.to raise_error(NoMethodError)
|
85
|
+
end
|
77
86
|
end
|
78
87
|
|
79
88
|
context 'when calling private method through `send`' do
|
80
|
-
|
81
|
-
|
82
|
-
|
89
|
+
before do
|
90
|
+
expect(target_instance).to receive(:puts)
|
91
|
+
.with("a_decorated_private_instance_method_with: #{[dummy_arg]}").ordered
|
92
|
+
end
|
93
|
+
before do
|
94
|
+
expect(target_instance).to receive(:puts)
|
95
|
+
.with("a_private_instance_method_arg: #{dummy_arg}").ordered
|
96
|
+
end
|
83
97
|
|
84
98
|
it { target_instance.send :a_private_instance_method, dummy_arg }
|
85
|
-
|
86
99
|
end
|
87
|
-
|
88
100
|
end
|
89
|
-
|
90
101
|
end
|
91
102
|
|
92
103
|
context 'when singleton class as target' do
|
93
|
-
|
94
104
|
let(:target) { DummyClass.singleton_class }
|
95
105
|
let(:target_instance) { DummyClass }
|
96
106
|
|
97
107
|
context 'when public method' do
|
98
|
-
|
99
108
|
context 'when through module singleton method' do
|
100
|
-
|
101
|
-
|
102
|
-
|
109
|
+
before do
|
110
|
+
expect(target_instance).to receive(:puts)
|
111
|
+
.with("a_decorated_public_singleton_method_with: #{[dummy_arg]}").ordered
|
112
|
+
end
|
113
|
+
before do
|
114
|
+
expect(target_instance).to receive(:puts)
|
115
|
+
.with("a_public_singleton_method_arg: #{dummy_arg}").ordered
|
116
|
+
end
|
103
117
|
|
104
118
|
it { target_instance.a_public_singleton_method dummy_arg }
|
105
|
-
|
106
119
|
end
|
107
120
|
|
108
121
|
context 'when through module inclusion' do
|
109
|
-
|
110
|
-
|
111
|
-
|
122
|
+
before do
|
123
|
+
expect(target_instance).to receive(:puts)
|
124
|
+
.with("another_decorated_public_singleton_method_with: #{[dummy_arg]}").ordered
|
125
|
+
end
|
126
|
+
before do
|
127
|
+
expect(target_instance).to receive(:puts)
|
128
|
+
.with("another_public_singleton_method_arg: #{dummy_arg}").ordered
|
129
|
+
end
|
112
130
|
|
113
131
|
it { target_instance.another_public_singleton_method dummy_arg }
|
114
|
-
|
115
132
|
end
|
116
|
-
|
117
133
|
end
|
118
134
|
|
119
135
|
context 'when protected method' do
|
120
|
-
|
121
136
|
context 'when calling protected method deliberately' do
|
122
|
-
|
123
|
-
|
124
|
-
|
137
|
+
it do
|
138
|
+
expect do
|
139
|
+
target_instance.a_protected_singleton_method dummy_arg
|
140
|
+
end.to raise_error(NoMethodError)
|
141
|
+
end
|
125
142
|
end
|
126
143
|
|
127
144
|
context 'when calling protected method through `send`' do
|
128
|
-
|
129
|
-
|
130
|
-
|
145
|
+
before do
|
146
|
+
expect(target_instance).to receive(:puts)
|
147
|
+
.with("a_decorated_protected_singleton_method_with: #{[dummy_arg]}").ordered
|
148
|
+
end
|
149
|
+
before do
|
150
|
+
expect(target_instance).to receive(:puts)
|
151
|
+
.with("a_protected_singleton_method_arg: #{dummy_arg}").ordered
|
152
|
+
end
|
131
153
|
|
132
154
|
it { target_instance.send :a_protected_singleton_method, dummy_arg }
|
133
|
-
|
134
155
|
end
|
135
|
-
|
136
156
|
end
|
137
157
|
|
138
158
|
context 'when private method' do
|
139
|
-
|
140
159
|
context 'when calling private method deliberately' do
|
141
|
-
|
142
|
-
|
143
|
-
|
160
|
+
it do
|
161
|
+
expect do
|
162
|
+
target_instance.a_private_singleton_method dummy_arg
|
163
|
+
end.to raise_error NoMethodError
|
164
|
+
end
|
144
165
|
end
|
145
166
|
|
146
167
|
context 'when calling private method through `send`' do
|
147
|
-
|
148
|
-
|
149
|
-
|
168
|
+
before do
|
169
|
+
expect(target_instance).to receive(:puts)
|
170
|
+
.with("a_decorated_private_singleton_method_with: #{[dummy_arg]}").ordered
|
171
|
+
end
|
172
|
+
before do
|
173
|
+
expect(target_instance).to receive(:puts)
|
174
|
+
.with("a_private_singleton_method_arg: #{dummy_arg}").ordered
|
175
|
+
end
|
150
176
|
|
151
177
|
it { target_instance.send :a_private_singleton_method, dummy_arg }
|
152
|
-
|
153
178
|
end
|
154
|
-
|
155
179
|
end
|
156
|
-
|
157
180
|
end
|
158
|
-
|
159
|
-
end
|
181
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'method_decorator/models/call'
|
5
|
+
|
6
|
+
describe MethodDecorator::Models::Call do
|
7
|
+
describe '#initialize' do
|
8
|
+
subject { described_class.new(*args, &block) }
|
9
|
+
|
10
|
+
context 'when klass is missing' do
|
11
|
+
let(:args) { [nil, nil] }
|
12
|
+
let(:block) { nil }
|
13
|
+
let(:error) { '`klass` attribute is required' }
|
14
|
+
|
15
|
+
it { expect { subject }.to raise_error(ArgumentError, error) }
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'when klass is not a class' do
|
19
|
+
let(:args) { [:klass, nil] }
|
20
|
+
let(:block) { nil }
|
21
|
+
let(:error) { '`klass` attribute must be a `Class`' }
|
22
|
+
|
23
|
+
it { expect { subject }.to raise_error(ArgumentError, error) }
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'when method name is missing' do
|
27
|
+
let(:args) { [String, nil] }
|
28
|
+
let(:block) { nil }
|
29
|
+
let(:error) { '`method_name` attribute is required' }
|
30
|
+
|
31
|
+
it { expect { subject }.to raise_error(ArgumentError, error) }
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'when method name is not a symbol' do
|
35
|
+
let(:args) { [String, 'method_name'] }
|
36
|
+
let(:block) { nil }
|
37
|
+
let(:error) { '`method_name` attribute must be a `Symbol`' }
|
38
|
+
|
39
|
+
it { expect { subject }.to raise_error(ArgumentError, error) }
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'when everything is ok' do
|
43
|
+
let(:args) { [String, :method_name, :a_first_arg, :a_second_arg] }
|
44
|
+
let(:block) { proc { :a_block } }
|
45
|
+
let(:call) { subject }
|
46
|
+
|
47
|
+
it { expect(call.klass).to eq String }
|
48
|
+
it { expect(call.method_name).to eq :method_name }
|
49
|
+
it { expect(call.args).to eq %i[a_first_arg a_second_arg] }
|
50
|
+
it { expect(call.block).to eq block }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'method_decorator/models/decoration'
|
5
|
+
require 'method_decorator/models/call'
|
6
|
+
|
7
|
+
describe MethodDecorator::Models::Decoration do
|
8
|
+
let(:method_instance) { String.instance_method(:to_s) }
|
9
|
+
|
10
|
+
describe '#initialize' do
|
11
|
+
subject { described_class.new(*args, &block) }
|
12
|
+
|
13
|
+
context 'when `klass` attribute is missing' do
|
14
|
+
let(:args) { [nil, :method_name, method_instance] }
|
15
|
+
let(:block) { proc {} }
|
16
|
+
let(:error) { '`klass` attribute is required' }
|
17
|
+
|
18
|
+
it { expect { subject }.to raise_error(ArgumentError, error) }
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'when `klass` attribute is not a class' do
|
22
|
+
let(:args) { [:klass, :method_name, method_instance] }
|
23
|
+
let(:block) { proc {} }
|
24
|
+
let(:error) { '`klass` attribute must be a `Class`' }
|
25
|
+
|
26
|
+
it { expect { subject }.to raise_error(ArgumentError, error) }
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'when `method_name` attribute is missing' do
|
30
|
+
let(:args) { [String, nil, method_instance] }
|
31
|
+
let(:block) { proc {} }
|
32
|
+
let(:error) { '`method_name` attribute is required' }
|
33
|
+
|
34
|
+
it { expect { subject }.to raise_error(ArgumentError, error) }
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'when `method_name` attribute is not a symbol' do
|
38
|
+
let(:args) { [String, 'method_name', method_instance] }
|
39
|
+
let(:block) { proc {} }
|
40
|
+
let(:error) { '`method_name` attribute must be a `Symbol`' }
|
41
|
+
|
42
|
+
it { expect { subject }.to raise_error(ArgumentError, error) }
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'when `method_instance` attribute is missing' do
|
46
|
+
let(:args) { [String, :method_name, nil] }
|
47
|
+
let(:block) { proc {} }
|
48
|
+
let(:error) { '`method_instance` attribute is required' }
|
49
|
+
|
50
|
+
it { expect { subject }.to raise_error(ArgumentError, error) }
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'when `method_instance` attribute is not a unbound method' do
|
54
|
+
let(:args) { [String, :method_name, :method_instance] }
|
55
|
+
let(:block) { proc {} }
|
56
|
+
let(:error) { '`method_instance` attribute must be a `UnboundMethod`' }
|
57
|
+
|
58
|
+
it { expect { subject }.to raise_error(ArgumentError, error) }
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'when `decoration` attribute is missing' do
|
62
|
+
let(:args) { [String, :method_name, method_instance] }
|
63
|
+
let(:block) { nil }
|
64
|
+
let(:error) { '`decoration` attribute is required' }
|
65
|
+
|
66
|
+
it { expect { subject }.to raise_error(ArgumentError, error) }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'method_decorator/models/model'
|
5
|
+
|
6
|
+
describe MethodDecorator::Models::Model do
|
7
|
+
describe '#validate!' do
|
8
|
+
subject { described_class.new.send :validate! }
|
9
|
+
it { expect { subject }.to raise_error(NotImplementedError) }
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'method_decorator/repositories/calls_repository'
|
5
|
+
|
6
|
+
describe MethodDecorator::Repositories::CallsRepository do
|
7
|
+
let(:repo) { described_class.singleton }
|
8
|
+
|
9
|
+
describe '#add' do
|
10
|
+
let(:common_args) { [String, :method_name, :a_first_arg, :a_second_arg] }
|
11
|
+
let(:block) { proc {} }
|
12
|
+
|
13
|
+
before { repo.calls = [] }
|
14
|
+
|
15
|
+
subject { repo.add(*args, &block) }
|
16
|
+
|
17
|
+
context 'when call not exists' do
|
18
|
+
let(:args) { common_args }
|
19
|
+
|
20
|
+
before { subject }
|
21
|
+
|
22
|
+
it { expect(repo.calls.count).to eq 1 }
|
23
|
+
it { expect(subject[0].klass).to eq String }
|
24
|
+
it { expect(subject[0].method_name).to eq :method_name }
|
25
|
+
it { expect(subject[0].args).to eq %i[a_first_arg a_second_arg] }
|
26
|
+
it { expect(subject[0].block).to eq block }
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'when call already exists' do
|
30
|
+
let(:args) { [String, :another_method_name, :another_arg] }
|
31
|
+
|
32
|
+
before { repo.add(*common_args, &block) }
|
33
|
+
before { subject }
|
34
|
+
|
35
|
+
it { expect(repo.calls.count).to eq 1 }
|
36
|
+
it { expect(subject.klass).to eq String }
|
37
|
+
it { expect(subject.method_name).to eq :another_method_name }
|
38
|
+
it { expect(subject.args).to eq %i[another_arg] }
|
39
|
+
it { expect(subject.block).to eq block }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'method_decorator/repositories/decorations_repository'
|
5
|
+
|
6
|
+
describe MethodDecorator::Repositories::DecorationsRepository do
|
7
|
+
let(:repo) { described_class.singleton }
|
8
|
+
let(:method_instance) { String.instance_method(:to_s) }
|
9
|
+
|
10
|
+
before { described_class.singleton.decorations = [] }
|
11
|
+
|
12
|
+
describe '#add!' do
|
13
|
+
let(:args) { [String, :method_name, method_instance] }
|
14
|
+
let(:block) { proc {} }
|
15
|
+
|
16
|
+
subject { repo.add!(*args, &block) }
|
17
|
+
|
18
|
+
context 'when decoration not exists' do
|
19
|
+
it { is_expected.to be_truthy }
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'when decoration already exists' do
|
23
|
+
before { repo.add!(*args, &block) }
|
24
|
+
it { expect { subject }.to raise_error(MethodDecorator::Errors::AlreadyDecoratedError) }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/spec/spec_helper.rb
CHANGED
data/spec/support/dummy_class.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
class DummyClass
|
3
4
|
def a_public_instance_method(arg)
|
4
5
|
puts "a_public_instance_method_arg: #{arg}"
|
5
6
|
end
|
@@ -25,7 +26,6 @@ class DummyClass
|
|
25
26
|
end
|
26
27
|
|
27
28
|
class << self
|
28
|
-
|
29
29
|
def a_public_singleton_method(arg)
|
30
30
|
puts "a_public_singleton_method_arg: #{arg}"
|
31
31
|
end
|
@@ -52,7 +52,6 @@ class DummyClass
|
|
52
52
|
puts "another_decorated_public_singleton_method_with: #{call_args}"
|
53
53
|
call_original
|
54
54
|
end
|
55
|
-
|
56
55
|
end
|
57
56
|
|
58
57
|
include MethodDecorator
|
@@ -61,7 +60,6 @@ class DummyClass
|
|
61
60
|
puts "another_decorated_public_instance_method_with: #{call_args}"
|
62
61
|
call_original
|
63
62
|
end
|
64
|
-
|
65
63
|
end
|
66
64
|
|
67
65
|
MethodDecorator.decorate DummyClass, :a_public_instance_method do
|
@@ -97,4 +95,4 @@ end
|
|
97
95
|
MethodDecorator.decorate DummyClass.singleton_class, :a_private_singleton_method do
|
98
96
|
puts "a_decorated_private_singleton_method_with: #{call_args}"
|
99
97
|
call_original
|
100
|
-
end
|
98
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: method_decorator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- r4z3c
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-07-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -66,7 +66,63 @@ dependencies:
|
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
-
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: codeclimate-test-reporter
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rake
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: byebug
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
description: Provides a way to dynamically override methods without losing original
|
70
126
|
behavior
|
71
127
|
email:
|
72
128
|
- r4z3c.43@gmail.com
|
@@ -75,17 +131,30 @@ extensions: []
|
|
75
131
|
extra_rdoc_files: []
|
76
132
|
files:
|
77
133
|
- ".gitignore"
|
134
|
+
- ".rubocop.yml"
|
78
135
|
- ".ruby-version"
|
136
|
+
- ".travis.yml"
|
79
137
|
- Gemfile
|
80
138
|
- MIT-LICENSE
|
81
139
|
- README.rdoc
|
82
140
|
- Rakefile
|
141
|
+
- bin/console
|
83
142
|
- lib/method_decorator.rb
|
84
|
-
- lib/method_decorator/
|
85
|
-
- lib/method_decorator/
|
143
|
+
- lib/method_decorator/errors/already_decorated_error.rb
|
144
|
+
- lib/method_decorator/models/call.rb
|
145
|
+
- lib/method_decorator/models/decoration.rb
|
146
|
+
- lib/method_decorator/models/model.rb
|
147
|
+
- lib/method_decorator/repositories/calls_repository.rb
|
148
|
+
- lib/method_decorator/repositories/decorations_repository.rb
|
149
|
+
- lib/method_decorator/services/decorations_service.rb
|
86
150
|
- lib/method_decorator/version.rb
|
87
151
|
- method_decorator.gemspec
|
88
152
|
- spec/lib/method_decorator_spec.rb
|
153
|
+
- spec/lib/models/call_spec.rb
|
154
|
+
- spec/lib/models/decoration_spec.rb
|
155
|
+
- spec/lib/models/model_spec.rb
|
156
|
+
- spec/lib/repositories/calls_repository_spec.rb
|
157
|
+
- spec/lib/repositories/decorations_repository_spec.rb
|
89
158
|
- spec/spec_helper.rb
|
90
159
|
- spec/support/dummy_class.rb
|
91
160
|
homepage: https://github.com/r4z3c/method_decorator.git
|
@@ -108,11 +177,16 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
108
177
|
version: '0'
|
109
178
|
requirements: []
|
110
179
|
rubyforge_project:
|
111
|
-
rubygems_version: 2.
|
180
|
+
rubygems_version: 2.6.12
|
112
181
|
signing_key:
|
113
182
|
specification_version: 4
|
114
|
-
summary:
|
183
|
+
summary: Override methods preserving the original behavior
|
115
184
|
test_files:
|
116
185
|
- spec/lib/method_decorator_spec.rb
|
186
|
+
- spec/lib/models/call_spec.rb
|
187
|
+
- spec/lib/models/decoration_spec.rb
|
188
|
+
- spec/lib/models/model_spec.rb
|
189
|
+
- spec/lib/repositories/calls_repository_spec.rb
|
190
|
+
- spec/lib/repositories/decorations_repository_spec.rb
|
117
191
|
- spec/spec_helper.rb
|
118
192
|
- spec/support/dummy_class.rb
|
@@ -1,14 +0,0 @@
|
|
1
|
-
module MethodDecorator
|
2
|
-
class Decoration
|
3
|
-
|
4
|
-
attr_accessor :target_class, :target_method_name, :target_method, :decoration
|
5
|
-
|
6
|
-
def initialize(target_class, target_method_name, target_method)
|
7
|
-
self.target_class = target_class
|
8
|
-
self.target_method_name = target_method_name
|
9
|
-
self.target_method = target_method
|
10
|
-
self.decoration = block_given? ? Proc.new : nil
|
11
|
-
end
|
12
|
-
|
13
|
-
end
|
14
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
require 'method_decorator/decoration'
|
2
|
-
|
3
|
-
module MethodDecorator
|
4
|
-
class Repository
|
5
|
-
class << self
|
6
|
-
|
7
|
-
attr_writer :decorations
|
8
|
-
|
9
|
-
def add(target_class, target_method_name, target_method, &decoration)
|
10
|
-
exists = original_target_method_of target_class, target_method_name
|
11
|
-
|
12
|
-
self.decorations.push(
|
13
|
-
Decoration.new target_class, target_method_name, target_method, &decoration
|
14
|
-
) unless exists
|
15
|
-
|
16
|
-
not exists
|
17
|
-
end
|
18
|
-
|
19
|
-
def original_target_method_of(target_class, target_method_name)
|
20
|
-
decoration = self.decorations.select do
|
21
|
-
|d| d.target_class.eql?(target_class) and d.target_method_name.eql?(target_method_name)
|
22
|
-
end.first
|
23
|
-
|
24
|
-
decoration ? decoration.target_method : nil
|
25
|
-
end
|
26
|
-
|
27
|
-
def decorations; @decorations ||= [] end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|