adornable 1.2.0 → 1.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dc81717998b4dc097647c96e4b7c89664b2d0c6969100dd22d713ba9c1720619
4
- data.tar.gz: a3998df4caeb6db3823a218fc77ac3d1d56949e3f9a66cc340f65465d9037686
3
+ metadata.gz: f70e9c6dc00ff446a979d776bb8effbe0063b8e3cb1269081fe3dd78749b1a6d
4
+ data.tar.gz: dd2ee8ef02b67f5c1c48f83d3f04e9869bac1705c8eb12101c5e90e06274e6d8
5
5
  SHA512:
6
- metadata.gz: 4aaf8fe2928bb4652dc5a7b1f235fadc7114580cfc29d91a9fdf508edbba15cccc09627b6a097552774c73babede934ca77a9388fa7ce1bfdbfb5975ab0661da
7
- data.tar.gz: cc2ec1780014bfb5043a44857a2d9fc6eb7957ec245fef03accbe64436c11cc9f2596105d25d5e888b136ca27d57565ec414f9fbb466f72fc425744d6fe69af2
6
+ metadata.gz: 4cc9f4866c9e130ca8fc7d8cf2988a43c252604bf9030b919526d289badc3554d91c2eb4f825462ff6ac491f0f6519aaacff2e2bdd5c5c8a16dda33bd865c735
7
+ data.tar.gz: 1ecc141ccfff6820a76811bef1427ff90ca4072c35690b732dfbae24a45c6f7c901b8aa83e8172e57d69ad3eca84a50736f8835cfaf3339b56b980a1b72dd1c6
@@ -0,0 +1,48 @@
1
+ name: Test and lint
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ pull_request:
9
+ branches:
10
+ - main
11
+
12
+ # Allows you to run this workflow manually from the Actions tab
13
+ workflow_dispatch:
14
+
15
+ jobs:
16
+ build:
17
+ name: Run RSpec tests and RuboCop lints
18
+
19
+ runs-on: ubuntu-latest
20
+
21
+ strategy:
22
+ matrix:
23
+ ruby-version:
24
+ - 3.2
25
+ - 3.1
26
+ - 3.0
27
+ - 2.7
28
+ - 2.6
29
+ - 2.5
30
+
31
+ steps:
32
+ - name: Checkout the repo
33
+ uses: actions/checkout@v2
34
+
35
+ - name: Set up Ruby v${{ matrix.ruby-version }}
36
+ uses: ruby/setup-ruby@v1
37
+ with:
38
+ ruby-version: ${{ matrix.ruby-version }}
39
+ bundler-cache: true
40
+
41
+ - name: Install dependencies
42
+ run: bundle install
43
+
44
+ - name: Run RSpec tests
45
+ run: bundle exec rake spec
46
+
47
+ - name: Run RuboCop lints
48
+ run: bundle exec rubocop
data/.rubocop.yml CHANGED
@@ -7,6 +7,12 @@ require:
7
7
 
8
8
  AllCops:
9
9
  NewCops: enable
10
+ TargetRubyVersion: 2.5
11
+
12
+ # Gemspec
13
+
14
+ Gemspec/RequiredRubyVersion:
15
+ Enabled: false
10
16
 
11
17
  # Layout
12
18
 
@@ -24,6 +30,7 @@ Layout/FirstArrayElementIndentation:
24
30
  # Metrics
25
31
 
26
32
  Metrics/AbcSize:
33
+ Max: 20
27
34
  CountRepeatedAttributes: false
28
35
  Exclude:
29
36
  - 'spec/**/*_spec.rb'
@@ -60,10 +67,13 @@ Metrics/ModuleLength:
60
67
  Exclude:
61
68
  - 'spec/**/*_spec.rb'
62
69
 
70
+ Metrics/ParameterLists:
71
+ CountKeywordArgs: false
72
+
63
73
  # Rspec
64
74
 
65
75
  RSpec/ExampleLength:
66
- Max: 25
76
+ Max: 40
67
77
 
68
78
  RSpec/MessageSpies:
69
79
  Enabled: false
data/Gemfile CHANGED
@@ -6,3 +6,15 @@ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
6
 
7
7
  # Specify your gem's dependencies in adornable.gemspec
8
8
  gemspec
