alpha_card 0.3.0 → 0.4.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/.travis.yml +4 -4
- data/CHANGELOG.md +21 -3
- data/Gemfile +0 -2
- data/Gemfile.lock +6 -24
- data/README.md +115 -85
- data/ROADMAP.md +13 -9
- data/alpha_card.gemspec +3 -4
- data/lib/alpha_card.rb +44 -72
- data/lib/alpha_card/account.rb +51 -0
- data/lib/alpha_card/attribute.rb +337 -0
- data/lib/alpha_card/data/credit_card_codes.yml +54 -54
- data/lib/alpha_card/errors/invalid_attribute_format.rb +14 -0
- data/lib/alpha_card/errors/invalid_attribute_type.rb +14 -0
- data/lib/alpha_card/errors/invalid_attribute_value.rb +14 -0
- data/lib/alpha_card/errors/{invalid_object_error.rb → validation_error.rb} +1 -1
- data/lib/alpha_card/{alpha_card_object.rb → resource.rb} +14 -28
- data/lib/alpha_card/resources/billing.rb +29 -0
- data/lib/alpha_card/{objects → resources}/order.rb +8 -8
- data/lib/alpha_card/{objects → resources}/shipping.rb +15 -13
- data/lib/alpha_card/{alpha_card_response.rb → response.rb} +21 -6
- data/lib/alpha_card/transaction.rb +30 -0
- data/lib/alpha_card/transactions/auth.rb +18 -0
- data/lib/alpha_card/transactions/capture.rb +32 -0
- data/lib/alpha_card/transactions/credit.rb +18 -0
- data/lib/alpha_card/{objects → transactions}/refund.rb +9 -2
- data/lib/alpha_card/transactions/sale.rb +91 -0
- data/lib/alpha_card/transactions/update.rb +61 -0
- data/lib/alpha_card/transactions/validate.rb +21 -0
- data/lib/alpha_card/transactions/void.rb +26 -0
- data/lib/alpha_card/version.rb +1 -1
- data/spec/alpha_card/attribute_spec.rb +126 -0
- data/spec/alpha_card/response_spec.rb +8 -4
- data/spec/alpha_card/transactions/auth_spec.rb +43 -0
- data/spec/alpha_card/{objects → transactions}/capture_spec.rb +11 -12
- data/spec/alpha_card/transactions/credit_spec.rb +102 -0
- data/spec/alpha_card/{objects → transactions}/refund_spec.rb +4 -4
- data/spec/alpha_card/{objects → transactions}/sale_spec.rb +42 -41
- data/spec/alpha_card/{objects → transactions}/update_spec.rb +4 -4
- data/spec/alpha_card/transactions/validate_spec.rb +100 -0
- data/spec/alpha_card/{objects → transactions}/void_spec.rb +11 -11
- data/spec/spec_helper.rb +4 -0
- metadata +36 -47
- data/lib/alpha_card/errors/alpha_card_error.rb +0 -29
- data/lib/alpha_card/objects/account.rb +0 -48
- data/lib/alpha_card/objects/billing.rb +0 -31
- data/lib/alpha_card/objects/capture.rb +0 -51
- data/lib/alpha_card/objects/sale.rb +0 -82
- data/lib/alpha_card/objects/update.rb +0 -54
- data/lib/alpha_card/objects/void.rb +0 -45
- data/spec/alpha_card/objects/account_spec.rb +0 -20
- data/spec/alpha_card/objects/deprecated_methods_spec.rb +0 -32
data/ROADMAP.md
CHANGED
@@ -1,12 +1,16 @@
|
|
1
1
|
# TODO
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
-
|
7
|
-
-
|
8
|
-
-
|
9
|
-
|
10
|
-
-
|
3
|
+
## 0.4.0
|
4
|
+
|
5
|
+
* ~~Own `Attribute` class with validations and etc~~:
|
6
|
+
- ~~default value~~
|
7
|
+
- ~~writable: true` / `false`~~
|
8
|
+
- ~~required: `true / `false~~
|
9
|
+
- ~~format: regex~~
|
10
|
+
- ~~values: ['1', '2']`~~
|
11
|
+
- ~~type(s)~~
|
12
|
+
* ~~Configuration with `Account` setup~~
|
13
|
+
- ~~all objects can use global or local account~~
|
11
14
|
* Add new transactions:
|
12
|
-
- Authorization
|
15
|
+
- ~~Authorization~~/~~Credit~~/~~Validate~~/Offline
|
16
|
+
* ~~Change all resources return value to AlphaCard::Response~~
|
data/alpha_card.gemspec
CHANGED
@@ -5,7 +5,7 @@ require 'alpha_card/version'
|
|
5
5
|
Gem::Specification.new do |gem|
|
6
6
|
gem.name = 'alpha_card'
|
7
7
|
gem.version = AlphaCard.gem_version
|
8
|
-
gem.date = '2016-
|
8
|
+
gem.date = '2016-10-07'
|
9
9
|
gem.summary = 'Alpha Card Services API for Ruby'
|
10
10
|
gem.description = 'Gem for creating sales with Alpha Card Services'
|
11
11
|
gem.authors = ['Nikita Bulaj']
|
@@ -14,10 +14,9 @@ Gem::Specification.new do |gem|
|
|
14
14
|
gem.files = `git ls-files`.split($RS)
|
15
15
|
gem.homepage = 'http://github.com/nbulaj/alpha_card'
|
16
16
|
gem.license = 'MIT'
|
17
|
-
gem.required_ruby_version = '>= 2.
|
17
|
+
gem.required_ruby_version = '>= 2.2.2'
|
18
18
|
|
19
|
-
gem.add_runtime_dependency '
|
20
|
-
gem.add_runtime_dependency 'rack', '~> 1.2', '>= 1.2'
|
19
|
+
gem.add_runtime_dependency 'rack', '~> 2.0', '>= 2.0'
|
21
20
|
|
22
21
|
gem.add_development_dependency 'rspec', '~> 3.5'
|
23
22
|
end
|
data/lib/alpha_card.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# encoding:utf-8
|
2
2
|
require 'yaml'
|
3
|
-
require 'virtus'
|
4
3
|
require 'net/http'
|
5
4
|
require 'uri'
|
6
5
|
require 'rack/utils'
|
@@ -8,24 +7,33 @@ require 'rack/utils'
|
|
8
7
|
# Version
|
9
8
|
require 'alpha_card/version'
|
10
9
|
|
11
|
-
require 'alpha_card/
|
12
|
-
require 'alpha_card/
|
10
|
+
require 'alpha_card/account'
|
11
|
+
require 'alpha_card/attribute'
|
12
|
+
require 'alpha_card/resource'
|
13
|
+
require 'alpha_card/response'
|
14
|
+
require 'alpha_card/transaction'
|
13
15
|
|
14
16
|
# Errors
|
15
|
-
require 'alpha_card/errors/alpha_card_error'
|
16
17
|
require 'alpha_card/errors/api_connection_error'
|
17
|
-
require 'alpha_card/errors/
|
18
|
+
require 'alpha_card/errors/invalid_attribute_value'
|
19
|
+
require 'alpha_card/errors/invalid_attribute_format'
|
20
|
+
require 'alpha_card/errors/invalid_attribute_type'
|
21
|
+
require 'alpha_card/errors/validation_error'
|
18
22
|
|
19
23
|
# Alpha Card Resources
|
20
|
-
require 'alpha_card/
|
21
|
-
require 'alpha_card/
|
22
|
-
require 'alpha_card/
|
23
|
-
|
24
|
-
|
25
|
-
require 'alpha_card/
|
26
|
-
require 'alpha_card/
|
27
|
-
require 'alpha_card/
|
28
|
-
require 'alpha_card/
|
24
|
+
require 'alpha_card/resources/billing'
|
25
|
+
require 'alpha_card/resources/shipping'
|
26
|
+
require 'alpha_card/resources/order'
|
27
|
+
|
28
|
+
# Alpha Card Transactions
|
29
|
+
require 'alpha_card/transactions/capture'
|
30
|
+
require 'alpha_card/transactions/void'
|
31
|
+
require 'alpha_card/transactions/refund'
|
32
|
+
require 'alpha_card/transactions/sale'
|
33
|
+
require 'alpha_card/transactions/update'
|
34
|
+
require 'alpha_card/transactions/auth'
|
35
|
+
require 'alpha_card/transactions/credit'
|
36
|
+
require 'alpha_card/transactions/validate'
|
29
37
|
|
30
38
|
##
|
31
39
|
# AlphaCard is a library for processing payments with Alpha Card Services, Inc.
|
@@ -49,88 +57,55 @@ module AlphaCard
|
|
49
57
|
# specified account. Request must contains params - Alpha Card
|
50
58
|
# transaction variables.
|
51
59
|
#
|
52
|
-
# @param [Hash]
|
53
|
-
# Alpha Card transaction variables
|
54
|
-
# @param [AlphaCard::Account] account
|
55
|
-
# An <code>AlphaCard::Account</code> credentials object.
|
60
|
+
# @param params [Hash]
|
61
|
+
# hash with Alpha Card transaction variables and it's values
|
56
62
|
#
|
57
|
-
# @
|
63
|
+
# @param credentials [Hash]
|
64
|
+
# Alpha Card merchant account credentials
|
65
|
+
#
|
66
|
+
# @return [AlphaCard::Response]
|
58
67
|
# Response from Alpha Card Gateway.
|
59
68
|
#
|
60
|
-
# @raise [
|
61
|
-
# AlphaCardError Exception if request failed.
|
69
|
+
# @raise [APIConnectionError] HTTP request error
|
62
70
|
#
|
63
71
|
# @example
|
64
|
-
# account = AlphaCard::Account.new('demo', 'password')
|
65
72
|
# response = AlphaCard.request(
|
66
|
-
# account,
|
67
73
|
# {
|
68
74
|
# cexp: '0720',
|
69
75
|
# ccnumber: '4111111111111111',
|
70
76
|
# amount: '10.00'
|
77
|
+
# },
|
78
|
+
# {
|
79
|
+
# username: 'demo',
|
80
|
+
# password: 'password'
|
71
81
|
# }
|
72
82
|
# )
|
73
83
|
#
|
74
|
-
# #=> #<AlphaCard::
|
84
|
+
# #=> #<AlphaCard::Response:0x1a0fda8 @data={"response"=>"1",
|
75
85
|
# "responsetext"=>"SUCCESS", "authcode"=>"123", "transactionid"=>"123",
|
76
86
|
# "avsresponse"=>"", "cvvresponse"=>"N", "orderid"=>"", "type"=>"",
|
77
87
|
# "response_code"=>"100"}>
|
78
88
|
#
|
79
|
-
|
80
|
-
|
81
|
-
# account,
|
82
|
-
# {
|
83
|
-
# cexp: '0720',
|
84
|
-
# ccnumber: '123',
|
85
|
-
# amount: '10.00'
|
86
|
-
# }
|
87
|
-
# )
|
88
|
-
#
|
89
|
-
# #=> AlphaCard::AlphaCardError: AlphaCard::AlphaCardError
|
90
|
-
def request(account, params = {})
|
91
|
-
raise AlphaCardError, 'You must set credentials to create the sale!' unless account.filled?
|
89
|
+
def request(params = {}, credentials = Account.credentials)
|
90
|
+
raise ArgumentError, 'You must pass a Hash with Account credentials!' unless Account.valid_credentials?(credentials)
|
92
91
|
|
93
92
|
begin
|
94
|
-
response = http_post_request(@api_base, params.merge(
|
93
|
+
response = http_post_request(@api_base, params.merge(credentials))
|
95
94
|
rescue => e
|
96
95
|
handle_connection_errors(e)
|
97
96
|
end
|
98
97
|
|
99
|
-
|
100
|
-
handle_alpha_card_errors(alpha_card_response)
|
101
|
-
|
102
|
-
alpha_card_response
|
103
|
-
end
|
104
|
-
|
105
|
-
##
|
106
|
-
# Raises an exception if Alpha Card Gateway return an error or
|
107
|
-
# decline code for the request. Message is taken from the Global
|
108
|
-
# Payment Systems Credit Card Authorization Codes (codes.yml).
|
109
|
-
# If code wasn't found in <code>CREDIT_CARD_CODES</code>, then
|
110
|
-
# message = Alpha Card response text.
|
111
|
-
#
|
112
|
-
# @param [AlphaCard::AlphaCardResponse] response
|
113
|
-
# Alpha Card Response object.
|
114
|
-
#
|
115
|
-
# @raise [AlphaCard::AlphaCardError]
|
116
|
-
# AlphaCardError Exception if request failed.
|
117
|
-
#
|
118
|
-
# @return [String]
|
119
|
-
# Alpha Card Services response text.
|
120
|
-
def handle_alpha_card_errors(response)
|
121
|
-
code = response.text
|
122
|
-
raise AlphaCardError.new(CREDIT_CARD_CODES[code] || code, response) unless response.success?
|
98
|
+
Response.new(response.body)
|
123
99
|
end
|
124
100
|
|
125
101
|
##
|
126
102
|
# Raises an exception if a network error occurs. It
|
127
103
|
# could be request timeout, socket error or anything else.
|
128
104
|
#
|
129
|
-
# @param [
|
130
|
-
# Exception object.
|
105
|
+
# @param error [Exception] exception object
|
131
106
|
#
|
132
|
-
# @raise [
|
133
|
-
#
|
107
|
+
# @raise [APIConnectionError]
|
108
|
+
# Failed request exception.
|
134
109
|
def handle_connection_errors(error)
|
135
110
|
case error
|
136
111
|
when Timeout::Error, Errno::EINVAL, Errno::ECONNRESET
|
@@ -150,13 +125,10 @@ module AlphaCard
|
|
150
125
|
end
|
151
126
|
|
152
127
|
##
|
153
|
-
# Send secure HTTP(S) request with params
|
154
|
-
# to requested URL.
|
128
|
+
# Send secure HTTP(S) request with params to requested URL.
|
155
129
|
#
|
156
|
-
# @param [String]
|
157
|
-
#
|
158
|
-
# @param [Hash] params
|
159
|
-
# Hash of params for the request
|
130
|
+
# @param url [String] URL
|
131
|
+
# @param params [Hash] hash of params for the request
|
160
132
|
#
|
161
133
|
# @return [HTTPResponse]
|
162
134
|
# Response of the request as HTTPResponse object
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module AlphaCard
|
2
|
+
# AlphaCard Account class for global credentials settings
|
3
|
+
class Account
|
4
|
+
class << self
|
5
|
+
# Global Alpha Card Merchant account credentials
|
6
|
+
#
|
7
|
+
# @return [String] username
|
8
|
+
# @return [String] password
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# AlphaCard::Account.username = 'demo'
|
12
|
+
# AlphaCard::Account.password = 'password'
|
13
|
+
#
|
14
|
+
attr_accessor :username, :password
|
15
|
+
|
16
|
+
##
|
17
|
+
# Setups demo Alpha Card credentials
|
18
|
+
def use_demo_credentials!
|
19
|
+
self.username = 'demo'
|
20
|
+
self.password = 'password'
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# Checks credentials not to be nil or empty string
|
25
|
+
#
|
26
|
+
# @param credentials [Hash] hash with :username and :password keys
|
27
|
+
#
|
28
|
+
# @return [Bool] true if credentials present, false in other cases
|
29
|
+
#
|
30
|
+
def valid_credentials?(credentials)
|
31
|
+
!credentials[:username].to_s.empty? && !credentials[:password].to_s.empty?
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# Returns hash with Alpha Card credentials
|
36
|
+
#
|
37
|
+
# @return [Hash] credentials
|
38
|
+
#
|
39
|
+
# @example
|
40
|
+
# AlphaCard::Account.username = 'john.doe'
|
41
|
+
# AlphaCard::Account.password = '123qwe!s'
|
42
|
+
#
|
43
|
+
# AlphaCard::Account.credentials
|
44
|
+
# #=> { username: "john.doe", password: "123qwe!s" }
|
45
|
+
#
|
46
|
+
def credentials
|
47
|
+
{ username: username, password: password }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,337 @@
|
|
1
|
+
module AlphaCard
|
2
|
+
# Attribute DSL for Alpha Card transaction variables
|
3
|
+
module Attribute
|
4
|
+
# Extends base class with Attributes DSL.
|
5
|
+
#
|
6
|
+
# @param base [Class] baseclass
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# class User
|
10
|
+
# include AlphaCard::Attribute
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
def self.included(base)
|
14
|
+
base.extend(ClassMethods)
|
15
|
+
base.send(:include, InstanceMethods)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Attributes class methods
|
19
|
+
# * attribute
|
20
|
+
# * remove_attribute
|
21
|
+
# * attributes_set
|
22
|
+
#
|
23
|
+
module ClassMethods
|
24
|
+
# Defines Attributes Set for the class.
|
25
|
+
# Attributes set contains all the attributes names as key
|
26
|
+
# and it's options as the value.
|
27
|
+
#
|
28
|
+
# @return [Hash] attributes set with options
|
29
|
+
def attributes_set
|
30
|
+
@attributes_set ||= {}
|
31
|
+
end
|
32
|
+
|
33
|
+
# Adds attribute to the class.
|
34
|
+
# Defines reader and writer methods based on options hash.
|
35
|
+
# Adds attribute to the global Attributes Set.
|
36
|
+
#
|
37
|
+
# @param name [Symbol, String] attribute name
|
38
|
+
# @param options [Hash] attribute options
|
39
|
+
#
|
40
|
+
# @example
|
41
|
+
# class User
|
42
|
+
# include AlphaCard::Attribute
|
43
|
+
#
|
44
|
+
# attribute :id, type: Integer, required: true, writable: false
|
45
|
+
# attribute :email, required: true, format: /.+@.+/
|
46
|
+
# attribute :name, type: String
|
47
|
+
# attribute :role, default: 'admin', values: ['admin', 'regular']
|
48
|
+
# attribute :status, types: [String, Symbol]
|
49
|
+
#
|
50
|
+
# attribute :metadata, type: Hash
|
51
|
+
# attribute :additional_info
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
def attribute(name, options = {})
|
55
|
+
define_reader(name)
|
56
|
+
define_writer(name, options) if options[:writable].nil? || options[:writable]
|
57
|
+
|
58
|
+
attributes_set[name.to_sym] = options
|
59
|
+
end
|
60
|
+
|
61
|
+
# Removes attribute from the class (reader, writer and entry in Attributes Set).
|
62
|
+
#
|
63
|
+
# @param name [String, Symbol] attribute name
|
64
|
+
#
|
65
|
+
# @example
|
66
|
+
# class User
|
67
|
+
# include AlphaCard::Attribute
|
68
|
+
#
|
69
|
+
# attribute :email
|
70
|
+
# attribute :name, default: 'John'
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# class Person < User
|
74
|
+
# attribute :email
|
75
|
+
# remove_attribute :name
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
def remove_attribute(name)
|
79
|
+
symbolized_name = name.to_sym
|
80
|
+
|
81
|
+
if attributes_set.keys.include?(symbolized_name)
|
82
|
+
undef_method(symbolized_name)
|
83
|
+
undef_method("#{name}=") if method_defined?("#{name}=")
|
84
|
+
|
85
|
+
attributes_set.delete(symbolized_name)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
# Writes Attributes Set to the superclass on inheritance.
|
92
|
+
#
|
93
|
+
# @private
|
94
|
+
#
|
95
|
+
# @param subclass [Class] inherited class
|
96
|
+
#
|
97
|
+
def inherited(subclass)
|
98
|
+
subclass.instance_variable_set(:@attributes_set, attributes_set.dup)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Creates a reader method for the attribute.
|
102
|
+
#
|
103
|
+
# @private
|
104
|
+
#
|
105
|
+
# @param name [String, Symbol] attribute name
|
106
|
+
#
|
107
|
+
def define_reader(name)
|
108
|
+
attr_reader name.to_sym
|
109
|
+
end
|
110
|
+
|
111
|
+
# Creates a writer method for the attribute with validation
|
112
|
+
# of setting value if options[:values] was passed.
|
113
|
+
#
|
114
|
+
# @private
|
115
|
+
#
|
116
|
+
# @raise [InvalidAttributeValue] when value is not included in the possible values list
|
117
|
+
# @raise [InvalidAttributeFormat] when value doesn't match the required format
|
118
|
+
# @raise [InvalidAttributeType] when value is not of valid type
|
119
|
+
#
|
120
|
+
# @param name [Symbol] attribute name
|
121
|
+
# @param options [Hash] attribute options
|
122
|
+
#
|
123
|
+
def define_writer(name, options = {})
|
124
|
+
values = extract_values_from(options)
|
125
|
+
format = extract_format_from(options)
|
126
|
+
types = extract_types_from(options)
|
127
|
+
|
128
|
+
define_method("#{name}=") do |value|
|
129
|
+
raise InvalidAttributeValue.new(value, values) if values && !values.include?(value)
|
130
|
+
|
131
|
+
raise InvalidAttributeFormat.new(value, format) if !format.nil? && value !~ format
|
132
|
+
|
133
|
+
raise InvalidAttributeType.new(value, types) if value && types && types.none? { |klass| value.is_a?(klass) }
|
134
|
+
|
135
|
+
instance_variable_set(:"@#{name}", value)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# Extract and validate possible attribute values from options hash.
|
140
|
+
#
|
141
|
+
# @private
|
142
|
+
#
|
143
|
+
# @param options [Hash] attribute options
|
144
|
+
#
|
145
|
+
# @return [Array] possible attribute values
|
146
|
+
#
|
147
|
+
def extract_values_from(options = {})
|
148
|
+
values = options[:values] || return
|
149
|
+
raise ArgumentError, ':values option must be an Array or respond to .to_a!' unless values.is_a?(Array) || values.respond_to?(:to_a)
|
150
|
+
raise ArgumentError, ":values option can't be empty!" if values.empty?
|
151
|
+
|
152
|
+
values.to_a
|
153
|
+
end
|
154
|
+
|
155
|
+
# Extract and validate attribute value format from options hash.
|
156
|
+
#
|
157
|
+
# @private
|
158
|
+
#
|
159
|
+
# @param options [Hash] attribute options
|
160
|
+
#
|
161
|
+
# @return [Regexp] attribute value format
|
162
|
+
#
|
163
|
+
def extract_format_from(options = {})
|
164
|
+
format = options[:format] || return
|
165
|
+
raise ArgumentError, ':format must be Regexp!' unless format.is_a?(Regexp)
|
166
|
+
|
167
|
+
format
|
168
|
+
end
|
169
|
+
|
170
|
+
# Extract and validate attribute type class from options hash.
|
171
|
+
#
|
172
|
+
# @private
|
173
|
+
#
|
174
|
+
# @param options [Hash] attribute options
|
175
|
+
#
|
176
|
+
# @return [Object] possible attribute type(s)
|
177
|
+
#
|
178
|
+
def extract_types_from(options = {})
|
179
|
+
types = Array(options[:type] || options[:types])
|
180
|
+
return if types.empty?
|
181
|
+
|
182
|
+
raise ArgumentError, 'attribute type must be a Class!' if types.any? { |type| !type.is_a?(Class) }
|
183
|
+
|
184
|
+
types
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# Attributes class methods
|
189
|
+
# * initialize
|
190
|
+
# * attributes
|
191
|
+
# * []
|
192
|
+
module InstanceMethods
|
193
|
+
# Constructor supports setting attributes when creating a new instance of the class.
|
194
|
+
# Sets default values for the attributes if they are present.
|
195
|
+
#
|
196
|
+
# @param attributes [Hash] attributes hash
|
197
|
+
#
|
198
|
+
# @example
|
199
|
+
# class User
|
200
|
+
# include AlphaCard::Attribute
|
201
|
+
#
|
202
|
+
# attribute :email
|
203
|
+
# attribute :name, default: 'John'
|
204
|
+
# end
|
205
|
+
#
|
206
|
+
# User.new(email: 'john.doe@gmail.com')
|
207
|
+
# #=> #<User:0x29cca00 @email='john.doe@gmail.com', @name="John">
|
208
|
+
#
|
209
|
+
def initialize(attributes = {})
|
210
|
+
set_attributes_defaults!
|
211
|
+
|
212
|
+
attributes.each do |name, value|
|
213
|
+
set_attribute_safely(name, value)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
# Returns class instance attributes.
|
218
|
+
#
|
219
|
+
# @return [Hash] attributes of the instance object
|
220
|
+
#
|
221
|
+
# @example
|
222
|
+
# class User
|
223
|
+
# include AlphaCard::Attribute
|
224
|
+
#
|
225
|
+
# attribute :email
|
226
|
+
# attribute :name, default: 'John'
|
227
|
+
# end
|
228
|
+
#
|
229
|
+
# User.new.attributes
|
230
|
+
# #=> { email: nil, name: 'John' }
|
231
|
+
#
|
232
|
+
def attributes
|
233
|
+
self.class.attributes_set.each_with_object({}) do |(name, _), attributes|
|
234
|
+
attributes[name] = __send__(name)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
# Returns attribute value by it's name.
|
239
|
+
#
|
240
|
+
# @param name [String, Symbol] attribute name
|
241
|
+
#
|
242
|
+
# @return [Object] attribute value
|
243
|
+
#
|
244
|
+
# @example
|
245
|
+
# class User
|
246
|
+
# include AlphaCard::Attribute
|
247
|
+
#
|
248
|
+
# attribute :email
|
249
|
+
# end
|
250
|
+
#
|
251
|
+
# u = User.new(email: 'john@email.com')
|
252
|
+
# u[:email]
|
253
|
+
# #=> 'john@email.com'
|
254
|
+
#
|
255
|
+
def [](name)
|
256
|
+
__send__(name)
|
257
|
+
end
|
258
|
+
|
259
|
+
# Returns names of the attributes that was marked as :required.
|
260
|
+
#
|
261
|
+
# @return [Array] array of attributes names
|
262
|
+
#
|
263
|
+
# @example
|
264
|
+
# class User
|
265
|
+
# include AlphaCard::Attribute
|
266
|
+
#
|
267
|
+
# attribute :id
|
268
|
+
# attribute :email, required: true
|
269
|
+
# attribute :name, required: true
|
270
|
+
# end
|
271
|
+
#
|
272
|
+
# u = User.new
|
273
|
+
# u.required_attributes
|
274
|
+
# #=> [:email, :name]
|
275
|
+
#
|
276
|
+
def required_attributes
|
277
|
+
self.class.attributes_set.select { |_, options| options[:required] }.keys
|
278
|
+
end
|
279
|
+
|
280
|
+
# Indicates if all the attributes with option required: true
|
281
|
+
# are filled with non-nil value.
|
282
|
+
#
|
283
|
+
# @return [Bool]
|
284
|
+
#
|
285
|
+
# @example
|
286
|
+
# class User
|
287
|
+
# include AlphaCard::Attribute
|
288
|
+
#
|
289
|
+
# attribute :email, required: true
|
290
|
+
# attribute :name
|
291
|
+
# end
|
292
|
+
#
|
293
|
+
# u = User.new
|
294
|
+
# u.required_attributes?
|
295
|
+
# #=> false
|
296
|
+
#
|
297
|
+
# u.email = 'john.doe@gmail.com'
|
298
|
+
# u.required_attributes?
|
299
|
+
# #=> true
|
300
|
+
#
|
301
|
+
def required_attributes?
|
302
|
+
required_attributes.all? { |attr| !self[attr].nil? }
|
303
|
+
end
|
304
|
+
|
305
|
+
protected
|
306
|
+
|
307
|
+
# Set attribute value only if attribute writable
|
308
|
+
#
|
309
|
+
# @param name [String, Symbol] attribute name
|
310
|
+
# @param value [Object] attribute value
|
311
|
+
#
|
312
|
+
def set_attribute_safely(name, value)
|
313
|
+
__send__("#{name}=", value) if attribute_writable?(name)
|
314
|
+
end
|
315
|
+
|
316
|
+
# Checks if attribute is writable by it's options in the Attributes Set.
|
317
|
+
#
|
318
|
+
# @param name [String, Symbol] attribute name
|
319
|
+
#
|
320
|
+
# @return [Boolean]
|
321
|
+
#
|
322
|
+
def attribute_writable?(name)
|
323
|
+
attribute_options = self.class.attributes_set[name.to_sym]
|
324
|
+
return false if attribute_options.nil?
|
325
|
+
|
326
|
+
attribute_options[:writable].nil? || attribute_options[:writable]
|
327
|
+
end
|
328
|
+
|
329
|
+
# Sets default values for the attributes, based on Attributes Set.
|
330
|
+
def set_attributes_defaults!
|
331
|
+
self.class.attributes_set.each do |attr_name, options|
|
332
|
+
instance_variable_set(:"@#{attr_name}", options[:default])
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|