Skip to content

Commit bd718ca

Browse files
committed
use dry-monad to get succes and failure and added transactions basics
1 parent c6c1fc0 commit bd718ca

File tree

12 files changed

+194
-69
lines changed

12 files changed

+194
-69
lines changed

Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ gem 'rake', '~> 13.0'
99

1010
gem 'minitest', '~> 5.16'
1111
gem 'minitest-reporters'
12+
gem 'pry'
1213
gem 'webmock'
1314

1415
gem 'rubocop', '~> 1.21', require: false

Gemfile.lock

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ PATH
22
remote: .
33
specs:
44
blnk (0.1.1)
5+
dry-configurable (~> 1.0.0)
6+
dry-monads (~> 1.6)
7+
dry-validation (~> 1.10.0)
58
http (~> 5.2.0)
69

710
GEM
@@ -16,11 +19,50 @@ GEM
1619
benchmark (0.3.0)
1720
bigdecimal (3.1.8)
1821
builder (3.3.0)
22+
coderay (1.1.3)
23+
concurrent-ruby (1.3.3)
1924
crack (1.0.0)
2025
bigdecimal
2126
rexml
2227
diff-lcs (1.5.1)
2328
domain_name (0.6.20240107)
29+
dry-configurable (1.0.1)
30+
dry-core (~> 1.0, < 2)
31+
zeitwerk (~> 2.6)
32+
dry-core (1.0.1)
33+
concurrent-ruby (~> 1.0)
34+
zeitwerk (~> 2.6)
35+
dry-inflector (1.0.0)
36+
dry-initializer (3.1.1)
37+
dry-logic (1.5.0)
38+
concurrent-ruby (~> 1.0)
39+
dry-core (~> 1.0, < 2)
40+
zeitwerk (~> 2.6)
41+
dry-monads (1.6.0)
42+
concurrent-ruby (~> 1.0)
43+
dry-core (~> 1.0, < 2)
44+
zeitwerk (~> 2.6)
45+
dry-schema (1.13.4)
46+
concurrent-ruby (~> 1.0)
47+
dry-configurable (~> 1.0, >= 1.0.1)
48+
dry-core (~> 1.0, < 2)
49+
dry-initializer (~> 3.0)
50+
dry-logic (>= 1.4, < 2)
51+
dry-types (>= 1.7, < 2)
52+
zeitwerk (~> 2.6)
53+
dry-types (1.7.2)
54+
bigdecimal (~> 3.0)
55+
concurrent-ruby (~> 1.0)
56+
dry-core (~> 1.0)
57+
dry-inflector (~> 1.0)
58+
dry-logic (~> 1.4)
59+
zeitwerk (~> 2.6)
60+
dry-validation (1.10.0)
61+
concurrent-ruby (~> 1.0)
62+
dry-core (~> 1.0, < 2)
63+
dry-initializer (~> 3.0)
64+
dry-schema (>= 1.12, < 2)
65+
zeitwerk (~> 2.6)
2466
e2mmap (0.1.0)
2567
ffi (1.17.0-arm64-darwin)
2668
ffi (1.17.0-x86_64-linux-gnu)
@@ -47,6 +89,7 @@ GEM
4789
llhttp-ffi (0.5.0)
4890
ffi-compiler (~> 1.0)
4991
rake (~> 13.0)
92+
method_source (1.1.0)
5093
minitest (5.24.0)
5194
minitest-reporters (1.7.1)
5295
ansi
@@ -61,6 +104,9 @@ GEM
61104
parser (3.3.3.0)
62105
ast (~> 2.4.1)
63106
racc
107+
pry (0.14.2)
108+
coderay (~> 1.1)
109+
method_source (~> 1.0)
64110
public_suffix (6.0.0)
65111
racc (1.8.0)
66112
rainbow (3.1.1)
@@ -110,6 +156,7 @@ GEM
110156
crack (>= 0.3.2)
111157
hashdiff (>= 0.4.0, < 2.0.0)
112158
yard (0.9.36)
159+
zeitwerk (2.6.16)
113160

114161
PLATFORMS
115162
arm64-darwin
@@ -119,6 +166,7 @@ DEPENDENCIES
119166
blnk!
120167
minitest (~> 5.16)
121168
minitest-reporters
169+
pry
122170
rake (~> 13.0)
123171
rubocop (~> 1.21)
124172
solargraph

blnk.gemspec

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
require_relative 'lib/blnk/version'
44

