Skip to content

Commit

Permalink
use dry-monad to get succes and failure and added transactions basics
Browse files Browse the repository at this point in the history
  • Loading branch information
devton committed Jun 29, 2024
1 parent c6c1fc0 commit bd718ca
Show file tree
Hide file tree
Showing 12 changed files with 194 additions and 69 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ gem 'rake', '~> 13.0'

gem 'minitest', '~> 5.16'
gem 'minitest-reporters'
gem 'pry'
gem 'webmock'

gem 'rubocop', '~> 1.21', require: false
Expand Down
48 changes: 48 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ PATH
remote: .
specs:
blnk (0.1.1)
dry-configurable (~> 1.0.0)
dry-monads (~> 1.6)
dry-validation (~> 1.10.0)
http (~> 5.2.0)

GEM
Expand All @@ -16,11 +19,50 @@ GEM
benchmark (0.3.0)
bigdecimal (3.1.8)
builder (3.3.0)
coderay (1.1.3)
concurrent-ruby (1.3.3)
crack (1.0.0)
bigdecimal
rexml
diff-lcs (1.5.1)
domain_name (0.6.20240107)
dry-configurable (1.0.1)
dry-core (~> 1.0, < 2)
zeitwerk (~> 2.6)
dry-core (1.0.1)
concurrent-ruby (~> 1.0)
zeitwerk (~> 2.6)
dry-inflector (1.0.0)
dry-initializer (3.1.1)
dry-logic (1.5.0)
concurrent-ruby (~> 1.0)
dry-core (~> 1.0, < 2)
zeitwerk (~> 2.6)
dry-monads (1.6.0)
concurrent-ruby (~> 1.0)
dry-core (~> 1.0, < 2)
zeitwerk (~> 2.6)
dry-schema (1.13.4)
concurrent-ruby (~> 1.0)
dry-configurable (~> 1.0, >= 1.0.1)
dry-core (~> 1.0, < 2)
dry-initializer (~> 3.0)
dry-logic (>= 1.4, < 2)
dry-types (>= 1.7, < 2)
zeitwerk (~> 2.6)
dry-types (1.7.2)
bigdecimal (~> 3.0)
concurrent-ruby (~> 1.0)
dry-core (~> 1.0)
dry-inflector (~> 1.0)
dry-logic (~> 1.4)
zeitwerk (~> 2.6)
dry-validation (1.10.0)
concurrent-ruby (~> 1.0)
dry-core (~> 1.0, < 2)
dry-initializer (~> 3.0)
dry-schema (>= 1.12, < 2)
zeitwerk (~> 2.6)
e2mmap (0.1.0)
ffi (1.17.0-arm64-darwin)
ffi (1.17.0-x86_64-linux-gnu)
Expand All @@ -47,6 +89,7 @@ GEM
llhttp-ffi (0.5.0)
ffi-compiler (~> 1.0)
rake (~> 13.0)
method_source (1.1.0)
minitest (5.24.0)
minitest-reporters (1.7.1)
ansi
Expand All @@ -61,6 +104,9 @@ GEM
parser (3.3.3.0)
ast (~> 2.4.1)
racc
pry (0.14.2)
coderay (~> 1.1)
method_source (~> 1.0)
public_suffix (6.0.0)
racc (1.8.0)
rainbow (3.1.1)
Expand Down Expand Up @@ -110,6 +156,7 @@ GEM
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
yard (0.9.36)
zeitwerk (2.6.16)

PLATFORMS
arm64-darwin
Expand All @@ -119,6 +166,7 @@ DEPENDENCIES
blnk!
minitest (~> 5.16)
minitest-reporters
pry
rake (~> 13.0)
rubocop (~> 1.21)
solargraph
Expand Down
5 changes: 4 additions & 1 deletion blnk.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

require_relative 'lib/blnk/version'

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

# Uncomment to register a new dependency of your gem
spec.add_dependency 'dry-configurable', '~> 1.0.0'
spec.add_dependency 'dry-monads', '~> 1.6'
spec.add_dependency 'dry-validation', '~> 1.10.0'
spec.add_dependency 'http', '~> 5.2.0'

# For more information and examples about making a new gem, check out our
Expand Down
2 changes: 2 additions & 0 deletions lib/blnk.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

require 'http'
require 'ostruct'
require 'dry-validation'
require 'dry/monads'
require_relative 'blnk/version'
require_relative 'blnk/client'
require_relative 'blnk/resourceable'
Expand Down
12 changes: 9 additions & 3 deletions lib/blnk/balance.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@
module Blnk
# Balance representation
class Balance < Resourceable
def self.resource_name = :balances
def self.id_field = :balance_id
class CreateContract < Dry::Validation::Contract
schema do
required(:ledger_id).value(:string)
required(:currency).value(:string)
end
end

def body_data = { ledger_id:, currency: }
self.resource_name = :balances
self.id_field = :balance_id
self.create_contract = CreateContract
end
end
12 changes: 9 additions & 3 deletions lib/blnk/ledger.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@
module Blnk
# Ledger representation
class Ledger < Resourceable
def self.resource_name = :ledgers
def self.id_field = :ledger_id
class CreateContract < Dry::Validation::Contract
schema do
required(:name).value(:string)
optional(:meta_data).value(:hash)
end
end