9
+
10
+ group :development, :test do
11
+ gem "bundler", "~> 2.2"
12
+ gem "pry"
13
+ gem "rake", "~> 13.0"
14
+ gem "rspec", "~> 3.0"
15
+ gem "rubocop", "~> 1.10"
16
+ gem "rubocop-performance", "~> 1.9"
17
+ gem "rubocop-rake", "~> 0.5"
18
+ gem "rubocop-rspec", "~> 2.2"
19
+ gem "solargraph"
20
+ end
data/README.md CHANGED
@@ -4,6 +4,8 @@ Adornable provides the ability to cleanly decorate methods in Ruby. You can make
4
4
 
5
5
  ## Installation
6
6
 
7
+ **NOTE:** This library is tested with Ruby versions 2.5.x through 3.2.x.
8
+
7
9
  ### Locally (to your application)
8
10
 
9
11
  Add the gem to your application's `Gemfile`:
@@ -232,7 +234,9 @@ The **required argument** is an instance of `Adornable::Context`, which has some
232
234
 
233
235
  - `Adornable::Context#method_name`: the name of the decorated method being called (a symbol; e.g., `:some_method` or `:other_method`)
234
236
  - `Adornable::Context#method_receiver`: the actual object that the decorated method (the `#method_name`) belongs to/is being called on (an object/class; e.g., the class `Foo` if it's a decorated class method, or an instance of `Foo` if it's a decorated instance method)
235
- - `Adornable::Context#method_arguments`: an array of arguments passed to the decorated method, including keyword arguments as a final hash (e.g., if `:yet_another_method` was called like `Foo.new.yet_another_method(123, bar: true)` then `arguments` would be `[123, {:bar=>true}]`)
237
+ - `Adornable::Context#method_arguments`: an array of arguments passed to the decorated method, including keyword arguments as a final hash (e.g., if `:yet_another_method` was called like `Foo.new.yet_another_method(123, bar: true, baz: 456)` then `method_arguments` would be `[123, {:bar=>true,:baz=>456}]`)
238
+ - `Adornable::Context#method_positional_args`: an array of just the positional arguments passed to the decorated method, excluding keyword arguments (e.g., if `:yet_another_method` was called like `Foo.new.yet_another_method(123, bar: true, baz: 456)` then `method_positional_args` would be `[123]`)
239
+ - `Adornable::Context#method_kwargs`: a hash of just the keyword arguments passed to the decorated method (e.g., if `:yet_another_method` was called like `Foo.new.yet_another_method(123, { bam: "hi" }, bar: true, baz: 456)` then `method_kwargs` would be `{:bar=>true,:baz=>456}`)
236
240
 
237
241
  ##### Custom keyword arguments (optional)
238
242
 
@@ -351,12 +355,6 @@ rake spec
351
355
  rubocop
