easy_decorator 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9712b4b6313e508e2cff7302f664f5d96fa0bc9e073b82ba6bfb3ec026831e61
4
- data.tar.gz: 9afdeb38a1b34ab102a571f9514ec8167718aadfddd68f8ffd0993c388227c30
3
+ metadata.gz: 982db95e1c4b8144dde9dfec4e93ce757f36dd4b9043a18a6dba47df7991b900
4
+ data.tar.gz: 4a40c620b429dc2475bae31593ea6cc4e923ad66e6e184224806f43c81c8f1c4
5
5
  SHA512:
6
- metadata.gz: 3fd37f90cdda492cd0e17c84d33f6c5c47748ace2e4afbc952129efc37909c82001e34f7e0e47d2a015a19d370626ef361bdd8526c406cd3f46cae5e7e8cb267
7
- data.tar.gz: b0e246be3a9ea5ed7fc80d547aa30aa9ff28a55fdb3d1e11212c6bb5d85f9bd0cfd7e73277e94e0cf11e919cba79e05a78fe03875331d21a6e30338c8e362d62
6
+ metadata.gz: 978d6ec8156a4ef3b2e5dcafb8fdbbc4cbe9b55cb3798f63be80584e80fc5af532f8d366be122396310ddc0695517f4eca35f613c9f403655b68d5c7521d1a43
7
+ data.tar.gz: fe31bf6a967dda3d2db6aa9b6981c4026c3bedb7f6a37bed1c61ef5adba510a2d360d4530e28d4d9418077f2116a69c7ae0a2baaf08b85417f22fdabf6f3ba0a
@@ -3,36 +3,85 @@ require 'easy_decorator/errors'
3
3
  module EasyDecorator
4
4
  def self.included(base_klass)
5
5
  base_klass.extend(ClassMethods)
6
- base_klass.instance_variable_set(:@decorated_methods, {})
6
+ base_klass.instance_variable_set(:@decorators, { method_buffer: [] })
7
7
  base_klass.define_method :wrapper do |&block|
8
8
  block
9
9
  end
10
10
  end
11
11
 
12
12
  module ClassMethods
13
- def decorate(decorated, decorator)
14
- @decorated_methods[decorated] ||= { method_chain: [], is_decorated: false }
15
- @decorated_methods[decorated][:method_chain] << decorator
13
+ def decorate(decorator)
14
+ # get line number of decorator statement and add decorator method to the buffer
15
+ line_num = caller.first.split(':')[1]
16
+ @decorators[:method_buffer].prepend({ decorator: decorator, line: line_num })
17
+ end
18
+
19
+ def decorators
20
+ @decorators
16
21
  end
17
22
 
18
23
  def method_added(method_name)
19
- decorator = @decorated_methods[method_name]
20
-
21
- if decorator && !decorator[:is_decorated]
22
- decorator[:is_decorated] = true
23
-
24
- source_method_name = "source_#{method_name}".to_sym
25
- alias_method source_method_name, method_name
26
- decorator[:method_chain] << source_method_name
27
-
28
- define_method method_name do |*args|
29
- decorator[:method_chain].then { |*funcs, base_func|
30
- funcs.reduce(public_method(base_func)) { |acc, func|
31
- public_method(func).call(acc, *args)
32
- }
33
- }.call(*args)
34
- end
24
+ return if bypass_method_added(method_name)
25
+
26
+ # validate correct syntax
27
+ method_line = caller.first.split(':')[1]
28
+ validate_syntax(method_line)
29
+
30
+ source_method_name = "easy_decorator_#{method_name}".to_sym
31
+ alias_method source_method_name, method_name
32
+ method_chain = build_method_chain(method_name)
33
+ setup_decorated_method(method_name, method_chain)
34
+ end
35
+
36
+ private
37
+
38
+ def bypass_method_added(method_name)
39
+ [
40
+ method_name.to_s.start_with?('easy_decorator_'),
41
+ @decorators.key?(method_name),
42
+ @decorators[:method_buffer].empty?
43
+ ].any?
44
+ end
45
+
46
+ def build_method_chain(method_name)
47
+ validate_decorators(@decorators[:method_buffer])
48
+ @decorators[method_name] = @decorators[:method_buffer]
49
+ @decorators[:method_buffer] = []
50
+ source_method_name = "source_#{method_name}".to_sym
51
+ alias_method source_method_name, method_name
52
+
53
+ @decorators[method_name].map { |i| i[:decorator] } << source_method_name
54
+ end
55
+
56
+ def setup_decorated_method(method_name, method_chain)
57
+ define_method method_name do |*args|
58
+ method_chain.then do |*funcs, base_func|
59
+ funcs.reduce(public_method(base_func)) do |acc, func|
60
+ public_method(func).call(acc, *args)
61
+ end
62
+ end.call(*args)
63
+ end
64
+ end
65
+
66
+ def validate_syntax(method_line)
67
+ # verify decorators are declared directly before the method definition
68
+ line_nums = @decorators[:method_buffer].map { |m| m[:line].to_i }.reverse << method_line.to_i
69
+ is_valid = line_nums.each_cons(2).all? { |i| (i[1] - i[0]).eql? 1 }
70
+
71
+ unless is_valid
72
+ raise EasyDecorator::InvalidSyntax, 'Decorators must be declared on line(s) directly before method definition'
35
73
  end
