kobana 0.2.2 → 0.3.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/README.md +53 -3
- data/lib/kobana/client.rb +60 -0
- data/lib/kobana/errors.rb +28 -0
- data/lib/kobana/resources/base.rb +16 -2
- data/lib/kobana/resources/connection.rb +28 -6
- data/lib/kobana/resources/operations.rb +18 -2
- data/lib/kobana/version.rb +1 -1
- data/lib/kobana.rb +2 -0
- data/lib/support/hash.rb +23 -9
- data/lib/support/string.rb +21 -15
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2c043850d8aa7c1f2df33d4956d25b6606c7318ec32350589ddd2da9fe75ea28
|
4
|
+
data.tar.gz: 81f645783198fa6a2e48f5fd660200c90c8c85cfe8439a4ec4de29a4fe299b0b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d165e5d9808c537d5966edaed2734c11b7f1679fd8d98baa37b82870a2f9a2bd9bc104af292af11caabeffd1a7ab28f9e66400d675934646a017cde0a4e3d0e3
|
7
|
+
data.tar.gz: 25e70219061793ec35b1747d193cbee5b524a9abb82a7fc102071b423391a9c18257453fc19c309ef45c76b94363767e1436150d2382598df3b761f866b1c97b
|
data/README.md
CHANGED
@@ -36,6 +36,8 @@ $ gem install kobana
|
|
36
36
|
|
37
37
|
### Configuration
|
38
38
|
|
39
|
+
#### Global Configuration (Single API Token)
|
40
|
+
|
39
41
|
Configure your API key by creating an initializer in your Rails project:
|
40
42
|
|
41
43
|
`config/initializers/kobana.rb`
|
@@ -49,12 +51,52 @@ end
|
|
49
51
|
|
50
52
|
Replace `'YOUR_API_TOKEN'` with your actual API key from the corresponding environment.
|
51
53
|
|
54
|
+
#### Multi-Client Configuration (Multiple API Tokens)
|
55
|
+
|
56
|
+
For applications that need to work with multiple Kobana accounts simultaneously (e.g., multi-tenant applications), you can create multiple client instances:
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
# Create separate clients for different accounts
|
60
|
+
client1 = Kobana::Client.new(
|
61
|
+
api_token: 'CLIENT1_API_TOKEN',
|
62
|
+
environment: :production
|
63
|
+
)
|
64
|
+
|
65
|
+
client2 = Kobana::Client.new(
|
66
|
+
api_token: 'CLIENT2_API_TOKEN',
|
67
|
+
environment: :sandbox
|
68
|
+
)
|
69
|
+
|
70
|
+
# Use client-specific resources
|
71
|
+
pix1 = client1.charge.pix.create(attributes)
|
72
|
+
pix2 = client2.charge.pix.create(attributes)
|
73
|
+
|
74
|
+
# Each client maintains its own configuration
|
75
|
+
account1 = client1.financial.account.find(account_id)
|
76
|
+
account2 = client2.financial.account.find(account_id)
|
77
|
+
```
|
78
|
+
|
79
|
+
You can also configure clients after initialization:
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
client = Kobana::Client.new
|
83
|
+
client.configure do |config|
|
84
|
+
config.api_token = 'YOUR_API_TOKEN'
|
85
|
+
config.environment = :production
|
86
|
+
config.custom_headers = { 'X-Custom-Header' => 'Value' }
|
87
|
+
config.debug = true
|
88
|
+
end
|
89
|
+
```
|
90
|
+
|
52
91
|
### Usage
|
53
92
|
|
93
|
+
The gem supports both global configuration (backward compatible) and multi-client usage patterns.
|
94
|
+
|
54
95
|
#### **Charges**
|
55
96
|
|
56
97
|
##### Creating a Charge
|
57
98
|
|
99
|
+
Using global configuration:
|
58
100
|
```ruby
|
59
101
|
attributes = {
|
60
102
|
'amount' => 100.50,
|
@@ -71,7 +113,15 @@ attributes = {
|
|
71
113
|
'custom_data' => '{"order_id": "12345"}'
|
72
114
|
}
|
73
115
|
|
116
|
+
# Global configuration approach (backward compatible)
|
74
117
|
pix = Kobana::Resources::Charge::Pix.create(attributes)
|
118
|
+
```
|
119
|
+
|
120
|
+
Using client-specific configuration:
|
121
|
+
```ruby
|
122
|
+
# Client-specific approach
|
123
|
+
client = Kobana::Client.new(api_token: 'YOUR_TOKEN')
|
124
|
+
pix = client.charge.pix.create(attributes)
|
75
125
|
pix.id # 1
|
76
126
|
pix.new_record? false
|
77
127
|
pix.created? # true
|
@@ -110,7 +160,7 @@ puts result
|
|
110
160
|
```ruby
|
111
161
|
attributes = { ... }
|
112
162
|
|
113
|
-
bank_billet = Kobana::Resources::
|
163
|
+
bank_billet = Kobana::Resources::Charge::BankBillet.create(attributes)
|
114
164
|
puts bank_billet
|
115
165
|
```
|
116
166
|
|
@@ -118,13 +168,13 @@ puts bank_billet
|
|
118
168
|
|
119
169
|
```ruby
|
120
170
|
bank_billet_id = 1 # Replace with your charge ID
|
121
|
-
bank_billet = Kobana::Resources::
|
171
|
+
bank_billet = Kobana::Resources::Charge::BankBillet.find(bank_billet_id)
|
122
172
|
puts bank_billet
|
123
173
|
```
|
124
174
|
|
125
175
|
##### Listing All Bank Billets
|
126
176
|
|
127
177
|
```ruby
|
128
|
-
bank_billets = Kobana::Resources::
|
178
|
+
bank_billets = Kobana::Resources::Charge::BankBillet.all
|
129
179
|
puts bank_billets
|
130
180
|
```
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kobana
|
4
|
+
class Client
|
5
|
+
attr_reader :configuration
|
6
|
+
|
7
|
+
def initialize(api_token: nil, environment: :sandbox, custom_headers: {}, debug: false, **extra_config)
|
8
|
+
@configuration = Configuration.new
|
9
|
+
@configuration.api_token = api_token
|
10
|
+
@configuration.environment = environment
|
11
|
+
@configuration.custom_headers = custom_headers
|
12
|
+
@configuration.debug = debug
|
13
|
+
|
14
|
+
# Allow any additional configuration options
|
15
|
+
extra_config.each do |key, value|
|
16
|
+
@configuration.public_send("#{key}=", value) if @configuration.respond_to?("#{key}=")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def configure
|
21
|
+
yield(@configuration) if block_given?
|
22
|
+
end
|
23
|
+
|
24
|
+
# Dynamically create resource accessors with metaprogramming
|
25
|
+
%i[charge financial admin].each do |resource_type|
|
26
|
+
define_method(resource_type) do
|
27
|
+
instance_variable_get("@#{resource_type}") ||
|
28
|
+
instance_variable_set("@#{resource_type}",
|
29
|
+
ResourceProxy.new(self, Resources.const_get(resource_type.to_s.capitalize)))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Generic proxy for all resource modules
|
34
|
+
class ResourceProxy
|
35
|
+
def initialize(client, module_ref)
|
36
|
+
@client = client
|
37
|
+
@module = module_ref
|
38
|
+
end
|
39
|
+
|
40
|
+
def method_missing(method_name, *args, &)
|
41
|
+
# Convert method name to class name (e.g., :pix => Pix, :account_balance => AccountBalance)
|
42
|
+
class_name = method_name.to_s.split("_").map(&:capitalize).join
|
43
|
+
|
44
|
+
# Try to find the resource class
|
45
|
+
if @module.const_defined?(class_name)
|
46
|
+
resource_class = @module.const_get(class_name)
|
47
|
+
# Return a client-bound version of the class
|
48
|
+
resource_class.with_client(@client)
|
49
|
+
else
|
50
|
+
super
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def respond_to_missing?(method_name, include_private = false)
|
55
|
+
class_name = method_name.to_s.split("_").map(&:capitalize).join
|
56
|
+
@module.const_defined?(class_name) || super
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kobana
|
4
|
+
class Error < StandardError; end
|
5
|
+
|
6
|
+
class ConfigurationError < Error; end
|
7
|
+
class ConnectionError < Error; end
|
8
|
+
class ResourceNotFoundError < Error; end
|
9
|
+
class UnauthorizedError < Error; end
|
10
|
+
class ValidationError < Error; end
|
11
|
+
|
12
|
+
class APIError < Error
|
13
|
+
attr_reader :status, :response_body, :errors
|
14
|
+
|
15
|
+
def initialize(message = nil, status: nil, response_body: nil, errors: nil)
|
16
|
+
@status = status
|
17
|
+
@response_body = response_body
|
18
|
+
@errors = errors
|
19
|
+
super(message || default_message)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def default_message
|
25
|
+
"API request failed with status #{status}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -10,7 +10,16 @@ module Kobana
|
|
10
10
|
include Operations
|
11
11
|
|
12
12
|
class << self
|
13
|
-
attr_accessor :primary_key, :api_version, :resource_endpoint, :errors, :default_attributes
|
13
|
+
attr_accessor :primary_key, :api_version, :resource_endpoint, :errors, :default_attributes, :client
|
14
|
+
|
15
|
+
def with_client(client)
|
16
|
+
@client_classes ||= {}.compare_by_identity
|
17
|
+
@client_classes[client] ||= begin
|
18
|
+
klass = Class.new(self)
|
19
|
+
klass.client = client
|
20
|
+
klass
|
21
|
+
end
|
22
|
+
end
|
14
23
|
|
15
24
|
def inherited(subclass)
|
16
25
|
super
|
@@ -34,7 +43,7 @@ module Kobana
|
|
34
43
|
end
|
35
44
|
|
36
45
|
def interpolate(template, attributes)
|
37
|
-
template.gsub(/\{([
|
46
|
+
template.gsub(/\{([^}]+)\}/) do
|
38
47
|
key = Regexp.last_match(1)
|
39
48
|
begin
|
40
49
|
if key.include?(".")
|
@@ -62,6 +71,11 @@ module Kobana
|
|
62
71
|
@errors = []
|
63
72
|
end
|
64
73
|
|
74
|
+
# Access to client configuration through class
|
75
|
+
def client
|
76
|
+
self.class.client
|
77
|
+
end
|
78
|
+
|
65
79
|
def [](key)
|
66
80
|
attributes[key.to_sym]
|
67
81
|
end
|
@@ -22,23 +22,44 @@ module Kobana
|
|
22
22
|
|
23
23
|
module ClassMethods
|
24
24
|
def headers
|
25
|
+
config = client&.configuration || Kobana.configuration
|
25
26
|
{
|
26
|
-
"Authorization" => "Bearer #{
|
27
|
+
"Authorization" => "Bearer #{config.api_token}",
|
27
28
|
"Content-Type" => "application/json"
|
28
|
-
}.merge(
|
29
|
+
}.merge(config.custom_headers)
|
29
30
|
end
|
30
31
|
|
31
32
|
def connection
|
33
|
+
config = client&.configuration || Kobana.configuration
|
34
|
+
# Don't cache connection when debugging to ensure logger works
|
35
|
+
return build_connection(config) if config.debug
|
36
|
+
|
37
|
+
@connection ||= build_connection(config)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def build_connection(config)
|
32
43
|
Faraday.new(url: base_url) do |faraday|
|
33
44
|
faraday.request :url_encoded
|
34
45
|
faraday.request :json
|
35
46
|
faraday.adapter Faraday.default_adapter
|
36
47
|
faraday.headers = headers
|
37
|
-
faraday.response :logger, logger if
|
48
|
+
faraday.response :logger, logger if config.debug
|
38
49
|
end
|
39
50
|
end
|
40
51
|
|
52
|
+
public
|
53
|
+
|
41
54
|
def multipart_connection
|
55
|
+
config = client&.configuration || Kobana.configuration
|
56
|
+
# Don't cache connection when debugging
|
57
|
+
return build_multipart_connection(config) if config.debug
|
58
|
+
|
59
|
+
@multipart_connection ||= build_multipart_connection(config)
|
60
|
+
end
|
61
|
+
|
62
|
+
def build_multipart_connection(config)
|
42
63
|
Faraday.new(url: base_url) do |faraday|
|
43
64
|
faraday.request :multipart
|
44
65
|
faraday.request :url_encoded
|
@@ -46,14 +67,14 @@ module Kobana
|
|
46
67
|
faraday.headers = headers.merge(
|
47
68
|
"Content-Type" => "multipart/form-data"
|
48
69
|
)
|
49
|
-
faraday.response :logger, logger if
|
70
|
+
faraday.response :logger, logger if config.debug
|
50
71
|
end
|
51
72
|
end
|
52
73
|
|
53
74
|
def logger
|
54
75
|
logger = Logger.new($stdout)
|
55
76
|
logger.formatter = proc do |severity, datetime, _progname, msg|
|
56
|
-
redacted_msg = msg.gsub(/(Bearer|Token)\s+[A-Za-z0-9\-_
|
77
|
+
redacted_msg = msg.gsub(/(Bearer|Token)\s+[A-Za-z0-9\-_.]+/, '\1 [REDACTED]')
|
57
78
|
"#{severity} #{datetime}: #{redacted_msg}\n"
|
58
79
|
end
|
59
80
|
logger
|
@@ -84,7 +105,8 @@ module Kobana
|
|
84
105
|
end
|
85
106
|
|
86
107
|
def base_url
|
87
|
-
|
108
|
+
config = client&.configuration || Kobana.configuration
|
109
|
+
BASE_URI[api_version&.to_sym][config.environment&.to_sym]
|
88
110
|
end
|
89
111
|
end
|
90
112
|
end
|
@@ -60,7 +60,7 @@ module Kobana
|
|
60
60
|
if options[:find_by_id]
|
61
61
|
find(params, options[:find_params]) || create(attributes)
|
62
62
|
else
|
63
|
-
find_by(params, options) || create(attributes.merge(params.deep_symbolize_keys))
|
63
|
+
find_by(params, options) || create(attributes.merge(params.deep_symbolize_keys), options)
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
@@ -72,6 +72,22 @@ module Kobana
|
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
|
+
def save
|
76
|
+
if new_record?
|
77
|
+
response = self.class.create(attributes)
|
78
|
+
if response.created?
|
79
|
+
@attributes = response.attributes
|
80
|
+
@errors = []
|
81
|
+
true
|
82
|
+
else
|
83
|
+
@errors = response.errors
|
84
|
+
false
|
85
|
+
end
|
86
|
+
else
|
87
|
+
update({})
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
75
91
|
def update(new_attributes = {})
|
76
92
|
return if new_attributes.empty?
|
77
93
|
|
@@ -79,7 +95,7 @@ module Kobana
|
|
79
95
|
response = request(:put, uri, data.to_json)
|
80
96
|
case response[:status]
|
81
97
|
when 200..204
|
82
|
-
new(response[:data].merge(updated: true))
|
98
|
+
self.class.new(response[:data].merge(updated: true))
|
83
99
|
else
|
84
100
|
handle_error_response(response)
|
85
101
|
resource = self.class.new(attributes.merge(updated: false))
|
data/lib/kobana/version.rb
CHANGED
data/lib/kobana.rb
CHANGED
@@ -7,6 +7,7 @@ require "json"
|
|
7
7
|
require "support/string"
|
8
8
|
require "support/hash"
|
9
9
|
require "kobana/configuration"
|
10
|
+
require "kobana/errors"
|
10
11
|
|
11
12
|
module Kobana
|
12
13
|
class << self
|
@@ -22,6 +23,7 @@ module Kobana
|
|
22
23
|
end
|
23
24
|
|
24
25
|
autoload :Version, "kobana/version"
|
26
|
+
autoload :Client, "kobana/client"
|
25
27
|
|
26
28
|
module Resources
|
27
29
|
autoload :Base, "kobana/resources/base"
|
data/lib/support/hash.rb
CHANGED
@@ -1,21 +1,35 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
unless defined?(ActiveSupport)
|
4
|
+
class Hash
|
5
|
+
unless method_defined?(:deep_symbolize_keys)
|
6
|
+
def deep_symbolize_keys
|
7
|
+
result = {}
|
8
|
+
each do |key, value|
|
9
|
+
sym_key = key.respond_to?(:to_sym) ? key.to_sym : key
|
10
|
+
result[sym_key] = deep_symbolize_value(value)
|
11
|
+
end
|
12
|
+
result
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def deep_symbolize_value(value)
|
9
18
|
case value
|
10
19
|
when Hash
|
11
20
|
value.deep_symbolize_keys
|
12
21
|
when Array
|
13
|
-
value
|
22
|
+
deep_symbolize_array(value)
|
14
23
|
else
|
15
24
|
value
|
16
25
|
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def deep_symbolize_array(array)
|
29
|
+
array.map do |element|
|
30
|
+
element.is_a?(Hash) ? element.deep_symbolize_keys : element
|
31
|
+
end
|
32
|
+
end
|
17
33
|
end
|
18
|
-
result
|
19
34
|
end
|
20
|
-
# rubocop:enable Metrics/MethodLength
|
21
35
|
end
|
data/lib/support/string.rb
CHANGED
@@ -1,21 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
3
|
+
unless defined?(ActiveSupport)
|
4
|
+
class String
|
5
|
+
unless method_defined?(:pluralize)
|
6
|
+
def pluralize
|
7
|
+
if end_with?("y") && !%w[a e i o u].include?(self[-2].downcase)
|
8
|
+
"#{self[0..-2]}ies"
|
9
|
+
elsif end_with?("s", "sh", "ch", "x", "z")
|
10
|
+
"#{self}es"
|
11
|
+
else
|
12
|
+
"#{self}s"
|
13
|
+
end
|
14
|
+
end
|
11
15
|
end
|
12
|
-
end
|
13
16
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
unless method_defined?(:underscore)
|
18
|
+
def underscore
|
19
|
+
word = gsub("::", "/")
|
20
|
+
word.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
21
|
+
word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
22
|
+
word.tr!("-", "_")
|
23
|
+
word.downcase
|
24
|
+
end
|
25
|
+
end
|
20
26
|
end
|
21
27
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kobana
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kivanio Barbosa
|
8
8
|
- Rafael Lima
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -71,7 +71,9 @@ files:
|
|
71
71
|
- README.md
|
72
72
|
- Rakefile
|
73
73
|
- lib/kobana.rb
|
74
|
+
- lib/kobana/client.rb
|
74
75
|
- lib/kobana/configuration.rb
|
76
|
+
- lib/kobana/errors.rb
|
75
77
|
- lib/kobana/resources/admin/subaccount.rb
|
76
78
|
- lib/kobana/resources/base.rb
|
77
79
|
- lib/kobana/resources/charge/bank_billet.rb
|
@@ -109,7 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
109
111
|
- !ruby/object:Gem::Version
|
110
112
|
version: '0'
|
111
113
|
requirements: []
|
112
|
-
rubygems_version: 3.
|
114
|
+
rubygems_version: 3.7.1
|
113
115
|
specification_version: 4
|
114
116
|
summary: Kobana API Client
|
115
117
|
test_files: []
|