352
356
  ```
353
357
 
354
- ### Create release
355
-
356
- ```
357
- rake release
358
- ```
359
-
360
358
  ## Contributing
361
359
 
362
360
  Bug reports and pull requests for this project are welcome at its [GitHub page](https://github.com/kjleitz/adornable). If you choose to contribute, please be nice so I don't have to run out of bubblegum, etc.
data/adornable.gemspec CHANGED
@@ -14,7 +14,7 @@ Gem::Specification.new do |spec|
14
14
  spec.description = "Adornable provides the ability to cleanly decorate methods in Ruby. You can make and use your own decorators, and you can also use some of the built-in ones that the gem provides. _Decorating_ methods is as simple as slapping a `decorate :some_decorator` above your method definition. _Defining_ decorators can be as simple as defining a method that yields to a block, or as complex as manipulating the decorated method's receiver and arguments, and/or changing the functionality of the decorator based on custom options supplied to it when initially applying the decorator." # rubocop:disable Layout/LineLength
15
15
  spec.homepage = "https://github.com/kjleitz/adornable"
16
16
  spec.license = "MIT"
17
- spec.required_ruby_version = ">= 2.4.7"
17
+ spec.required_ruby_version = ">= 2.5.0"
18
18
 
19
19
  # Specify which files should be added to the gem when it is released.
20
20
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
@@ -24,13 +24,5 @@ Gem::Specification.new do |spec|
24
24
  spec.bindir = "exe"
25
25
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
26
  spec.require_paths = ["lib"]
27
-
28
- spec.add_development_dependency "bundler", "~> 2.2"
29
- spec.add_development_dependency "rake", "~> 13.0"
30
- spec.add_development_dependency "rspec", "~> 3.0"
31
- spec.add_development_dependency "rubocop", "~> 1.10"
32
- spec.add_development_dependency "rubocop-performance", "~> 1.9"
33
- spec.add_development_dependency "rubocop-rake", "~> 0.5"
34
- spec.add_development_dependency "rubocop-rspec", "~> 2.2"
35
- spec.add_development_dependency "solargraph"
27
+ spec.metadata['rubygems_mfa_required'] = 'true'
36
28
  end
data/bin/asdf_switch ADDED
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # First argument should be desired ruby version. If a partial version is given
4
+ # (e.g., 2.6) and multiple versions are found to be installed (e.g., 2.6.9 and
5
+ # 2.6.10), then the highest version (in semver version order) will be selected.
6
+ SELECTED_RUBY_VERSION=$(asdf list ruby | sort -V | sed -e 's/^[[:space:]]*//' | grep "^$1" | tail -n 1)
7
+
8
+ if [ -z "$SELECTED_RUBY_VERSION" ]; then
9
+ echo "Ruby version $1 is not installed. Try running:"
10
+ echo " asdf install ruby $1"
11
+ exit 1
12
+ fi
13
+
14
+ echo "Using Ruby version $SELECTED_RUBY_VERSION"
15
+
16
+ echo "(setting global Ruby)"
17
+ asdf global ruby $SELECTED_RUBY_VERSION
18
+
19
+ echo "(reshimming for this version)"
20
+ asdf reshim ruby $SELECTED_RUBY_VERSION
21
+
22
+ echo "(reinstalling bundled gems)"
23
+ bundle install --redownload
24
+
@@ -8,14 +8,26 @@ module Adornable
8
8
  method_receiver
9
9
  method_name
10
10
  method_arguments
11
+ method_positional_args
12
+ method_kwargs
11
13
  decorator_name
12
14
  decorator_options
13
15
  ])
14
16
 
15
- def initialize(method_receiver:, method_name:, method_arguments:, decorator_name:, decorator_options:)
17
+ def initialize(
18
+ method_receiver:,
19
+ method_name:,
20
+ method_arguments:,
21
+ method_positional_args:,
22
+ method_kwargs:,
23
+ decorator_name:,
24
+ decorator_options:
25
+ )
16
26
  @method_receiver = method_receiver
17
27
  @method_name = method_name
18
28
  @method_arguments = method_arguments
29
+ @method_positional_args = method_positional_args
30
+ @method_kwargs = method_kwargs
19
31
  @decorator_name = decorator_name
20
32
  @decorator_options = decorator_options
21
33
  end
@@ -38,14 +38,14 @@ module Adornable
38
38
  clear_accumulated_decorators!
39
39
  end
40
40
 
41
- def run_decorated_instance_method(bound_method, *args)
41
+ def run_decorated_instance_method(bound_method, *args, **kwargs)
42
42
  decorators = get_instance_method_decorators(bound_method.name)
43
- run_decorators(decorators, bound_method, *args)
43
+ run_decorators(decorators, bound_method, *args, **kwargs)
44
44
  end
45
45
 
46
- def run_decorated_class_method(bound_method, *args)
46
+ def run_decorated_class_method(bound_method, *args, **kwargs)
47
47
  decorators = get_class_method_decorators(bound_method.name)
48
- run_decorators(decorators, bound_method, *args)
48
+ run_decorators(decorators, bound_method, *args, **kwargs)
49
49
  end
50
50
 
51
51
  private
@@ -88,8 +88,10 @@ module Adornable
88
88
  @class_method_decorators[name] = decorators || []
89
89
  end
90
90
 
91
- def run_decorators(decorators, bound_method, *method_arguments)
92
- return bound_method.call(*method_arguments) if Adornable::Utils.blank?(decorators)
91
+ def run_decorators(decorators, bound_method, *method_positional_args, **method_kwargs)
92
+ if Adornable::Utils.blank?(decorators)
93
+ return Adornable::Utils.empty_aware_send(bound_method, :call, method_positional_args, method_kwargs)
94
+ end
93
95
 
94
96
  decorator, *remaining_decorators = decorators
95
97
  decorator_name = decorator[:name]
@@ -97,22 +99,30 @@ module Adornable
97
99
  decorator_options = decorator[:options]
98
100
  validate_decorator!(decorator_name, decorator_receiver, bound_method)
99
101
 
102
+ # This is for backwards-compatibility between Ruby 2.x and Ruby 3.x; in v3
103
+ # keyword arguments are treated differently than in v2 with respect to
104
+ # hash parameter equivalency. Previously, it was easy to just assume
105
+ # `method_arguments` could be an array with a hash at the end representing
106
+ # any given keyword arguments. However, in Ruby 3.x, we have to be able to
107
+ # distinguish between kwargs and trailing positional args of type `Hash`,
108
+ # so we'll shim `Adornable::Context#method_arguments` to look like it used
109
+ # to and then provide two new properties, `#method_positional_args` and
110
+ # `#method_kwargs`, to `Adornable::Context` for explicitness.
111
+ method_arguments = method_positional_args.dup
112
+ method_arguments << method_kwargs if Adornable::Utils.present?(method_kwargs)
113
+
100
114
  context = Adornable::Context.new(
101
115
  method_receiver: bound_method.receiver,
102
116
  method_name: bound_method.name,
103
117
  method_arguments: method_arguments,
118
+ method_positional_args: method_positional_args,
119
+ method_kwargs: method_kwargs,
104
120
  decorator_name: decorator_name,
105
121
  decorator_options: decorator_options,
106
122
  )
