Initial commit

This commit is contained in:
bterkuile committed 2025-09-15 15:55:58 -05:00
commit 4b5f7906d7
33 files changed
+933

No files matched your search

+34
View File
@@ -0,0 +1,34 @@
# frozen_string_literal: true
require 'pry'
# External
require 'active_support/all'
require 'active_model'
require 'faraday'
# Internal
require_relative 'api/version'
require_relative 'btc'
require_relative 'btc_attribute_type'
# require_relative 'api/base_model'
# require_relative 'api/invoice_response'
ActiveSupport::Inflector.inflections(:en) { |inflect| inflect.acronym 'API' }
module StrikeMe
module API
class Error < StandardError; end
def self.autoload_default(name)
autoload name, "#{"#{self.name}/#{name}".underscore}.rb"
end
# autoload :InvoiceResponse, 'strike_me/api/invoice_response.rb'
autoload_default :Client
autoload_default :BaseClient
autoload_default :BaseModel
autoload_default :Account
autoload_default :InvoiceResponse
end
end
+52
View File
@@ -0,0 +1,52 @@
# frozen_string_literal: true
module StrikeMe
module API
class Account
include BaseClient
autoload_default :Profile
autoload_default :Balances
autoload_default :Balance
def profile
return @profile if @profile.present?
response = client.get profile_url
if response.success?
body = response.body
body = JSON.parse(body) if body.is_a? String
@profile = Profile.new body.merge(account: self)
else
# if you see this, something went wrong, this is test code
@profile = ResponseError.new(profile_url, response)
end
end
def profile_url
if (account_id = ENV['STRIKE_ACCOUNT_ID'].presence)
"accounts/#{account_id}/profile"
else
account_handle = ENV.fetch('STRIKE_ACCOUNT_HANDLE')
"accounts/handles/#{account_handle}/profile"
end
end
def balances
return @balances if @balances.present?
response = client.get 'balances'
@balances = if response.success?
Balances.new self, response.body
else
# if you see this, something went wrong, this is test code
ResponseError.new('balances', response)
end
end
def inspect
"#<StrikeMe::API::Account profile: #{@profile}, balances: #{@balances}>"
end
end
end
end
+17
View File
@@ -0,0 +1,17 @@
# frozen_string_literal: true
module StrikeMe
module API
class Account
class Balance < BaseModel
attribute :currency
attribute :current, :float
attribute :pending, :float
attribute :outgoing, :float
attribute :reserved, :float
attribute :available, :float
attribute :total, :float
end
end
end
end
+27
View File
@@ -0,0 +1,27 @@
# frozen_string_literal: true
module StrikeMe
module API
class Account
class Balances
include Enumerable
attr_reader :account
def initialize(account, items)
@account = account
# Map hash to object
@items = items.map { |item| Balance.new item }
end
def each(&block)
@items.each(&block)
end
def to_a
@items.map(&:attributes)
end
end
end
end
end
+21
View File
@@ -0,0 +1,21 @@
# frozen_string_literal: true
module StrikeMe
module API
class Account
class Profile < BaseModel
attribute :account
attribute :id
attribute :handle
attribute :canReceive
attribute :currencies
delegate :balances, to: :account
def can_receive?
canReceive.present?
end
end
end
end
end
+32
View File
@@ -0,0 +1,32 @@
# frozen_string_literal: true
module StrikeMe
module API
# The BaseClient module is to be included in objects using the Strike client
# to make calls and transform the responses to objects.
# Factory of BaseModel instances
module BaseClient
extend ActiveSupport::Concern
def client
@client ||= Client.new
end
module ClassMethods
def autoload_default(name)
autoload name, "#{"#{self.name}/#{name}".underscore}.rb"
end
end
# cumbersome, but better for later debugging and handling
class ResponseError
attr_reader :url, :faraday_response
def initialize(url, faraday_response)
@url = url
@faraday_response = faraday_response
end
end
end
end
end
+10
View File
@@ -0,0 +1,10 @@
# frozen_string_literal: true
module StrikeMe
module API
class BaseModel
include ActiveModel::Model
include ActiveModel::Attributes
end
end
end
+32
View File
@@ -0,0 +1,32 @@
# frozen_string_literal: true
module StrikeMe
module API
class Client
attr_reader :version, :connection
delegate :get, to: :connection
def initialize(**args)
@version = args[:version] || 'v1'
@connection_args = args.slice(:url, :params, :headers).reverse_merge(
url: "https://api.strike.me/#{version}"
)
# headers: {
# 'Content-Type' => 'application/json',
# 'Authorization' => "Bearer #{ENV.fetch('STRIKE_API_KEY')}"
# }
@connection = Faraday.new(@connection_args) do |builder|
builder.request :authorization, 'Bearer', -> { ENV.fetch('STRIKE_API_KEY') }
builder.request :json
builder.response :json
end
end
def account_id
ENV.fetch('STRIKE_ACCOUNT_ID')
end
end
end
end
+11
View File
@@ -0,0 +1,11 @@
# frozen_string_literal: true
module StrikeMe
module API
class InvoiceResponse < BaseModel
attr_accessor :ok, :checking_id, :payment_request, :error_message
validates :ok, presence: true
end
end
end
+7
View File
@@ -0,0 +1,7 @@
# frozen_string_literal: true
module StrikeMe
module API
VERSION = '0.1.0'
end
end
+82
View File
@@ -0,0 +1,82 @@
# frozen_string_literal: true
class Float
def btc
StrikeMe::BTC.new((self * 100_000_000).round)
end
def sats
StrikeMe::BTC.new(round)
end
end
class Integer
def btc
StrikeMe::BTC.new(self * 100_000_000)
end
def sats
StrikeMe::BTC.new(self)
end
end
module StrikeMe
class BTC
class Error < StandardError; end
MAX_NUMBER_OF_SATS = 21_000_000 * 100_000_000
NUMERICAL_OPERATORS = %i[* /].freeze
attr_reader :sats
def initialize(sats)
raise Error, 'Must be initialized using an integer representing sats' unless sats.is_a? Integer
raise Error, "Max sats is: #{MAX_NUMBER_OF_SATS}. You have: #{sats}" if sats > MAX_NUMBER_OF_SATS
raise Error, 'Must be initialized using a positive number' if sats.negative?
@sats = sats
end
def to_s
# to_f.to_s
format('%.8f', to_f)
end
def to_f
sats.to_f / 100_000_000
end
def as_json
to_s
end
def *(other)
self.class.new((sats * other).to_i)
end
def /(other)
self.class.new((sats / other).to_i)
end
def +(other)
raise Error, "Addition can only be performed using a #{self.class.name} object" unless other.is_a?(self.class)
self.class.new(sats + other.sats)
end
def -(other)
raise Error, "Addition can only be performed using a #{self.class.name} object" unless other.is_a?(self.class)
self.class.new(sats - other.sats)
end
def ==(other)
case other
when Integer then sats == other
when BTC then sats == other.sats
else
false
end
end
end
end
+19
View File
@@ -0,0 +1,19 @@
# frozen_string_literal: true
module StrikeMe
class BtcAttributeType < ActiveModel::Type::Value
def type
:btc
end
def cast(value)
StrikeMe::BTC.new((100_000_000 * value.to_f).to_i)
end
def serialize(value)
value.to_s
end
end
end
ActiveModel::Type.register(:btc, StrikeMe::BtcAttributeType)