defi 3.0.0 → 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/LICENSE.md +1 -1
- data/README.md +40 -54
- data/lib/defi/challenge.rb +71 -0
- data/lib/defi/value.rb +20 -78
- data/lib/defi.rb +10 -23
- metadata +16 -12
- data/lib/defi/method.rb +0 -99
- data/lib/kernel.rb +0 -42
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 147abcfe45309cf8e5e5df3cf02dbcaeaccd2c421841ab017844206dd1218ed1
|
|
4
|
+
data.tar.gz: 8d82cceb4e3e68f29ee6ae8632369cf9c4d2bdfdc3d3ba1a1cd3de5659cad8bc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9c0076455aa06379af57797aff1c160fce9bd1d930f45702a95eea4836b0c77270f9bcdac8cb4f00b4c5fe364766cb0a8a07b3f4c0800606366df9408debe882
|
|
7
|
+
data.tar.gz: '0379b004d430b65a97b2f56d2a968457ec37a41510a08f253899060e7645937b7c29e234e2d6a9bbca75b991edde71485ade15d64cb0f61a7b348fd750d2e092'
|
data/LICENSE.md
CHANGED
data/README.md
CHANGED
|
@@ -2,92 +2,78 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/fixrb/defi/tags)
|
|
4
4
|
[](https://rubydoc.info/github/fixrb/defi/main)
|
|
5
|
-
[](https://github.com/fixrb/defi/actions?query=workflow%3Aruby+branch%3Amain)
|
|
6
|
-
[](https://github.com/fixrb/defi/actions?query=workflow%3Arubocop+branch%3Amain)
|
|
7
5
|
[](https://github.com/fixrb/defi/raw/main/LICENSE.md)
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
Rather than representing method signatures themselves, Defi focuses on providing a way to furnish methods with appropriate arguments, thereby preparing them for invocation.
|
|
11
|
-
|
|
12
|
-
This approach serves as an alternative to traditional methods like `Object#method` and `UnboundMethod`.
|
|
13
|
-
Where `Object#method` is bound to a specific object and `UnboundMethod` requires compatibility with the method’s originating class, Defi offers a more flexible and universal way to prepare method calls.
|
|
14
|
-
It allows method arguments to be specified in advance and then applied to any compatible object – those equipped with corresponding methods.
|
|
15
|
-
|
|
16
|
-
Defi is particularly useful in scenarios where you need to apply a set of method arguments across different objects to observe the varying outcomes, whether they be returned values or exceptions.
|
|
17
|
-
This makes it an ideal tool for testing, method comparison across different implementations, or any situation where method behavior needs to be assessed dynamically across various objects.
|
|
7
|
+
Defi prepares a method call, a name with its arguments, and sends it to any object, keeping what came back: the value returned, or the exception raised. The same call can be sent to as many objects as you like, to compare what each one does with it.
|
|
18
8
|
|
|
19
9
|
## Installation
|
|
20
10
|
|
|
21
|
-
Add this line to your application's Gemfile:
|
|
22
|
-
|
|
23
11
|
```ruby
|
|
12
|
+
# Gemfile
|
|
24
13
|
gem "defi"
|
|
25
14
|
```
|
|
26
15
|
|
|
27
|
-
|
|
16
|
+
Defi requires Ruby 3.3 or later, and depends on nothing.
|
|
28
17
|
|
|
29
|
-
|
|
30
|
-
bundle install
|
|
31
|
-
```
|
|
18
|
+
## Usage
|
|
32
19
|
|
|
33
|
-
|
|
20
|
+
A challenge is a method name, with positional arguments, keyword arguments and a block, as you would write them after a dot:
|
|
34
21
|
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
```
|
|
22
|
+
```ruby
|
|
23
|
+
require "defi"
|
|
38
24
|
|
|
39
|
-
|
|
25
|
+
challenge = Defi::Challenge.new(:fetch, :b, 0)
|
|
26
|
+
challenge.to_s # => ".fetch(:b, 0)"
|
|
27
|
+
```
|
|
40
28
|
|
|
41
|
-
|
|
42
|
-
Below are some detailed examples to demonstrate its versatility and ease of use:
|
|
29
|
+
Sent to an object with `to`, it brings back a value: what the method returned, or what it raised.
|
|
43
30
|
|
|
44
|
-
|
|
31
|
+
```ruby
|
|
32
|
+
value = challenge.to({ b: 2 })
|
|
33
|
+
value.raised? # => false
|
|
34
|
+
value.object # => 2
|
|
35
|
+
value.call # => 2
|
|
36
|
+
|
|
37
|
+
value = Defi::Challenge.new(:fetch, :b).to({})
|
|
38
|
+
value.raised? # => true
|
|
39
|
+
value.object # => #<KeyError: key not found: :b>
|
|
40
|
+
value.call # raises the KeyError again
|
|
41
|
+
```
|
|
45
42
|
|
|
46
|
-
|
|
47
|
-
With `Defi`, this can be elegantly done as follows:
|
|
43
|
+
Keyword arguments and the block go through as they are:
|
|
48
44
|
|
|
49
45
|
```ruby
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
Defi::Challenge.new(:encode, "US-ASCII", undef: :replace).to("café").object # => "caf?"
|
|
47
|
+
Defi::Challenge.new(:map) { |n| n * 2 }.to([1, 2]).object # => [2, 4]
|
|
52
48
|
```
|
|
53
49
|
|
|
54
|
-
|
|
55
|
-
The `.to(6)` method specifies that this challenge should be applied to the number `6`.
|
|
56
|
-
Finally, `.call` executes the challenge, yielding the result `42`.
|
|
50
|
+
## What comes back
|
|
57
51
|
|
|
58
|
-
|
|
52
|
+
The message is sent with `public_send`: a private method, or a `BasicObject`, which has no `public_send`, gives a `NoMethodError`. `public_send` protects the named method only: a challenge of `send` or `instance_eval`, which are public, reaches what they reach.
|
|
59
53
|
|
|
60
|
-
`
|
|
61
|
-
For instance:
|
|
54
|
+
A challenge keeps as its value the exceptions a method raises on its own: a `StandardError`, a `ScriptError` (such as `NotImplementedError`), a `SystemStackError`, or the `SystemExit` of an `exit` or an `abort`. What is not the method's doing goes through: an `Interrupt`, a `SignalException`, a `NoMemoryError`, the exception a `Timeout` uses to end its block (a `Timeout` given a class to raise, such as `RuntimeError`, is kept like any `StandardError`), and any other subclass of `Exception`. `Defi::Value::RAISED` lists the classes kept. `call` raises the exception again, the same object, with its own cause.
|
|
62
55
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
rescue NoMethodError => e
|
|
67
|
-
puts e.message # Output: undefined method `boom' for "foo":String
|
|
68
|
-
end
|
|
69
|
-
```
|
|
56
|
+
`to_s` writes the call as `.name(1, key: 2, &block)`: the arguments and the keys by their `inspect`, which runs their code and can change from one Ruby to the next (a Hash in 3.4, a Set in 4.0), the block as `&block`.
|
|
57
|
+
|
|
58
|
+
A challenge and a value are frozen. Their arguments are the objects given, not copies: a mutable argument stays shared with the caller.
|
|
70
59
|
|
|
71
|
-
|
|
72
|
-
`Defi` correctly raises a `NoMethodError`, showing that the method `boom` is undefined for a `String` object.
|
|
60
|
+
## Changes in 4.0
|
|
73
61
|
|
|
74
|
-
|
|
62
|
+
Compared with 3.0.1:
|
|
75
63
|
|
|
76
|
-
|
|
64
|
+
- `Defi::Method` becomes `Defi::Challenge`, a plain class, no longer a `BasicObject`, in `defi/challenge.rb`: `require "defi/method"` becomes `require "defi"`. `Kernel#Defi`, which every object received as a public method, is gone: write `Defi::Challenge.new`.
|
|
65
|
+
- `to` keeps the exceptions of the method only, where 3.0.1 caught every `Exception`, `Timeout` and `Interrupt` included. A name that is not a Symbol raises `ArgumentError`, as documented, with a new message; the `BasicObject` could not even `raise`. `call` keeps the cause of the exception it raises again.
|
|
66
|
+
- `Challenge#to_s` writes keyword arguments as `key: value` and the block as `&block`. `args` and `opts` are frozen. `Value.new` takes the object and whether it was raised; `Value::RAISE` and `Value::RETURN` are gone, as are `to_h` on both classes, `to_s` on a value and the custom `inspect` methods.
|
|
67
|
+
- Ruby 3.3 or later; tested on 3.3, 3.4, 4.0 and head.
|
|
77
68
|
|
|
78
69
|
## Versioning
|
|
79
70
|
|
|
80
|
-
|
|
71
|
+
Defi follows [Semantic Versioning 2.0](https://semver.org/).
|
|
81
72
|
|
|
82
73
|
## License
|
|
83
74
|
|
|
84
75
|
The [gem](https://rubygems.org/gems/defi) is available as open source under the terms of the [MIT License](https://github.com/fixrb/defi/raw/main/LICENSE.md).
|
|
85
76
|
|
|
86
|
-
|
|
77
|
+
## Sponsors
|
|
87
78
|
|
|
88
|
-
|
|
89
|
-
This project is sponsored by:<br />
|
|
90
|
-
<a href="https://sashite.com/"><img
|
|
91
|
-
src="https://github.com/fixrb/defi/raw/main/img/sashite.png"
|
|
92
|
-
alt="Sashité" /></a>
|
|
93
|
-
</p>
|
|
79
|
+
This project is sponsored by [Sashité](https://sashite.com/)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "value"
|
|
4
|
+
|
|
5
|
+
module Defi
|
|
6
|
+
# A method call prepared in advance: a name, positional arguments, keyword
|
|
7
|
+
# arguments and a block, to send to any object with {#to}. A challenge is
|
|
8
|
+
# frozen, and sends its message as `public_send` does: a private method is
|
|
9
|
+
# a `NoMethodError`, while a public one that reaches further, such as
|
|
10
|
+
# `send` or `instance_eval`, does what it does.
|
|
11
|
+
class Challenge
|
|
12
|
+
# @return [Symbol] the name of the method
|
|
13
|
+
attr_reader :name
|
|
14
|
+
|
|
15
|
+
# @return [Array] the positional arguments, frozen
|
|
16
|
+
attr_reader :args
|
|
17
|
+
|
|
18
|
+
# @return [Hash] the keyword arguments, frozen
|
|
19
|
+
attr_reader :opts
|
|
20
|
+
|
|
21
|
+
# @return [Proc, nil] the block
|
|
22
|
+
attr_reader :block
|
|
23
|
+
|
|
24
|
+
# @param name [Symbol] the name of the method
|
|
25
|
+
# @param args [Array] the positional arguments
|
|
26
|
+
# @param opts [Hash] the keyword arguments
|
|
27
|
+
# @param block [Proc] the block
|
|
28
|
+
# @raise [ArgumentError] when the name is not a Symbol
|
|
29
|
+
def initialize(name, *args, **opts, &block)
|
|
30
|
+
raise ::ArgumentError, "the name of the method must be a Symbol" unless name.instance_of?(::Symbol)
|
|
31
|
+
|
|
32
|
+
@name = name
|
|
33
|
+
@args = args.freeze
|
|
34
|
+
@opts = opts.freeze
|
|
35
|
+
@block = block
|
|
36
|
+
freeze
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Sends the message to an object. What the method raises comes back as
|
|
40
|
+
# the value, as long as it is the method's own doing: a `StandardError`,
|
|
41
|
+
# a `ScriptError` (such as `NotImplementedError`), a `SystemStackError`
|
|
42
|
+
# or a `SystemExit`. An `Interrupt`, a signal, a `NoMemoryError` and the
|
|
43
|
+
# exception a `Timeout` uses to end the block go through.
|
|
44
|
+
#
|
|
45
|
+
# @param object [Object] the receiver; a `BasicObject` has no
|
|
46
|
+
# `public_send`, so the value is a `NoMethodError`
|
|
47
|
+
# @return [Defi::Value] what came back
|
|
48
|
+
def to(object)
|
|
49
|
+
Value.new(object.public_send(name, *args, **opts, &block), false)
|
|
50
|
+
rescue *Value::RAISED => e
|
|
51
|
+
Value.new(e, true)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# @return [String] the call, as `.name(1, key: 2, &block)`: the arguments
|
|
55
|
+
# and the keys as their `inspect` writes them (user code, which runs
|
|
56
|
+
# here), the block as `&block`
|
|
57
|
+
def to_s
|
|
58
|
+
parts = args.map(&:inspect) + opts.map { |key, value| "#{keyword(key)} #{value.inspect}" }
|
|
59
|
+
parts << "&block" if block
|
|
60
|
+
parts.empty? ? ".#{name}" : ".#{name}(#{parts.join(", ")})"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
# A keyword as Ruby writes it: `key:` for a Symbol, quoted when its
|
|
66
|
+
# inspect is, `"key" =>` otherwise.
|
|
67
|
+
def keyword(key)
|
|
68
|
+
key.instance_of?(::Symbol) ? "#{key.inspect[1..]}:" : "#{key.inspect} =>"
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
data/lib/defi/value.rb
CHANGED
|
@@ -1,95 +1,37 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Defi
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
# This class is used to handle the outcome of a method invocation, allowing
|
|
8
|
-
# to distinguish between successful results and exceptions.
|
|
4
|
+
# What came back from a challenge: the object returned, or the exception
|
|
5
|
+
# raised. A value is frozen.
|
|
9
6
|
class Value
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
# The exceptions a method raises on its own, which a challenge keeps as
|
|
8
|
+
# its value. Anything else, an interruption or a signal, a shortage of
|
|
9
|
+
# memory, the end of a `Timeout`, goes through.
|
|
10
|
+
RAISED = [::StandardError, ::ScriptError, ::SystemStackError, ::SystemExit].freeze
|
|
12
11
|
|
|
13
|
-
# @return [
|
|
12
|
+
# @return [Object] the object returned, or the exception raised
|
|
14
13
|
attr_reader :object
|
|
15
14
|
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
# @example Handling an exception
|
|
24
|
-
# value = Defi::Value.new { raise 'Error' }
|
|
25
|
-
# value.call # raises 'Error'
|
|
26
|
-
#
|
|
27
|
-
# @yieldreturn [#object_id] The result of the block or the exception raised.
|
|
28
|
-
# rubocop:disable Lint/RescueException
|
|
29
|
-
def initialize
|
|
30
|
-
@object = yield
|
|
31
|
-
@raised = false
|
|
32
|
-
rescue ::Exception => e
|
|
33
|
-
@object = e
|
|
34
|
-
@raised = true
|
|
15
|
+
# @param object [Object] the object returned, or the exception raised
|
|
16
|
+
# @param raised [Boolean] whether the method raised: `true` or `false`,
|
|
17
|
+
# as given
|
|
18
|
+
def initialize(object, raised)
|
|
19
|
+
@object = object
|
|
20
|
+
@raised = raised
|
|
21
|
+
freeze
|
|
35
22
|
end
|
|
36
|
-
# rubocop:enable Lint/RescueException
|
|
37
23
|
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
# @example
|
|
41
|
-
# value = Defi::Value.new { "Hello" }
|
|
42
|
-
# value.call # => "Hello"
|
|
43
|
-
#
|
|
44
|
-
# @return [#object_id] The returned object or raises the captured exception.
|
|
45
|
-
def call
|
|
46
|
-
raise object if raised?
|
|
47
|
-
|
|
48
|
-
object
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
# Checks if an exception was raised during the execution.
|
|
52
|
-
#
|
|
53
|
-
# @example
|
|
54
|
-
# value = Defi::Value.new { raise 'Error' }
|
|
55
|
-
# value.raised? # => true
|
|
56
|
-
#
|
|
57
|
-
# @return [Boolean] True if an exception was raised, otherwise false.
|
|
24
|
+
# @return [Boolean] whether the method raised
|
|
58
25
|
def raised?
|
|
59
26
|
@raised
|
|
60
27
|
end
|
|
61
28
|
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
{
|
|
67
|
-
raised: raised?,
|
|
68
|
-
object:
|
|
69
|
-
}
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
# Returns a string representation of the Value object.
|
|
73
|
-
#
|
|
74
|
-
# @return [String] The string representation of the Value object.
|
|
75
|
-
def to_s
|
|
76
|
-
"#{raise_or_return} #{object}"
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
# Returns a human-readable representation of the Value object.
|
|
80
|
-
#
|
|
81
|
-
# @return [String] The human-readable representation of the Value object.
|
|
82
|
-
def inspect
|
|
83
|
-
"Value(object: #{object}, raised: #{raised?})"
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
private
|
|
29
|
+
# @return [Object] the object returned
|
|
30
|
+
# @raise [Exception] the exception raised, again, with its own cause
|
|
31
|
+
def call
|
|
32
|
+
raise object, cause: object.cause if raised?
|
|
87
33
|
|
|
88
|
-
|
|
89
|
-
#
|
|
90
|
-
# @return [String] A "raise" or "return" string.
|
|
91
|
-
def raise_or_return
|
|
92
|
-
raised? ? RAISE : RETURN
|
|
34
|
+
object
|
|
93
35
|
end
|
|
94
36
|
end
|
|
95
37
|
end
|
data/lib/defi.rb
CHANGED
|
@@ -1,28 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
# @example Adding 2 to 1
|
|
13
|
-
# # Create a Defi method object for addition with an argument of 2
|
|
14
|
-
# addition = Defi(:+, 2)
|
|
15
|
-
# addition.inspect # => "Defi(name: :+, args: [2], opts: {}, block: nil)"
|
|
16
|
-
#
|
|
17
|
-
# # Apply the addition to the number 1
|
|
18
|
-
# result = addition.to(1)
|
|
19
|
-
# result # => Value(object: 3, raised: false)
|
|
20
|
-
#
|
|
21
|
-
# # Execute the addition and get the result
|
|
22
|
-
# result.call # => 3
|
|
23
|
-
#
|
|
3
|
+
# Prepares a method call, a name with its arguments, and sends it to any
|
|
4
|
+
# object, keeping what came back: the value returned, or the exception
|
|
5
|
+
# raised.
|
|
6
|
+
#
|
|
7
|
+
# @example
|
|
8
|
+
# challenge = Defi::Challenge.new(:+, 2)
|
|
9
|
+
# challenge.to(40).object # => 42
|
|
10
|
+
# challenge.to("a").call # raises TypeError: no implicit conversion of Integer into String
|
|
24
11
|
module Defi
|
|
25
12
|
end
|
|
26
13
|
|
|
27
|
-
|
|
28
|
-
require_relative "
|
|
14
|
+
require_relative "defi/challenge"
|
|
15
|
+
require_relative "defi/value"
|
metadata
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: defi
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 4.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Cyril Kato
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-10-24 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
|
-
description:
|
|
13
|
+
description: 'Prepares a method call, a name with its arguments, and sends it to any
|
|
14
|
+
object, keeping what came back: the value returned, or the exception raised.'
|
|
14
15
|
email: contact@cyril.email
|
|
15
16
|
executables: []
|
|
16
17
|
extensions: []
|
|
@@ -19,15 +20,18 @@ files:
|
|
|
19
20
|
- LICENSE.md
|
|
20
21
|
- README.md
|
|
21
22
|
- lib/defi.rb
|
|
22
|
-
- lib/defi/
|
|
23
|
+
- lib/defi/challenge.rb
|
|
23
24
|
- lib/defi/value.rb
|
|
24
|
-
|
|
25
|
-
homepage: https://github.com/fixrb/defi
|
|
25
|
+
homepage: https://fixrb.dev/gems/defi/
|
|
26
26
|
licenses:
|
|
27
27
|
- MIT
|
|
28
28
|
metadata:
|
|
29
|
+
bug_tracker_uri: https://github.com/fixrb/defi/issues
|
|
30
|
+
changelog_uri: https://github.com/fixrb/defi#changes-in-40
|
|
31
|
+
documentation_uri: https://rubydoc.info/gems/defi
|
|
29
32
|
rubygems_mfa_required: 'true'
|
|
30
|
-
|
|
33
|
+
source_code_uri: https://github.com/fixrb/defi
|
|
34
|
+
post_install_message:
|
|
31
35
|
rdoc_options: []
|
|
32
36
|
require_paths:
|
|
33
37
|
- lib
|
|
@@ -35,15 +39,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
35
39
|
requirements:
|
|
36
40
|
- - ">="
|
|
37
41
|
- !ruby/object:Gem::Version
|
|
38
|
-
version: 3.
|
|
42
|
+
version: 3.3.0
|
|
39
43
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
40
44
|
requirements:
|
|
41
45
|
- - ">="
|
|
42
46
|
- !ruby/object:Gem::Version
|
|
43
47
|
version: '0'
|
|
44
48
|
requirements: []
|
|
45
|
-
rubygems_version: 3.
|
|
46
|
-
signing_key:
|
|
49
|
+
rubygems_version: 3.5.22
|
|
50
|
+
signing_key:
|
|
47
51
|
specification_version: 4
|
|
48
|
-
summary:
|
|
52
|
+
summary: A method call prepared in advance, to send to any object
|
|
49
53
|
test_files: []
|
data/lib/defi/method.rb
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Defi
|
|
4
|
-
# Represents a method to be applied against an object.
|
|
5
|
-
# This class encapsulates the method name, its arguments, keyword arguments,
|
|
6
|
-
# and an optional block, enabling dynamic method invocation.
|
|
7
|
-
class Method < ::BasicObject
|
|
8
|
-
# Initialize a new Method object.
|
|
9
|
-
#
|
|
10
|
-
# @param name [Symbol] The name of the method.
|
|
11
|
-
# @param args [Array] Any arguments of the method.
|
|
12
|
-
# @param opts [Hash] Any keyword arguments of the method.
|
|
13
|
-
# @param block [Proc] Any block argument of the method.
|
|
14
|
-
# @raise [ArgumentError] If the name is not a symbol.
|
|
15
|
-
def initialize(name, *args, **opts, &block)
|
|
16
|
-
raise ::ArgumentError, name.class.inspect unless name.is_a?(::Symbol)
|
|
17
|
-
|
|
18
|
-
@name = name
|
|
19
|
-
@args = args
|
|
20
|
-
@opts = opts
|
|
21
|
-
@block = block
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
# Applies the method to the given object.
|
|
25
|
-
#
|
|
26
|
-
# @example
|
|
27
|
-
# add = Defi::Method.new(:+, 1)
|
|
28
|
-
# add.to(2).call # => 3
|
|
29
|
-
#
|
|
30
|
-
# @param object [#object_id] The object to method.
|
|
31
|
-
# @return [Defi::Value] The actual value, to raise or to return.
|
|
32
|
-
def to(object)
|
|
33
|
-
Value.new { object.public_send(@name, *@args, **@opts, &@block) }
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# Returns a hash containing the method's properties.
|
|
37
|
-
#
|
|
38
|
-
# @return [Hash] The properties of the method.
|
|
39
|
-
def to_h
|
|
40
|
-
{
|
|
41
|
-
name: @name,
|
|
42
|
-
args: @args,
|
|
43
|
-
opts: @opts,
|
|
44
|
-
block: @block
|
|
45
|
-
}
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
# rubocop:disable Metrics/AbcSize
|
|
49
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
|
50
|
-
|
|
51
|
-
# Returns a string representation of the method.
|
|
52
|
-
#
|
|
53
|
-
# @example
|
|
54
|
-
# add = Defi::Method.new(:+, 1)
|
|
55
|
-
# add.to_s # => ".+(1)"
|
|
56
|
-
#
|
|
57
|
-
# @return [String] The string representation of the method.
|
|
58
|
-
def to_s
|
|
59
|
-
string = ".#{@name}"
|
|
60
|
-
return string if @args.empty? && @opts.empty? && @block.nil?
|
|
61
|
-
|
|
62
|
-
stringified_args = @args.inspect[1..-2]
|
|
63
|
-
stringified_opts = @opts.inspect[1..-2]
|
|
64
|
-
stringified_block = "<Proc>" unless @block.nil?
|
|
65
|
-
|
|
66
|
-
stringified_items = []
|
|
67
|
-
stringified_items << stringified_args unless @args.empty?
|
|
68
|
-
stringified_items << stringified_opts unless @opts.empty?
|
|
69
|
-
stringified_items << stringified_block unless @block.nil?
|
|
70
|
-
|
|
71
|
-
"#{string}(#{stringified_items.join(", ")})"
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
# rubocop:enable Metrics/AbcSize
|
|
75
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
|
76
|
-
|
|
77
|
-
# Returns a human-readable representation of the method.
|
|
78
|
-
#
|
|
79
|
-
# @example
|
|
80
|
-
# add = Defi::Method.new(:+, 1)
|
|
81
|
-
# add.inspect # => "Defi(name: :+, args: [1], opts: {}, block: nil)"
|
|
82
|
-
#
|
|
83
|
-
# @return [String] The human-readable representation of the method.
|
|
84
|
-
def inspect
|
|
85
|
-
inspected_name = @name.inspect
|
|
86
|
-
inspected_args = @args.inspect
|
|
87
|
-
inspected_opts = @opts.inspect
|
|
88
|
-
inspected_block = @block.nil? ? "nil" : "<Proc>"
|
|
89
|
-
|
|
90
|
-
"Defi(" \
|
|
91
|
-
"name: #{inspected_name}, " \
|
|
92
|
-
"args: #{inspected_args}, " \
|
|
93
|
-
"opts: #{inspected_opts}, " \
|
|
94
|
-
"block: #{inspected_block})"
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
require_relative "value"
|
data/lib/kernel.rb
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative File.join("defi", "method")
|
|
4
|
-
|
|
5
|
-
# Extension of the Kernel module to include the Defi method.
|
|
6
|
-
# The Defi method is a convenient way to create Method objects
|
|
7
|
-
# that encapsulate a method name and its arguments, offering a
|
|
8
|
-
# dynamic approach to method invocation.
|
|
9
|
-
module Kernel
|
|
10
|
-
# Disabling the RuboCop rule for method naming conventions
|
|
11
|
-
# to define a method with an uppercase name for stylistic reasons.
|
|
12
|
-
# rubocop:disable Naming/MethodName
|
|
13
|
-
|
|
14
|
-
# Creates a new Defi::Method instance.
|
|
15
|
-
# This method provides a simple and elegant way to encapsulate
|
|
16
|
-
# a method name and its arguments for later invocation.
|
|
17
|
-
#
|
|
18
|
-
# @example Creating a Defi method without arguments
|
|
19
|
-
# Defi(:foo).inspect # => "Defi(name: :foo, args: [], opts: {}, block: nil)"
|
|
20
|
-
#
|
|
21
|
-
# @example Adding 2 to 1
|
|
22
|
-
# # Create a Defi method object for addition with an argument of 2
|
|
23
|
-
# addition = Defi(:+, 2)
|
|
24
|
-
# addition.inspect # => "Defi(name: :+, args: [2], opts: {}, block: nil)"
|
|
25
|
-
#
|
|
26
|
-
# # Apply the addition to the number 1
|
|
27
|
-
# result = addition.to(1)
|
|
28
|
-
# result # => Value(object: 3, raised: false)
|
|
29
|
-
#
|
|
30
|
-
# # Execute the addition and get the result
|
|
31
|
-
# result.call # => 3
|
|
32
|
-
#
|
|
33
|
-
# @param method_name [Symbol] The method name to be sent to an object.
|
|
34
|
-
#
|
|
35
|
-
# @return [Defi::Method] An instance of Defi::Method encapsulating the method name and provided arguments.
|
|
36
|
-
def Defi(method_name, ...)
|
|
37
|
-
::Defi::Method.new(method_name, ...)
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
# Re-enabling the RuboCop rule for method naming conventions.
|
|
41
|
-
# rubocop:enable Naming/MethodName
|
|
42
|
-
end
|