adornable 1.2.1 → 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: 2b36a2ad558b19305d75b4d59c81b93574b2d4635a267d00cb97bbd26e218e76
4
- data.tar.gz: 7e23c912633d929ae70078e8d10ec286d379196e5e59bb27f877764494cce4a0
3
+ metadata.gz: f70e9c6dc00ff446a979d776bb8effbe0063b8e3cb1269081fe3dd78749b1a6d
4
+ data.tar.gz: dd2ee8ef02b67f5c1c48f83d3f04e9869bac1705c8eb12101c5e90e06274e6d8
5
5
  SHA512:
6
- metadata.gz: 3a8b0dfb923e8f997e24ca6636de35d8f5f432f0f13143ff5bdd108b0c0ae6d9a74b42c5f4d2da59576ce8c9724918ed4ad8d20b7dae25347807192acb7a11e6
7
- data.tar.gz: 4e5df5251a5ca785fca8ce87bf849e29234c514951012dde337dd865ea823d1abe50f59c085eaf10c95d6dabcd0a317f0c08ed9c2b7de0a2bd84a5feeb11911e
6
+ metadata.gz: 4cc9f4866c9e130ca8fc7d8cf2988a43c252604bf9030b919526d289badc3554d91c2eb4f825462ff6ac491f0f6519aaacff2e2bdd5c5c8a16dda33bd865c735
7
+ data.tar.gz: 1ecc141ccfff6820a76811bef1427ff90ca4072c35690b732dfbae24a45c6f7c901b8aa83e8172e57d69ad3eca84a50736f8835cfaf3339b56b980a1b72dd1c6
@@ -21,6 +21,9 @@ jobs:
21
21
  strategy:
22
22
  matrix:
23
23
  ruby-version:
24
+ - 3.2
25
+ - 3.1
26
+ - 3.0
24
27
  - 2.7
25
28
  - 2.6
26
29
  - 2.5
data/.rubocop.yml CHANGED
@@ -7,6 +7,7 @@ require:
7
7
 
8
8
  AllCops:
9
9
  NewCops: enable
10
+ TargetRubyVersion: 2.5
10
11
 
11
12
  # Gemspec
12
13
 
@@ -29,6 +30,7 @@ Layout/FirstArrayElementIndentation:
29
30
  # Metrics
30
31
 
31
32
  Metrics/AbcSize:
33
+ Max: 20
32
34
  CountRepeatedAttributes: false
33
35
  Exclude:
34
36
  - 'spec/**/*_spec.rb'
@@ -65,10 +67,13 @@ Metrics/ModuleLength:
65
67
  Exclude:
66
68
  - 'spec/**/*_spec.rb'
67
69
 
70
+ Metrics/ParameterLists:
71
+ CountKeywordArgs: false
72
+
68
73
  # Rspec
69
74
 
70
75
  RSpec/ExampleLength:
71
- Max: 30
76
+ Max: 40
72
77
 
73
78
  RSpec/MessageSpies:
74
79
  Enabled: false
data/Gemfile CHANGED
@@ -9,6 +9,7 @@ gemspec
9
9
 
10
10
  group :development, :test do
11
11
  gem "bundler", "~> 2.2"
12
+ gem "pry"
12
13
  gem "rake", "~> 13.0"
13
14
  gem "rspec", "~> 3.0"
14
15
  gem "rubocop", "~> 1.10"
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
 
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.
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.1"
4
+ VERSION = "1.3.0"
5
5
  end
data/lib/adornable.rb CHANGED
@@ -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,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: adornable
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
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: 2023-07-07 00:00:00.000000000 Z
11
+ date: 2023-07-10 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Adornable provides the ability to cleanly decorate methods in Ruby. You
14
14
  can make and use your own decorators, and you can also use some of the built-in
@@ -33,6 +33,7 @@ files:
33
33
  - README.md
34
34
  - Rakefile
35
35
  - adornable.gemspec
36
+ - bin/asdf_switch
36
37
  - bin/console
37
38
  - bin/setup
38
39
  - lib/adornable.rb
@@ -55,14 +56,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
55
56
  requirements:
56
57
  - - ">="
57
58
  - !ruby/object:Gem::Version
58
- version: 2.4.7
59
+ version: 2.5.0
59
60
  required_rubygems_version: !ruby/object:Gem::Requirement
60
61
  requirements:
61
62
  - - ">="
62
63
  - !ruby/object:Gem::Version
63
64
  version: '0'
64
65
  requirements: []
65
- rubygems_version: 3.1.6
66
+ rubygems_version: 3.4.1
66
67
  signing_key:
67
68
  specification_version: 4
68
69
  summary: Method decorators for Ruby