Skip to content

Commit 7bf8ba6

Browse files
authored
Merge pull request #41 from B-CDD/feat/add-hash-methods-to-bcdd-context
Add some Hash's methods to BCDD::Context
2 parents 3e1cfed + 8ffa69a commit 7bf8ba6

File tree

7 files changed

+173
-41
lines changed

7 files changed

+173
-41
lines changed

CHANGELOG.md

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,58 @@
11
- [\[Unreleased\]](#unreleased)
2-
- [1.0.0 - 2024-03-16](#100---2024-03-16)
32
- [Added](#added)
3+
- [1.0.0 - 2024-03-16](#100---2024-03-16)
4+
- [Added](#added-1)
45
- [Changed](#changed)
56
- [\[0.13.0\] - 2024-02-01](#0130---2024-02-01)
6-
- [Added](#added-1)
7+
- [Added](#added-2)
78
- [Changed](#changed-1)
89
- [\[0.12.0\] - 2024-01-07](#0120---2024-01-07)
9-
- [Added](#added-2)
10+
- [Added](#added-3)
1011
- [Changed](#changed-2)
1112
- [\[0.11.0\] - 2024-01-02](#0110---2024-01-02)
12-
- [Added](#added-3)
13+
- [Added](#added-4)
1314
- [Changed](#changed-3)
1415
- [\[0.10.0\] - 2023-12-31](#0100---2023-12-31)
15-
- [Added](#added-4)
16+
- [Added](#added-5)
1617
- [\[0.9.1\] - 2023-12-12](#091---2023-12-12)
1718
- [Changed](#changed-4)
1819
- [Fixed](#fixed)
1920
- [\[0.9.0\] - 2023-12-12](#090---2023-12-12)
20-
- [Added](#added-5)
21+
- [Added](#added-6)
2122
- [Changed](#changed-5)
2223
- [\[0.8.0\] - 2023-12-11](#080---2023-12-11)
23-
- [Added](#added-6)
24+
- [Added](#added-7)
2425
- [Changed](#changed-6)
2526
- [Removed](#removed)
2627
- [\[0.7.0\] - 2023-10-27](#070---2023-10-27)
27-
- [Added](#added-7)
28+
- [Added](#added-8)
2829
- [Changed](#changed-7)
2930
- [\[0.6.0\] - 2023-10-11](#060---2023-10-11)
30-
- [Added](#added-8)
31+
- [Added](#added-9)
3132
- [Changed](#changed-8)
3233
- [\[0.5.0\] - 2023-10-09](#050---2023-10-09)
33-
- [Added](#added-9)
34-
- [\[0.4.0\] - 2023-09-28](#040---2023-09-28)
3534
- [Added](#added-10)
35+
- [\[0.4.0\] - 2023-09-28](#040---2023-09-28)
36+
- [Added](#added-11)
3637
- [Changed](#changed-9)
3738
- [Removed](#removed-1)
3839
- [\[0.3.0\] - 2023-09-26](#030---2023-09-26)
39-
- [Added](#added-11)
40-
- [\[0.2.0\] - 2023-09-26](#020---2023-09-26)
4140
- [Added](#added-12)
41+
- [\[0.2.0\] - 2023-09-26](#020---2023-09-26)
42+
- [Added](#added-13)
4243
- [Removed](#removed-2)
4344
- [\[0.1.0\] - 2023-09-25](#010---2023-09-25)
44-
- [Added](#added-13)
45+
- [Added](#added-14)
4546

4647
## [Unreleased]
4748

49+
### Added
50+
51+
- Add some Hash's methods to `BCDD::Context`. They are:
52+
- `#slice` to extract only the desired keys.
53+
- `#[]`, `#dig`, `#fetch` to access the values.
54+
- `#values_at` and `#fetch_values` to get the values of the desired keys.
55+
4856
## 1.0.0 - 2024-03-16
4957

5058
### Added

README.md

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,7 @@ Use it to enable the [Railway Oriented Programming](https://fsharpforfunandprofi
6363
- [`BCDD::Result::Expectations.mixin` add-ons](#bcddresultexpectationsmixin-add-ons)
6464
- [`BCDD::Context`](#bcddcontext)
6565
- [Defining successes and failures](#defining-successes-and-failures)
66-
- [Constant aliases](#constant-aliases)
67-
- [`BCDD::Context.mixin`](#bcddcontextmixin)
68-
- [Class example (Instance Methods)](#class-example-instance-methods-1)
66+
- [Hash methods](#hash-methods)
6967
- [`and_expose`](#and_expose)
7068
- [Module example (Singleton Methods)](#module-example-singleton-methods-1)
7169
- [`BCDD::Context::Expectations`](#bcddcontextexpectations)
@@ -1436,20 +1434,33 @@ BCDD::Context::Success(:ok, **{ message: 'hashes can be converted to keyword arg
14361434

14371435
<p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
14381436

1439-
#### Constant aliases
1437+
#### Hash methods
14401438

1441-
You can configure `Context` or `BCDD::Context` as an alias for `BCDD::Context`. This is helpful to define a standard way to avoid the full constant name/path in your code.
1439+
The `BCDD::Context` only accepts hashes as its values. Because of this, its instances have some Hash's methods to query/access the values. The available methods are:
1440+
1441+
- `#slice` to extract only the desired keys.
1442+
- `#[]`, `#dig`, `#fetch` to access the values.
1443+
- `#values_at` and `#fetch_values` to get the values of the desired keys.
14421444

14431445
```ruby
1444-
BCDD::Result.configuration do |config|
1445-
config.context_alias.enable!('BCDD::Context')
1446+
result = BCDD::Context::Success(:ok, a: 1, b: 2, c: {d: 4})
14461447

1447-
# or
1448+
result[:a] # 1
1449+
result.fetch(:a) # 1
1450+
result.dig(:c, :d) # 4
14481451

1449-
config.context_alias.enable!('Context')
1450-
end
1452+
result.slice(:a, :b) # {:a=>1, :b=>2}
1453+
1454+
result.values_at(:a, :b) # [1, 2]
1455+
result.fetch_values(:a, :b) # [1, 2]
14511456
```
14521457

1458+
These methods are available for `BCDD::Context::Success` and `BCDD::Context::Failure` instances.
1459+
1460+
<p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
1461+
1462+
```ruby
1463+
14531464
<p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
14541465

14551466
#### `BCDD::Context.mixin`

lib/bcdd/context.rb

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def self.Failure(type, **value)
2020
def initialize(type:, value:, source: nil, expectations: nil, terminal: nil)
2121
value.is_a?(::Hash) or raise ::ArgumentError, 'value must be a Hash'
2222

23-
@acc = {}
23+
@memo = {}
2424

2525
super
2626
end
@@ -32,14 +32,38 @@ def and_then(method_name = nil, **injected_value, &block)
3232
def and_then!(source, **injected_value)
3333
_call = injected_value.delete(:_call)
3434

35-
acc.merge!(injected_value)
35+
memo.merge!(injected_value)
3636

3737
super(source, injected_value, _call: _call)
3838
end
3939

40+
def [](key)
41+
value[key]
42+
end
43+
44+
def dig(...)
45+
value.dig(...)
46+
end
47+
48+
def fetch(...)
49+
value.fetch(...)
50+
end
51+
52+
def slice(...)
53+
value.slice(...)
54+
end
55+
56+
def values_at(...)
57+
value.values_at(...)
58+
end
59+
60+
def fetch_values(...)
61+
value.fetch_values(...)
62+
end
63+
4064
protected
4165

42-
attr_reader :acc
66+
attr_reader :memo
4367

4468
private
4569

@@ -54,31 +78,31 @@ def and_then!(source, **injected_value)
5478
end
5579

5680
def call_and_then_source_method!(method, injected_value)
57-
acc.merge!(value.merge(injected_value))
81+
memo.merge!(value.merge(injected_value))
5882

5983
case SourceMethodArity[method]
6084
when 0 then source.send(method.name)
61-
when 1 then source.send(method.name, **acc)
85+
when 1 then source.send(method.name, **memo)
6286
else raise Error::InvalidSourceMethodArity.build(source: source, method: method, max_arity: 1)
6387
end
6488
end
6589

6690
def call_and_then_block!(block)
67-
acc.merge!(value)
91+
memo.merge!(value)
6892

69-
block.call(acc)
93+
block.call(memo)
7094
end
7195

7296
def call_and_then_callable!(source, value:, injected_value:, method_name:)
73-
acc.merge!(value.merge(injected_value))
97+
memo.merge!(value.merge(injected_value))
7498

75-
CallableAndThen::Caller.call(source, value: acc, injected_value: injected_value, method_name: method_name)
99+
CallableAndThen::Caller.call(source, value: memo, injected_value: injected_value, method_name: method_name)
76100
end
77101

78102
def ensure_result_object(result, origin:)
79103
raise_unexpected_outcome_error(result, origin) unless result.is_a?(BCDD::Context)
80104

81-
return result.tap { _1.acc.merge!(acc) } if result.source.equal?(source)
105+
return result.tap { _1.memo.merge!(memo) } if result.source.equal?(source)
82106

83107
raise Error::InvalidResultSource.build(given_result: result, expected_source: source)
84108
end

lib/bcdd/context/callable_and_then.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def self.call_method!(source, method, value, _injected_value)
2828
end
2929

3030
def self.ensure_result_object(source, value, result)
31-
return result.tap { result.send(:acc).then { _1.merge!(value.merge(_1)) } } if result.is_a?(Context)
31+
return result.tap { result.send(:memo).then { _1.merge!(value.merge(_1)) } } if result.is_a?(Context)
3232

3333
raise Result::Error::UnexpectedOutcome.build(outcome: result, origin: source,
3434
expected: Context::EXPECTED_OUTCOME)

lib/bcdd/context/success.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ class Error < BCDD::Result::Error
88
class Success < self
99
include ::BCDD::Success
1010

11-
FetchValues = ->(acc_values, keys) do
12-
fetched_values = acc_values.fetch_values(*keys)
11+
FetchValues = ->(memo_values, keys) do
12+
fetched_values = memo_values.fetch_values(*keys)
1313

1414
keys.zip(fetched_values).to_h
1515
rescue ::KeyError => e
16-
message = "#{e.message}. Available to expose: #{acc_values.keys.map(&:inspect).join(', ')}"
16+
message = "#{e.message}. Available to expose: #{memo_values.keys.map(&:inspect).join(', ')}"
1717

1818
raise Error::InvalidExposure, message
1919
end
@@ -25,9 +25,9 @@ def and_expose(type, keys, terminal: true)
2525

2626
EventLogs.tracking.reset_and_then!
2727

28-
acc_values = acc.merge(value)
28+
memo_values = memo.merge(value)
2929

30-
value_to_expose = FetchValues.call(acc_values, keys)
30+
value_to_expose = FetchValues.call(memo_values, keys)
3131

3232
expectations = type_checker.expectations
3333

sig/bcdd/context.rbs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ class BCDD::Context < BCDD::Result
33

44
SourceMethodArity: ^(Method) -> Integer
55

6-
attr_reader acc: Hash[Symbol, untyped]
6+
attr_reader memo: Hash[Symbol, untyped]
77

88
def initialize: (
99
type: Symbol,

test/bcdd/context/success_test.rb

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,94 @@ class ContextSuccessTest < Minitest::Test
2020
result.inspect
2121
)
2222
end
23+
24+
test '#[]' do
25+
result1 = Context::Success(:ok)
26+
result2 = Context::Success(:ok, a: 1, b: 2)
27+
28+
assert_nil result1[:a]
29+
assert_nil result1[:b]
30+
31+
assert_equal 1, result2[:a]
32+
assert_equal 2, result2[:b]
33+
end
34+
35+
# rubocop:disable Style/SingleArgumentDig
36+
test '#dig' do
37+
result1 = Context::Success(:ok)
38+
result2 = Context::Success(:ok, a: { b: 1 })
39+
40+
assert_nil result1.dig(:a, :b)
41+
assert_nil result2.dig(:a, :c)
42+
43+
assert_equal({ b: 1 }, result2.dig(:a))
44+
assert_equal 1, result2.dig(:a, :b)
45+
end
46+
# rubocop:enable Style/SingleArgumentDig
47+
48+
test '#fetch' do
49+
result1 = Context::Success(:ok)
50+
result2 = Context::Success(:ok, a: 1, b: 2)
51+
52+
assert_raises(KeyError) { result1.fetch(:a) }
53+
assert_raises(KeyError) { result1.fetch(:b) }
54+
55+
assert_equal 1, result2.fetch(:a)
56+
assert_equal 2, result2.fetch(:b)
57+
58+
# ---
59+
60+
assert_equal 3, result1.fetch(:a, 3)
61+
assert_equal 4, result1.fetch(:b, 4)
62+
63+
assert_equal 1, result2.fetch(:a, 3)
64+
assert_equal 2, result2.fetch(:b, 4)
65+
66+
# ---
67+
68+
# rubocop:disable Style/RedundantFetchBlock
69+
assert_equal(5, result1.fetch(:a) { 5 })
70+
assert_equal(6, result1.fetch(:b) { 6 })
71+
72+
assert_equal(1, result2.fetch(:a) { 7 })
73+
assert_equal(2, result2.fetch(:b) { 8 })
74+
# rubocop:enable Style/RedundantFetchBlock
75+
end
76+
77+
test '#slice' do
78+
result1 = Context::Success(:ok)
79+
result2 = Context::Success(:ok, a: 1, b: 2)
80+
81+
assert_equal({}, result1.slice(:a, :b))
82+
assert_equal({ a: 1, b: 2 }, result2.slice(:a, :b))
83+
end
84+
85+
test '#values_at' do
86+
result1 = Context::Success(:ok)
87+
result2 = Context::Success(:ok, a: 1, b: 2)
88+
89+
assert_equal [nil, nil], result1.values_at(:a, :b)
90+
assert_equal [1, 2], result2.values_at(:a, :b)
91+
end
92+
93+
test '#fetch_values' do
94+
result1 = Context::Success(:ok)
95+
result2 = Context::Success(:ok, a: 1, b: 2)
96+
97+
assert_raises(KeyError) do
98+
result1.fetch_values(:a, :b)
99+
end
100+
101+
assert_equal [1, 2], result2.fetch_values(:a, :b)
102+
103+
# ---
104+
105+
values1 = result1.fetch_values(:a, :b, :c, :d) { |key| key.to_s.upcase }
106+
values2 = result2.fetch_values(:a, :b, :c, :d) { |key| key.to_s.upcase }
107+
108+
assert_equal %w[A B C D], values1
109+
110+
assert_equal [1, 2, 'C', 'D'], values2
111+
end
23112
end
24113
end

0 commit comments

Comments
 (0)