74
+
75
+ is_valid
76
+ end
77
+
78
+ def validate_decorators(method_buffer)
79
+ invalid_methods = method_buffer.filter { |m| !method_defined? m[:decorator] }
80
+ is_valid = invalid_methods.empty?
81
+
82
+ raise(EasyDecorator::InvalidDecorator, "Decorator(s) not defined:\n\t#{invalid_methods}") unless is_valid
83
+
84
+ is_valid
36
85
  end
37
86
  end
38
87
  end
@@ -1,3 +1,5 @@
1
1
  module EasyDecorator
2
2
  class Error < StandardError; end
3
+ class InvalidSyntax < StandardError; end
4
+ class InvalidDecorator < StandardError; end
3
5
  end
@@ -1,3 +1,3 @@
1
1
  module EasyDecorator
2
- VERSION = '0.2.0'
2
+ VERSION = '0.2.1'.freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: easy_decorator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Crank
@@ -11,7 +11,7 @@ cert_chain: []
11
11
  date: 2020-10-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: pry
14
+ name: byebug
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
@@ -38,17 +38,54 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubocop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop-rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
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'
41
83
  description: Bring in Python's decorator pattern for Ruby methods.
42
84
  email: joshuatcrank@gmail.com
43
85
  executables: []
44
86
  extensions: []
45
87
  extra_rdoc_files: []
46
88
  files:
47
- - ".gitignore"
48
- - Gemfile
49
- - LICENSE
50
- - README.md
51
- - easy_decorator.gemspec
52
89
  - lib/decorators/decorators.rb
53
90
  - lib/easy_decorator.rb
54
91
  - lib/easy_decorator/errors.rb