107
123
 
108
- send_parameters = if Adornable::Utils.present?(decorator_options)
109
- [decorator_name, context, decorator_options]
110
- else
111
- [decorator_name, context]
112
- end
113
-
114
- decorator_receiver.send(*send_parameters) do
115
- run_decorators(remaining_decorators, bound_method, *method_arguments)
124
+ Adornable::Utils.empty_aware_send(decorator_receiver, decorator_name, [context], decorator_options) do
125
+ run_decorators(remaining_decorators, bound_method, *method_positional_args, **method_kwargs)
116
126
  end
117
127
  end
118
128
 
@@ -23,6 +23,29 @@ module Adornable
23
23
  end
24
24
  "`#{receiver_name}#{name_delimiter}#{method_name}`"
25
25
  end
26
+
27
+ # This craziness is here because Ruby 2.6 and below don't like when you
28
+ # pass even _empty_ arguments to `#call` or `#send` or any other method
29
+ # with a splat, for callables that take no arguments. For example, this
30
+ # takes the place of:
31
+ #
32
+ # receiver.send(method_name, *splat_args, **splat_kwargs)
33
+ #
34
+ # ...or:
35
+ #
36
+ # receiver.some_method(*splat_args, **splat_kwargs)
37
+ #
38
+ # ...which is not cool <= 2.6.x apparently, if `#some_method` takes zero
39
+ # arguments even if both `splat_args` and `splat_kwargs` are empty (thus
40
+ # passing it zero arguments in actuality). Oh well.
41
+ #
42
+ def empty_aware_send(receiver, method_name, splat_args, splat_kwargs, &block)
43
+ return receiver.send(method_name, &block) if splat_args.empty? && splat_kwargs.empty?
44
+ return receiver.send(method_name, *splat_args, &block) if splat_kwargs.empty?
45
+ return receiver.send(method_name, **splat_kwargs, &block) if splat_args.empty?
46
+
47
+ receiver.send(method_name, *splat_args, **splat_kwargs, &block)
48
+ end
26
49
  end
27
50
  end
28
51
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Adornable
4
- VERSION = "1.2.0"
4
+ VERSION = "1.3.0"
5
5
  end
data/lib/adornable.rb CHANGED
@@ -14,7 +14,7 @@ module Adornable
14
14
  end
15
15
 
16
16
  def decorate(decorator_name, from: nil, defer_validation: false, **decorator_options)
17
- if Adornable::Utils.blank?(name)
17
+ if Adornable::Utils.blank?(decorator_name)
18
18
  raise Adornable::Error::InvalidDecoratorArguments, "Decorator name must be provided."
19
19
  end
20
20
 
@@ -36,10 +36,16 @@ module Adornable
36
36
 
37
37
  machinery.apply_accumulated_decorators_to_instance_method!(method_name)
38
38
  original_method = instance_method(method_name)
39
- define_method(method_name) do |*args|
39
+
40
+ # NB: If you only supply `*args` to the block, you get kwargs as a trailing
41
+ # Hash member in the `args` array. If you supply both `*args, **kwargs` to
42
+ # the block, kwargs are excluded from the `args` array and only appear in
43
+ # the `kwargs` argument as a Hash.
44
+ define_method(method_name) do |*args, **kwargs|
40
45
  bound_method = original_method.bind(self)