5-
Gem::Specification.new do |spec|
5+
Gem::Specification.new do |spec| # rubocop:disable Metric/Metrics/BlockLength
66
spec.name = 'blnk'
77
spec.version = Blnk::VERSION
88
spec.authors = ['Antonio Roberto Silva']
@@ -34,6 +34,9 @@ Gem::Specification.new do |spec|
3434
spec.require_paths = ['lib']
3535

3636
# Uncomment to register a new dependency of your gem
37+
spec.add_dependency 'dry-configurable', '~> 1.0.0'
38+
spec.add_dependency 'dry-monads', '~> 1.6'
39+
spec.add_dependency 'dry-validation', '~> 1.10.0'
3740
spec.add_dependency 'http', '~> 5.2.0'
3841

3942
# For more information and examples about making a new gem, check out our

lib/blnk.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
require 'http'
44
require 'ostruct'
5+
require 'dry-validation'
6+
require 'dry/monads'
57
require_relative 'blnk/version'
68
require_relative 'blnk/client'
79
require_relative 'blnk/resourceable'

lib/blnk/balance.rb

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,15 @@
33
module Blnk
44
# Balance representation
55
class Balance < Resourceable
6-
def self.resource_name = :balances
7-
def self.id_field = :balance_id
6+
class CreateContract < Dry::Validation::Contract
7+
schema do
8+
required(:ledger_id).value(:string)
9+
required(:currency).value(:string)
10+
end
11+
end
812

9-
def body_data = { ledger_id:, currency: }
13+
self.resource_name = :balances
14+
self.id_field = :balance_id
15+
self.create_contract = CreateContract
1016
end
1117
end

lib/blnk/ledger.rb

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,15 @@
33
module Blnk
44
# Ledger representation
55
class Ledger < Resourceable
6-
def self.resource_name = :ledgers
7-
def self.id_field = :ledger_id
6+
class CreateContract < Dry::Validation::Contract
7+
schema do
8+
required(:name).value(:string)
9+
optional(:meta_data).value(:hash)
10+
end
11+
end
812

9-
def body_data = { name:, meta_data: }
13+
self.resource_name = :ledgers
14+
self.id_field = :ledger_id
15+
self.create_contract = CreateContract
1016
end
1117
end

lib/blnk/resourceable.rb

Lines changed: 55 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,54 +3,82 @@
33
module Blnk
44
# Resoureable module that bring some tweaks for basic REST api integration
55
class Resourceable < OpenStruct
6+
extend Client
7+
68
class SearchResult < OpenStruct; end
79

8-
include Client
10+
class DefaultSearchContract < Dry::Validation::Contract
11+
schema do
12+
required(:q).value(:string)
13+
end
14+
end
915

1016
class << self
11-
def resource_name = raise NotImplementedError
12-
def id_field = :id
17+
include Dry::Monads[:result]
18+
19+
attr_accessor :resource_name, :id_field, :create_contract, :search_contract
1320

1421
def find(id)
15-
response = new.get_request(path: "/#{resource_name}/#{id}")
16-
return response unless response.status.success?
22+
check_vars
23+
res = get_request(path: "/#{resource_name}/#{id}")
24+
return Success(new(res.parse)) if res.status.success?
1725

18-
new response.parse
26+
Failure(res.parse)
1927
end
2028

2129
def all
22-
response = new.get_request(path: "/#{resource_name}")
23-
return response unless response.status.success?
30+
check_vars
31+
res = get_request(path: "/#{resource_name}")
32+
return Failure(res.parse&.symbolize_keys) unless res.status.success?
2433

25-
response.parse.map do |r|
26-
new r
27-
end
34+
Success(res.parse.map { |r| new(r) })
2835
end
2936

3037
def create(**args)
31-
response = new.post_request(
32-
path: "/#{resource_name}",
33-
body: args
34-
)
35-
return response unless response.status.success?
38+
contract = wrap_call(create_contract_new, args)
39+
return contract if contract.failure?
3640

37-
new(response.parse)
41+
res = post_request(path: "/#{resource_name}", body: contract.to_h)
42+
return Failure(res.parse&.symbolize_keys) unless res.status.success?
43+
44+
Success(new(res.parse))
3845
end
3946