data/.gitignore DELETED
@@ -1,56 +0,0 @@
1
- *.gem
2
- *.rbc
3
- /.config
4
- /coverage/
5
- /InstalledFiles
6
- /pkg/
7
- /spec/reports/
8
- /spec/examples.txt
9
- /test/tmp/
10
- /test/version_tmp/
11
- /tmp/
12
-
13
- # Used by dotenv library to load environment variables.
14
- # .env
15
-
16
- # Ignore Byebug command history file.
17
- .byebug_history
18
-
19
- ## Specific to RubyMotion:
20
- .dat*
21
- .repl_history
22
- build/
23
- *.bridgesupport
24
- build-iPhoneOS/
25
- build-iPhoneSimulator/
26
-
27
- ## Specific to RubyMotion (use of CocoaPods):
28
- #
29
- # We recommend against adding the Pods directory to your .gitignore. However
30
- # you should judge for yourself, the pros and cons are mentioned at:
31
- # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
32
- #
33
- # vendor/Pods/
34
-
35
- ## Documentation cache and generated files:
36
- /.yardoc/
37
- /_yardoc/
38
- /doc/
39
- /rdoc/
40
-
41
- ## Environment normalization:
42
- /.bundle/
43
- /vendor/bundle
44
- /lib/bundler/man/
45
-
46
- # for a library or gem, you might want to ignore these files since the code is
47
- # intended to run in multiple environments; otherwise, check them in:
48
- Gemfile.lock
49
- .ruby-version
50
- # .ruby-gemset
51
-
52
- # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
53
- .rvmrc
54
-
55
- # Used by RuboCop. Remote config files pulled in from inherit_from directive.
56
- # .rubocop-https?--*
data/Gemfile DELETED
@@ -1,3 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec
data/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2020 Josh Crank
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
data/README.md DELETED
@@ -1,114 +0,0 @@
1
- # EasyDecorator
2
-
3
- EasyDecorator is a module that bring's in a Python-like method decorator pattern into Ruby.
4
-
5
- # Table of Contents
6
-
7
- * [Installation](#installation)
8
- * [Usage](#usage)
9
- * [Include Module](#include-module)
10
- * [Define Decorator](#define-decorator)
11
- * [Decorating Methods](#decorating-methods)
12
- * [Example](#example)
13
- * [Contributing](#contributing)
14
-
15
- # Installation
16
- ### command line
17
- `$ gem install easy_decorator`
18
-
19
- ### Gemfile
20
- ```ruby
21
- # ./Gemfile
22
-
23
- source 'https://rubygems.org`
24
- # ...
25
- gem 'easy_decorator', '~> 0.2.0'
26
- ```
27
- # Usage
28
- ### Include Module
29
- ```ruby
30
- # ./my_class.rb
31
- class MyClass
32
- include EasyDecorator
33
- end
34
- ```
35
- ### Define Decorator
36
- A decorator should be defined as a method with an inner wrapper. Within the wrapper you can call the passed method via `func.call(*args)`.
37
-
38
- ```ruby
39
- def my_decorator(func, *args)
40
- wrapper do
41
- # code here
42
- func.call(*args)
43
- # code here
44
- end
45
- end
46
- ```
47
- \* `wrapper` is essentially syntactic sugar for a proc.
48
-
49
- ### Decorating Methods
50
- ```ruby
51
- decorate(:my_method, :my_decorator)
52
- def my_method(a, b)
53
- # code here
54
- end
55
- ```
56
- You can apply multiple decorators to a method, which will applies from last to first declared.
57
-
58
- ```ruby
59
- decorate(:my_method, :outer_decorator)
60
- decorate(:my_method, :inner_decorator)
61
- def my_method(*args)
62
- # ...
63
- end
64
-
65
- # is essentially the same as:
66
- outer_decorator(inner_decorator(public_method(:my_method))).call(*args)
67
- ```
68
-
69
- ### Example
70
- ```ruby
71
- # ./calculator.rb
72
-
73
- class Calculator
74
- # include module
75
- include EasyDecorator
76
-
77
- # define decorator
78
- def calculate_time(func, *args)
79
- wrap do
80
- logger.info("Timing #{method_name}...")
81
- start_time = Time.now
82
- # call inner method
83
- result = func.call(*args)
84
- end_time = Time.now
85
- logger.info("Processing Time: #{end_time - start_time}")
86
-
87
- result
88
- end
89
- end
90
-
91
- # decorate method
92
- decorate(:add_numbers, :calculate_time)
93
- def add_numbers(a, b)
94
- return a + b
95
- end
96
- end
97
- ```
98
- Result:
99
- ```
100
- $ Calculator.new.add_numbers(1,2)
101
- Timing add_numbers...
102
- Processing Time: 0.0001572930
103
- => 3
104
- ```
105
-
106
- # Contributing
107
- 1. Fork the repo
108
- 2. Create a your feature branch (`git checkout -b my-feature-branch`)
109
- 3. Update CHANGELOG.md with a bulleted list of your changes under the `unreleased` heading.
110
- 4. Include rspec tests for your changes
111
- 5. Commit your changes to your branch (`git commit -am 'Added my feature'`)
112
- 6. Push to your remote forked repo (`git push origin my-featuer-branch`)
113
- 7. Create a new Pull Request
114
- Once I am able to review the pull request, I will either either approve and merge, or give feedback on it if I do not merge it. I will do my best to address Pull Requests as time allows.
@@ -1,30 +0,0 @@
1
- require_relative 'lib/easy_decorator/version'
2
-
3
- Gem::Specification.new do |gem|
4
- gem.name = 'easy_decorator'
5
- gem.version = EasyDecorator::VERSION
6
- gem.date = '2020-10-13'
7
- gem.authors = ['Josh Crank']
8
- gem.email = 'joshuatcrank@gmail.com'
9
-
10
- gem.summary = %q{A method decorator for Ruby.}
11
- gem.description = %q{Bring in Python's decorator pattern for Ruby methods.}
12
- gem.homepage = 'https://github.com/jtcrank/easy_decorator'
13
- gem.license = 'MIT'
14
- gem.required_ruby_version = Gem::Requirement.new(">=2.6")
15
-
16
- gem.metadata['homepage_uri'] = gem.homepage
17
- gem.metadata['source_code_uri'] = gem.homepage
18
- gem.metadata['changelog_uri'] = gem.homepage
19
-
20
- gem.files = Dir.chdir(File.expand_path('..', __FILE__)) do
21
- `git ls-files -z`.split("\x0").reject { |f|
22
- f.match(%r{^(test|spec|features)/})
23
- }
24
- end
25
-
26
- gem.require_paths = ['lib']
27
- gem.add_development_dependency 'pry'
28
- gem.add_development_dependency 'rspec'
29
-
30
- end