41
- machinery.run_decorated_instance_method(bound_method, *args)
46
+ machinery.run_decorated_instance_method(bound_method, *args, **kwargs)
42
47
  end
48
+
43
49
  super
44
50
  end
45
51
 
@@ -49,9 +55,15 @@ module Adornable
49
55
 
50
56
  machinery.apply_accumulated_decorators_to_class_method!(method_name)
51
57
  original_method = method(method_name)
52
- define_singleton_method(method_name) do |*args|
53
- machinery.run_decorated_class_method(original_method, *args)
58
+
59
+ # NB: If you only supply `*args` to the block, you get kwargs as a trailing
60
+ # Hash member in the `args` array. If you supply both `*args, **kwargs` to
61
+ # the block, kwargs are excluded from the `args` array and only appear in
62
+ # the `kwargs` argument as a Hash.
63
+ define_singleton_method(method_name) do |*args, **kwargs|
64
+ machinery.run_decorated_class_method(original_method, *args, **kwargs)
54
65
  end
66
+
55
67
  super
56
68
  end
57
69
  end
metadata CHANGED
@@ -1,127 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: adornable
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Keegan Leitz
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-04-19 00:00:00.000000000 Z
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: bundler
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '2.2'
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '2.2'
27
- - !ruby/object:Gem::Dependency
28
- name: rake
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: '13.0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: '13.0'
41
- - !ruby/object:Gem::Dependency
42
- name: rspec
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '3.0'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: '3.0'
55
- - !ruby/object:Gem::Dependency
56
- name: rubocop
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: '1.10'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '1.10'
69
- - !ruby/object:Gem::Dependency
70
- name: rubocop-performance
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: '1.9'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: '1.9'
83
- - !ruby/object:Gem::Dependency
84
- name: rubocop-rake
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - "~>"
88
- - !ruby/object:Gem::Version
89
- version: '0.5'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - "~>"
95
- - !ruby/object:Gem::Version
96
- version: '0.5'
97
- - !ruby/object:Gem::Dependency
98
- name: rubocop-rspec
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - "~>"
102
- - !ruby/object:Gem::Version
103
- version: '2.2'
104
- type: :development
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - "~>"
109
- - !ruby/object:Gem::Version
110
- version: '2.2'
111
- - !ruby/object:Gem::Dependency
112
- name: solargraph
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'
11
+ date: 2023-07-10 00:00:00.000000000 Z
12
+ dependencies: []
125
13
  description: Adornable provides the ability to cleanly decorate methods in Ruby. You
126
14
  can make and use your own decorators, and you can also use some of the built-in
127
15
  ones that the gem provides. _Decorating_ methods is as simple as slapping a `decorate
@@ -135,6 +23,7 @@ executables: []
135
23
  extensions: []
136
24
  extra_rdoc_files: []
137
25
  files:
26
+ - ".github/workflows/main.yml"
138
27
  - ".gitignore"
139
28
  - ".rspec"
140
29
  - ".rubocop.yml"
@@ -144,6 +33,7 @@ files:
144
33
  - README.md
145
34
  - Rakefile
146
35
  - adornable.gemspec
36
+ - bin/asdf_switch
147
37
  - bin/console
148
38
  - bin/setup
149
39
  - lib/adornable.rb
@@ -156,7 +46,8 @@ files:
156
46
  homepage: https://github.com/kjleitz/adornable
157
47
  licenses:
158
48
  - MIT
159
- metadata: {}
49
+ metadata:
50
+ rubygems_mfa_required: 'true'
160
51
  post_install_message:
161
52
  rdoc_options: []
162
53
  require_paths:
@@ -165,14 +56,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
165
56
  requirements:
166
57
  - - ">="
167
58
  - !ruby/object:Gem::Version
168
- version: 2.4.7
59
+ version: 2.5.0
169
60
  required_rubygems_version: !ruby/object:Gem::Requirement
170
61
  requirements:
171
62
  - - ">="
172
63
  - !ruby/object:Gem::Version
173
64
  version: '0'
174
65
  requirements: []
175
- rubygems_version: 3.0.9
66
+ rubygems_version: 3.4.1
176
67
  signing_key:
177
68
  specification_version: 4
178
69
  summary: Method decorators for Ruby