def body_data = { name:, meta_data: }
self.resource_name = :ledgers
self.id_field = :ledger_id
self.create_contract = CreateContract
end
end
82 changes: 55 additions & 27 deletions lib/blnk/resourceable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,54 +3,82 @@
module Blnk
# Resoureable module that bring some tweaks for basic REST api integration
class Resourceable < OpenStruct
extend Client

class SearchResult < OpenStruct; end

include Client
class DefaultSearchContract < Dry::Validation::Contract
schema do
required(:q).value(:string)
end
end

class << self
def resource_name = raise NotImplementedError
def id_field = :id
include Dry::Monads[:result]

attr_accessor :resource_name, :id_field, :create_contract, :search_contract

def find(id)
response = new.get_request(path: "/#{resource_name}/#{id}")
return response unless response.status.success?
check_vars
res = get_request(path: "/#{resource_name}/#{id}")
return Success(new(res.parse)) if res.status.success?

new response.parse
Failure(res.parse)
end

def all
response = new.get_request(path: "/#{resource_name}")
return response unless response.status.success?
check_vars
res = get_request(path: "/#{resource_name}")
return Failure(res.parse&.symbolize_keys) unless res.status.success?

response.parse.map do |r|
new r
end
Success(res.parse.map { |r| new(r) })
end

def create(**args)
response = new.post_request(
path: "/#{resource_name}",
body: args
)
return response unless response.status.success?
contract = wrap_call(create_contract_new, args)
return contract if contract.failure?

new(response.parse)
res = post_request(path: "/#{resource_name}", body: contract.to_h)
return Failure(res.parse&.symbolize_keys) unless res.status.success?

Success(new(res.parse))
end

def search(**args)
response = new.post_request(
path: "/search/#{resource_name}",
body: args
)
return response unless response.status.success?
contract = wrap_call(search_contract_new, args)
return contract if contract.failure?

res = post_request(path: "/search/#{resource_name}", body: contract.to_h)
return Failure(res.parse&.symbolize_keys) unless res.status.success?

result = SearchResult.new(res.parse.merge(resource_name:))
Success(result)
end

sr = SearchResult.new(response.parse)
sr.resource_name = resource_name
sr
def check_vars
raise NotImplementedError, 'missing self.resource_name' unless resource_name
raise NotImplementedError, 'missing self.id_field' unless id_field
raise NotImplementedError, 'missing self.create_contract' unless create_contract
end

def wrap_call(contract, args)
check_vars
ccall = contract.call(args)
return Failure(ccall.errors.to_h) if ccall.failure?

ccall
end

def create_contract_new
return create_contract.new if create_contract

raise NotImplementedError, 'missing self.create_contract'
end

def search_contract_new = (search_contract || DefaultSearchContract).new
end

def persisted? = table[self.class.id_field]
def body_data = raise NotImplementedError
# table[self.class.id_field]
def persisted? = public_send(self.class.id_field)
end
end
20 changes: 17 additions & 3 deletions lib/blnk/transaction.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,23 @@
module Blnk
# Transaction representation
class Transaction < Resourceable
def self.resource_name = :transactions
def self.id_field = :transaction_id
class CreateContract < Dry::Validation::Contract
schema do
required(:amount).value(:integer)
required(:precision).value(:integer)
required(:currency).value(:string)
required(:reference).value(:string)
required(:source).value(:string)
required(:destination).value(:string)
required(:description).value(:string)
required(:allow_overdraft).value(:bool)
optional(:inflight).value(:bool)
optional(:rate).value(:integer)
end
end

def body_data = {}
self.resource_name = :transactions
self.id_field = :transaction_id
self.create_contract = CreateContract
end
end
20 changes: 11 additions & 9 deletions test/blnk/test_balance.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,20 +52,21 @@ def stub_all_balance_request_with_success
.to_return_json(body: [balance_response_body], status: 200)
end

class TestLedger < Minitest::Test
class TestBalance < Minitest::Test
def test_that_balance_not_found
stub_find_balance_request_with_error
find = Blnk::Balance.find 'BALANCE_ID'

assert find.status.bad_request?
assert find.failure?
end

def test_that_balance_find_success
stub_find_balance_request_with_success
find = Blnk::Balance.find 'BALANCE_ID'

assert find.is_a?(Blnk::Balance)
assert find.balance_id.eql?(balance_response_body[:balance_id])
assert find.success?
assert find.value!.is_a?(Blnk::Balance)
assert find.value!.balance_id.eql?(balance_response_body[:balance_id])
end

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

create = Blnk::Balance.create

assert create.status.bad_request?
assert create.failure?
end

def test_that_balance_create_success
def test_that_balance_create_success # rubocop:disable Metrics/AbcSize
stub_create_balance_request_with_success

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

assert create.is_a?(Blnk::Balance)
assert create.balance_id.eql?(balance_response_body[:balance_id])
assert create.name.eql?(balance_response_body[:name])
assert create.success?
assert create.value!.is_a?(Blnk::Balance)
assert create.value!.balance_id.eql?(balance_response_body[:balance_id])
assert create.value!.name.eql?(balance_response_body[:name])
end
end
Loading

0 comments on commit bd718ca

Please sign in to comment.