kobana 0.2.3 → 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 +50 -0
- 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
- metadata +5 -6
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
|
@@ -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"
|
metadata
CHANGED
@@ -1,15 +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
|
-
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: faraday
|
@@ -72,7 +71,9 @@ files:
|
|
72
71
|
- README.md
|
73
72
|
- Rakefile
|
74
73
|
- lib/kobana.rb
|
74
|
+
- lib/kobana/client.rb
|
75
75
|
- lib/kobana/configuration.rb
|
76
|
+
- lib/kobana/errors.rb
|
76
77
|
- lib/kobana/resources/admin/subaccount.rb
|
77
78
|
- lib/kobana/resources/base.rb
|
78
79
|
- lib/kobana/resources/charge/bank_billet.rb
|
@@ -96,7 +97,6 @@ metadata:
|
|
96
97
|
bug_tracker_uri: https://github.com/universokobana/kobana-ruby-client/issues
|
97
98
|
documentation_uri: https://github.com/universokobana/kobana-ruby-client/wiki
|
98
99
|
rubygems_mfa_required: 'true'
|
99
|
-
post_install_message:
|
100
100
|
rdoc_options: []
|
101
101
|
require_paths:
|
102
102
|
- lib
|
@@ -111,8 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
111
111
|
- !ruby/object:Gem::Version
|
112
112
|
version: '0'
|
113
113
|
requirements: []
|
114
|
-
rubygems_version: 3.
|
115
|
-
signing_key:
|
114
|
+
rubygems_version: 3.7.1
|
116
115
|
specification_version: 4
|
117
116
|
summary: Kobana API Client
|
118
117
|
test_files: []
|