adornable 1.2.1 → 1.3.0

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: 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