4047
def search(**args)
41-
response = new.post_request(
42-
path: "/search/#{resource_name}",
43-
body: args
44-
)
45-
return response unless response.status.success?
48+
contract = wrap_call(search_contract_new, args)
49+
return contract if contract.failure?
50+
51+
res = post_request(path: "/search/#{resource_name}", body: contract.to_h)
52+
return Failure(res.parse&.symbolize_keys) unless res.status.success?
53+
54+
result = SearchResult.new(res.parse.merge(resource_name:))
55+
Success(result)
56+
end
4657

47-
sr = SearchResult.new(response.parse)
48-
sr.resource_name = resource_name
49-
sr
58+
def check_vars
59+
raise NotImplementedError, 'missing self.resource_name' unless resource_name
60+
raise NotImplementedError, 'missing self.id_field' unless id_field
61+
raise NotImplementedError, 'missing self.create_contract' unless create_contract
5062
end
63+
64+
def wrap_call(contract, args)
65+
check_vars
66+
ccall = contract.call(args)
67+
return Failure(ccall.errors.to_h) if ccall.failure?
68+
69+
ccall
70+
end
71+
72+
def create_contract_new
73+
return create_contract.new if create_contract
74+
75+
raise NotImplementedError, 'missing self.create_contract'
76+
end
77+
78+
def search_contract_new = (search_contract || DefaultSearchContract).new
5179
end
5280

53-
def persisted? = table[self.class.id_field]
54-
def body_data = raise NotImplementedError
81+
# table[self.class.id_field]
82+
def persisted? = public_send(self.class.id_field)
5583
end
5684
end

lib/blnk/transaction.rb

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,23 @@
33
module Blnk
44
# Transaction representation
55
class Transaction < Resourceable
6-
def self.resource_name = :transactions
7-
def self.id_field = :transaction_id
6+
class CreateContract < Dry::Validation::Contract
7+
schema do
8+
required(:amount).value(:integer)
9+
required(:precision).value(:integer)
10+
required(:currency).value(:string)
11+
required(:reference).value(:string)
12+
required(:source).value(:string)
13+
required(:destination).value(:string)
14+
required(:description).value(:string)
15+
required(:allow_overdraft).value(:bool)
16+
optional(:inflight).value(:bool)
17+
optional(:rate).value(:integer)
18+
end
19+
end
820

9-
def body_data = {}
21+
self.resource_name = :transactions
22+
self.id_field = :transaction_id
23+
self.create_contract = CreateContract
1024
end
1125
end

test/blnk/test_balance.rb

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,20 +52,21 @@ def stub_all_balance_request_with_success
5252
.to_return_json(body: [balance_response_body], status: 200)
5353
end
5454

55-
class TestLedger < Minitest::Test
55+
class TestBalance < Minitest::Test
5656
def test_that_balance_not_found
5757
stub_find_balance_request_with_error
5858
find = Blnk::Balance.find 'BALANCE_ID'
5959

60-
assert find.status.bad_request?
60+
assert find.failure?
6161
end
6262

6363
def test_that_balance_find_success
6464
stub_find_balance_request_with_success
6565
find = Blnk::Balance.find 'BALANCE_ID'
6666

67-
assert find.is_a?(Blnk::Balance)
68-
assert find.balance_id.eql?(balance_response_body[:balance_id])
67+
assert find.success?
68+
assert find.value!.is_a?(Blnk::Balance)
69+
assert find.value!.balance_id.eql?(balance_response_body[:balance_id])
6970
end
7071

7172
# NOTE: /balances route does not exist, at moment
@@ -92,16 +93,17 @@ def test_that_balance_create_errosr
9293

9394
create = Blnk::Balance.create
9495

95-
assert create.status.bad_request?
96+
assert create.failure?
9697
end
9798

98-
def test_that_balance_create_success
99+
def test_that_balance_create_success # rubocop:disable Metrics/AbcSize
99100
stub_create_balance_request_with_success
100101

101102
create = Blnk::Balance.create(ledger_id: 'ledger_id', currency: 'USD')
102103

103-
assert create.is_a?(Blnk::Balance)
104-
assert create.balance_id.eql?(balance_response_body[:balance_id])
105-
assert create.name.eql?(balance_response_body[:name])
104+
assert create.success?
105+
assert create.value!.is_a?(Blnk::Balance)
106+
assert create.value!.balance_id.eql?(balance_response_body[:balance_id])
107+
assert create.value!.name.eql?(balance_response_body[:name])
106108
end
107109
end

0 commit comments

Comments
 (0)