braintree 2.2.0 → 2.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/braintree.rb +1 -4
- data/lib/braintree/credit_card.rb +7 -6
- data/lib/braintree/credit_card_verification.rb +3 -2
- data/lib/braintree/customer.rb +13 -6
- data/lib/braintree/error_codes.rb +18 -7
- data/lib/braintree/error_result.rb +7 -1
- data/lib/braintree/ssl_expiration_check.rb +0 -5
- data/lib/braintree/subscription.rb +1 -0
- data/lib/braintree/transaction.rb +6 -1
- data/lib/braintree/transparent_redirect.rb +31 -0
- data/lib/braintree/version.rb +2 -2
- data/lib/braintree/xml/parser.rb +10 -2
- data/lib/braintree/xml/rexml.rb +71 -0
- data/spec/integration/braintree/credit_card_spec.rb +48 -6
- data/spec/integration/braintree/customer_spec.rb +128 -4
- data/spec/{unit → integration}/braintree/ssl_expiration_check_spec.rb +6 -14
- data/spec/integration/braintree/subscription_spec.rb +24 -11
- data/spec/integration/braintree/transaction_spec.rb +20 -4
- data/spec/integration/braintree/transparent_redirect_spec.rb +217 -2
- data/spec/spec_helper.rb +50 -2
- data/spec/unit/braintree/credit_card_spec.rb +2 -21
- data/spec/unit/braintree/credit_card_verification_spec.rb +3 -2
- data/spec/unit/braintree/customer_spec.rb +80 -0
- data/spec/unit/braintree/error_result_spec.rb +40 -0
- data/spec/unit/braintree/xml/parser_spec.rb +51 -0
- data/spec/unit/braintree/xml/rexml_spec.rb +51 -0
- metadata +28 -19
data/lib/braintree.rb
CHANGED
@@ -12,10 +12,6 @@ require "time"
|
|
12
12
|
require "zlib"
|
13
13
|
|
14
14
|
require "builder"
|
15
|
-
require "libxml"
|
16
|
-
unless ::LibXML::XML.respond_to?(:default_keep_blanks=)
|
17
|
-
raise LoadError, "The version of libxml that you're using is not compatible with the Braintree gem."
|
18
|
-
end
|
19
15
|
|
20
16
|
module Braintree
|
21
17
|
end
|
@@ -55,6 +51,7 @@ require "braintree/version.rb"
|
|
55
51
|
require "braintree/xml.rb"
|
56
52
|
require "braintree/xml/generator.rb"
|
57
53
|
require "braintree/xml/libxml.rb"
|
54
|
+
require "braintree/xml/rexml.rb"
|
58
55
|
require "braintree/xml/parser.rb"
|
59
56
|
|
60
57
|
Braintree::SSLExpirationCheck.check_dates
|
@@ -46,10 +46,12 @@ module Braintree
|
|
46
46
|
|
47
47
|
# The transparent redirect URL to use to create a credit card.
|
48
48
|
def self.create_credit_card_url
|
49
|
+
warn "[DEPRECATED] CreditCard.create_credit_card_url is deprecated. Please use TransparentRedirect.url"
|
49
50
|
"#{Braintree::Configuration.base_merchant_url}/payment_methods/all/create_via_transparent_redirect_request"
|
50
51
|
end
|
51
52
|
|
52
53
|
def self.create_from_transparent_redirect(query_string)
|
54
|
+
warn "[DEPRECATED] CreditCard.create_from_transparent_redirect is deprecated. Please use TransparentRedirect.confirm"
|
53
55
|
params = TransparentRedirect.parse_and_validate_query_string query_string
|
54
56
|
_do_create("/payment_methods/all/confirm_transparent_redirect_request", :id => params[:id])
|
55
57
|
end
|
@@ -107,12 +109,14 @@ module Braintree
|
|
107
109
|
end
|
108
110
|
|
109
111
|
def self.update_from_transparent_redirect(query_string)
|
112
|
+
warn "[DEPRECATED] CreditCard.update_via_transparent_redirect_request is deprecated. Please use TransparentRedirect.confirm"
|
110
113
|
params = TransparentRedirect.parse_and_validate_query_string query_string
|
111
114
|
_do_update(:post, "/payment_methods/all/confirm_transparent_redirect_request", :id => params[:id])
|
112
115
|
end
|
113
116
|
|
114
117
|
# The transparent redirect URL to use to update a credit card.
|
115
118
|
def self.update_credit_card_url
|
119
|
+
warn "[DEPRECATED] CreditCard.update_credit_card_url is deprecated. Please use TransparentRedirect.url"
|
116
120
|
"#{Braintree::Configuration.base_merchant_url}/payment_methods/all/update_via_transparent_redirect_request"
|
117
121
|
end
|
118
122
|
|
@@ -163,10 +167,7 @@ module Braintree
|
|
163
167
|
|
164
168
|
# Returns true if the credit card is expired.
|
165
169
|
def expired?
|
166
|
-
|
167
|
-
return expiration_month.to_i < Time.now.month
|
168
|
-
end
|
169
|
-
expiration_year.to_i < Time.now.year
|
170
|
+
@expired
|
170
171
|
end
|
171
172
|
|
172
173
|
def inspect # :nodoc:
|
@@ -233,7 +234,7 @@ module Braintree
|
|
233
234
|
self.new *args
|
234
235
|
end
|
235
236
|
|
236
|
-
def self._do_create(url, params) # :nodoc:
|
237
|
+
def self._do_create(url, params=nil) # :nodoc:
|
237
238
|
response = Http.post url, params
|
238
239
|
if response[:credit_card]
|
239
240
|
SuccessfulResult.new(:credit_card => new(response[:credit_card]))
|
@@ -263,7 +264,7 @@ module Braintree
|
|
263
264
|
billing_address_params = [:company, :country_name, :extended_address, :first_name, :last_name, :locality, :postal_code, :region, :street_address]
|
264
265
|
signature = [
|
265
266
|
:cardholder_name, :cvv, :expiration_date, :expiration_month, :expiration_year, :number, :token,
|
266
|
-
{:options => [:make_default, :verify_card]},
|
267
|
+
{:options => [:make_default, :verification_merchant_account_id, :verify_card]},
|
267
268
|
{:billing_address => billing_address_params}
|
268
269
|
]
|
269
270
|
|
@@ -3,7 +3,7 @@ module Braintree
|
|
3
3
|
include BaseModule
|
4
4
|
|
5
5
|
attr_reader :avs_error_response_code, :avs_postal_code_response_code, :avs_street_address_response_code,
|
6
|
-
:cvv_response_code, :processor_response_code, :processor_response_text, :status
|
6
|
+
:cvv_response_code, :merchant_account_id, :processor_response_code, :processor_response_text, :status
|
7
7
|
|
8
8
|
def initialize(attributes) # :nodoc:
|
9
9
|
set_instance_variables_from_hash(attributes)
|
@@ -13,7 +13,8 @@ module Braintree
|
|
13
13
|
attr_order = [
|
14
14
|
:status, :processor_response_code, :processor_response_text,
|
15
15
|
:cvv_response_code, :avs_error_response_code,
|
16
|
-
:avs_postal_code_response_code, :avs_street_address_response_code
|
16
|
+
:avs_postal_code_response_code, :avs_street_address_response_code,
|
17
|
+
:merchant_account_id
|
17
18
|
]
|
18
19
|
formatted_attrs = attr_order.map do |attr|
|
19
20
|
"#{attr}: #{send(attr).inspect}"
|
data/lib/braintree/customer.rb
CHANGED
@@ -52,18 +52,16 @@ module Braintree
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def self.create_customer_url
|
55
|
+
warn "[DEPRECATED] Customer.create_customer_url is deprecated. Please use TransparentRedirect.url"
|
55
56
|
"#{Braintree::Configuration.base_merchant_url}/customers/all/create_via_transparent_redirect_request"
|
56
57
|
end
|
57
58
|
|
58
59
|
def self.create_from_transparent_redirect(query_string)
|
60
|
+
warn "[DEPRECATED] Customer.create_from_transparent_redirect is deprecated. Please use TransparentRedirect.confirm"
|
59
61
|
params = TransparentRedirect.parse_and_validate_query_string query_string
|
60
62
|
_do_create("/customers/all/confirm_transparent_redirect_request", :id => params[:id])
|
61
63
|
end
|
62
64
|
|
63
|
-
def self.create_customer_transparent_redirect_url
|
64
|
-
"#{Braintree::Configuration.base_merchant_url}/customers"
|
65
|
-
end
|
66
|
-
|
67
65
|
def self.credit(customer_id, transaction_attributes)
|
68
66
|
Transaction.credit(transaction_attributes.merge(:customer_id => customer_id))
|
69
67
|
end
|
@@ -118,10 +116,12 @@ module Braintree
|
|
118
116
|
end
|
119
117
|
|
120
118
|
def self.update_customer_url
|
119
|
+
warn "[DEPRECATED] Customer.update_customer_url is deprecated. Please use TransparentRedirect.url"
|
121
120
|
"#{Braintree::Configuration.base_merchant_url}/customers/all/update_via_transparent_redirect_request"
|
122
121
|
end
|
123
122
|
|
124
123
|
def self.update_from_transparent_redirect(query_string)
|
124
|
+
warn "[DEPRECATED] Customer.update_from_transparent_redirect is deprecated. Please use TransparentRedirect.confirm"
|
125
125
|
params = TransparentRedirect.parse_and_validate_query_string(query_string)
|
126
126
|
_do_update(:post, "/customers/all/confirm_transparent_redirect_request", :id => params[:id])
|
127
127
|
end
|
@@ -208,7 +208,7 @@ module Braintree
|
|
208
208
|
]
|
209
209
|
end
|
210
210
|
|
211
|
-
def self._do_create(url, params) # :nodoc:
|
211
|
+
def self._do_create(url, params=nil) # :nodoc:
|
212
212
|
response = Http.post url, params
|
213
213
|
if response[:customer]
|
214
214
|
SuccessfulResult.new(:customer => new(response[:customer]))
|
@@ -235,7 +235,14 @@ module Braintree
|
|
235
235
|
end
|
236
236
|
|
237
237
|
def self._update_signature # :nodoc:
|
238
|
-
|
238
|
+
credit_card_signature = CreditCard._update_signature - [:customer_id]
|
239
|
+
credit_card_options = credit_card_signature.find { |item| item.respond_to?(:keys) && item.keys == [:options] }
|
240
|
+
credit_card_options[:options] << :update_existing_token
|
241
|
+
[
|
242
|
+
:company, :email, :fax, :first_name, :id, :last_name, :phone, :website,
|
243
|
+
{:credit_card => credit_card_signature},
|
244
|
+
{:custom_fields => :_any_key_}
|
245
|
+
]
|
239
246
|
end
|
240
247
|
end
|
241
248
|
end
|
@@ -24,29 +24,34 @@ module Braintree
|
|
24
24
|
BillingAddressIdIsInvalid = "91702"
|
25
25
|
CardholderNameIsTooLong = "81723"
|
26
26
|
CreditCardTypeIsNotAccepted = "81703"
|
27
|
-
|
27
|
+
CreditCardTypeIsNotAcceptedBySubscriptionMerchantAccount = "81718"
|
28
28
|
CustomerIdIsInvalid = "91705"
|
29
|
-
|
29
|
+
CustomerIdIsRequired = "91704"
|
30
30
|
CvvIsInvalid = "81707"
|
31
|
+
CvvIsRequired = "81706"
|
31
32
|
ExpirationDateConflict = "91708"
|
32
|
-
ExpirationDateIsRequired = "81709"
|
33
33
|
ExpirationDateIsInvalid = "81710"
|
34
|
+
ExpirationDateIsRequired = "81709"
|
34
35
|
ExpirationDateYearIsInvalid = "81711"
|
35
36
|
ExpirationMonthIsInvalid = "81712"
|
36
37
|
ExpirationYearIsInvalid = "81713"
|
37
|
-
NumberIsRequired = "81714"
|
38
|
-
NumberIsInvalid = "81715"
|
39
38
|
NumberHasInvalidLength = "81716"
|
39
|
+
NumberIsInvalid = "81715"
|
40
|
+
NumberIsRequired = "81714"
|
40
41
|
NumberMustBeTestNumber = "81717"
|
41
42
|
TokenInvalid = "91718"
|
42
43
|
TokenIsInUse = "91719"
|
43
|
-
TokenIsTooLong = "91720"
|
44
44
|
TokenIsNotAllowed = "91721"
|
45
45
|
TokenIsRequired = "91722"
|
46
|
+
TokenIsTooLong = "91720"
|
47
|
+
|
48
|
+
module Options
|
49
|
+
UpdateExistingTokenIsInvalid = "91723"
|
50
|
+
end
|
46
51
|
end
|
47
52
|
|
48
53
|
module Customer
|
49
|
-
|
54
|
+
CompanyIsTooLong = "81601"
|
50
55
|
CustomFieldIsInvalid = "91602"
|
51
56
|
CustomFieldIsTooLong = "81603"
|
52
57
|
EmailIsInvalid = "81604"
|
@@ -67,6 +72,11 @@ module Braintree
|
|
67
72
|
module Subscription
|
68
73
|
CannotEditCanceledSubscription = "81901"
|
69
74
|
IdIsInUse = "81902"
|
75
|
+
MerchantAccountIdIsInvalid = "91901"
|
76
|
+
PaymentMethodTokenCardTypeIsNotAccepted = "91902"
|
77
|
+
PaymentMethodTokenIsInvalid = "91903"
|
78
|
+
PaymentMethodTokenNotAssociatedWithCustomer = "91905"
|
79
|
+
PlanIdIsInvalid = "91904"
|
70
80
|
PriceCannotBeBlank = "81903"
|
71
81
|
PriceFormatIsInvalid = "81904"
|
72
82
|
StatusIsCanceled = "81905"
|
@@ -81,6 +91,7 @@ module Braintree
|
|
81
91
|
AmountIsRequired = "81502"
|
82
92
|
AmountIsInvalid = "81503"
|
83
93
|
AmountIsTooLarge = "81528"
|
94
|
+
BillingAddressConflict = "91530"
|
84
95
|
CannotBeVoided = "91504"
|
85
96
|
CannotRefundCredit = "91505"
|
86
97
|
CannotRefundUnlessSettled = "91506"
|
@@ -27,7 +27,13 @@ module Braintree
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def inspect # :nodoc:
|
30
|
-
|
30
|
+
if @credit_card_verification
|
31
|
+
verification_inspect = " credit_card_verification: #{@credit_card_verification.inspect}"
|
32
|
+
end
|
33
|
+
if @transaction
|
34
|
+
transaction_inspect = " transaction: #{@transaction.inspect}"
|
35
|
+
end
|
36
|
+
"#<#{self.class} params:{...} errors:<#{@errors._inner_inspect}>#{verification_inspect}#{transaction_inspect}>"
|
31
37
|
end
|
32
38
|
|
33
39
|
# Always returns false.
|
@@ -6,7 +6,6 @@ module Braintree
|
|
6
6
|
|
7
7
|
def self.check_dates # :nodoc:
|
8
8
|
{
|
9
|
-
"QA" => qa_expiration_date,
|
10
9
|
"Sandbox" => sandbox_expiration_date,
|
11
10
|
"Production" => production_expiration_date
|
12
11
|
}.each do |host, expiration_date|
|
@@ -24,9 +23,5 @@ module Braintree
|
|
24
23
|
def self.sandbox_expiration_date # :nodoc:
|
25
24
|
Date.civil(2010, 12, 1)
|
26
25
|
end
|
27
|
-
|
28
|
-
def self.qa_expiration_date # :nodoc:
|
29
|
-
Date.civil(2010, 12, 1)
|
30
|
-
end
|
31
26
|
end
|
32
27
|
end
|
@@ -156,6 +156,7 @@ module Braintree
|
|
156
156
|
|
157
157
|
attr_reader :avs_error_response_code, :avs_postal_code_response_code, :avs_street_address_response_code
|
158
158
|
attr_reader :amount, :created_at, :credit_card_details, :customer_details, :id
|
159
|
+
attr_reader :currency_iso_code
|
159
160
|
attr_reader :custom_fields
|
160
161
|
attr_reader :cvv_response_code
|
161
162
|
attr_reader :merchant_account_id
|
@@ -167,9 +168,11 @@ module Braintree
|
|
167
168
|
attr_reader :processor_response_code
|
168
169
|
# The response text from the processor.
|
169
170
|
attr_reader :processor_response_text
|
171
|
+
attr_reader :refund_id, :refunded_transaction_id
|
170
172
|
# See Transaction::Status
|
171
173
|
attr_reader :status
|
172
174
|
attr_reader :status_history
|
175
|
+
attr_reader :subscription_id
|
173
176
|
# Will either be "sale" or "credit"
|
174
177
|
attr_reader :type
|
175
178
|
attr_reader :updated_at
|
@@ -184,12 +187,14 @@ module Braintree
|
|
184
187
|
end
|
185
188
|
|
186
189
|
def self.create_from_transparent_redirect(query_string)
|
190
|
+
warn "[DEPRECATED] Transaction.create_from_transparent_redirect is deprecated. Please use TransparentRedirect.confirm"
|
187
191
|
params = TransparentRedirect.parse_and_validate_query_string query_string
|
188
192
|
_do_create("/transactions/all/confirm_transparent_redirect_request", :id => params[:id])
|
189
193
|
end
|
190
194
|
|
191
195
|
# The URL to use to create transactions via transparent redirect.
|
192
196
|
def self.create_transaction_url
|
197
|
+
warn "[DEPRECATED] Transaction.create_transaction_url is deprecated. Please use TransparentRedirect.url"
|
193
198
|
"#{Braintree::Configuration.base_merchant_url}/transactions/all/create_via_transparent_redirect_request"
|
194
199
|
end
|
195
200
|
|
@@ -383,7 +388,7 @@ module Braintree
|
|
383
388
|
end
|
384
389
|
end
|
385
390
|
|
386
|
-
def self._do_create(url, params) # :nodoc:
|
391
|
+
def self._do_create(url, params=nil) # :nodoc:
|
387
392
|
response = Http.post url, params
|
388
393
|
if response[:transaction]
|
389
394
|
SuccessfulResult.new(:transaction => new(response[:transaction]))
|
@@ -26,15 +26,38 @@ module Braintree
|
|
26
26
|
CreateCreditCardSignature = TransparentRedirectKeys + [{:credit_card => CreditCard._create_signature}] # :nodoc:
|
27
27
|
UpdateCreditCardSignature = TransparentRedirectKeys + [:payment_method_token, {:credit_card => CreditCard._update_signature}] # :nodoc:
|
28
28
|
|
29
|
+
module Kind
|
30
|
+
CreateCustomer = "create_customer"
|
31
|
+
UpdateCustomer = "update_customer"
|
32
|
+
CreatePaymentMethod = "create_payment_method"
|
33
|
+
UpdatePaymentMethod = "update_payment_method"
|
34
|
+
CreateTransaction = "create_transaction"
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.confirm(query_string)
|
38
|
+
params = TransparentRedirect.parse_and_validate_query_string query_string
|
39
|
+
confirmation_klass = {
|
40
|
+
Kind::CreateCustomer => Braintree::Customer,
|
41
|
+
Kind::UpdateCustomer => Braintree::Customer,
|
42
|
+
Kind::CreatePaymentMethod => Braintree::CreditCard,
|
43
|
+
Kind::UpdatePaymentMethod => Braintree::CreditCard,
|
44
|
+
Kind::CreateTransaction => Braintree::Transaction
|
45
|
+
}[params[:kind]]
|
46
|
+
|
47
|
+
confirmation_klass._do_create("/transparent_redirect_requests/#{params[:id]}/confirm")
|
48
|
+
end
|
49
|
+
|
29
50
|
# Returns the tr_data string for creating a credit card.
|
30
51
|
def self.create_credit_card_data(params)
|
31
52
|
Util.verify_keys(CreateCreditCardSignature, params)
|
53
|
+
params[:kind] = Kind::CreatePaymentMethod
|
32
54
|
_data(params)
|
33
55
|
end
|
34
56
|
|
35
57
|
# Returns the tr_data string for creating a customer.
|
36
58
|
def self.create_customer_data(params)
|
37
59
|
Util.verify_keys(CreateCustomerSignature, params)
|
60
|
+
params[:kind] = Kind::CreateCustomer
|
38
61
|
_data(params)
|
39
62
|
end
|
40
63
|
|
@@ -58,6 +81,7 @@ module Braintree
|
|
58
81
|
# Returns the tr_data string for creating a transaction.
|
59
82
|
def self.transaction_data(params)
|
60
83
|
Util.verify_keys(TransactionSignature, params)
|
84
|
+
params[:kind] = Kind::CreateTransaction
|
61
85
|
transaction_type = params[:transaction] && params[:transaction][:type]
|
62
86
|
unless %w[sale credit].include?(transaction_type)
|
63
87
|
raise ArgumentError, "expected transaction[type] of sale or credit, was: #{transaction_type.inspect}"
|
@@ -77,6 +101,7 @@ module Braintree
|
|
77
101
|
unless params[:payment_method_token]
|
78
102
|
raise ArgumentError, "expected params to contain :payment_method_token of payment method to update"
|
79
103
|
end
|
104
|
+
params[:kind] = Kind::UpdatePaymentMethod
|
80
105
|
_data(params)
|
81
106
|
end
|
82
107
|
|
@@ -92,9 +117,15 @@ module Braintree
|
|
92
117
|
unless params[:customer_id]
|
93
118
|
raise ArgumentError, "expected params to contain :customer_id of customer to update"
|
94
119
|
end
|
120
|
+
params[:kind] = Kind::UpdateCustomer
|
95
121
|
_data(params)
|
96
122
|
end
|
97
123
|
|
124
|
+
# Returns the URL to which Transparent Redirect Requests should be posted
|
125
|
+
def self.url
|
126
|
+
"#{Braintree::Configuration.base_merchant_url}/transparent_redirect_requests"
|
127
|
+
end
|
128
|
+
|
98
129
|
def self._data(params) # :nodoc:
|
99
130
|
raise ArgumentError, "expected params to contain :redirect_url" unless params[:redirect_url]
|
100
131
|
tr_data_segment = Util.hash_to_query_string(params.merge(
|
data/lib/braintree/version.rb
CHANGED
data/lib/braintree/xml/parser.rb
CHANGED
@@ -11,13 +11,21 @@ module Braintree
|
|
11
11
|
"boolean" => Proc.new { |boolean| %w(1 true).include?(boolean.strip) },
|
12
12
|
}
|
13
13
|
|
14
|
-
def self.hash_from_xml(xml)
|
15
|
-
standardized_hash_structure =
|
14
|
+
def self.hash_from_xml(xml, parser = _determine_parser)
|
15
|
+
standardized_hash_structure = parser.parse(xml)
|
16
16
|
with_underscores_in_keys = _unrename_keys(standardized_hash_structure)
|
17
17
|
typecasted_xml = _typecast_xml_value(with_underscores_in_keys)
|
18
18
|
Util.symbolize_keys(typecasted_xml)
|
19
19
|
end
|
20
20
|
|
21
|
+
def self._determine_parser
|
22
|
+
if defined?(::LibXml::XML) && ::LibXml::XML.respond_to?(:default_keep_blanks=)
|
23
|
+
::Braintree::Xml::Libxml
|
24
|
+
else
|
25
|
+
::Braintree::Xml::Rexml
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
21
29
|
def self._typecast_xml_value(value)
|
22
30
|
case value.class.to_s
|
23
31
|
when 'Hash'
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# Portions of this code were copied and modified from Ruby on Rails, released
|
2
|
+
# under the MIT license, copyright (c) 2005-2009 David Heinemeier Hansson
|
3
|
+
module Braintree
|
4
|
+
module Xml
|
5
|
+
module Rexml
|
6
|
+
|
7
|
+
CONTENT_KEY = '__content__'.freeze
|
8
|
+
|
9
|
+
def self.parse(string)
|
10
|
+
require 'rexml/document' unless defined?(REXML::Document)
|
11
|
+
doc = REXML::Document.new(string)
|
12
|
+
_merge_element!({}, doc.root)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self._merge_element!(hash, element)
|
16
|
+
_merge!(hash, element.name, _collapse(element))
|
17
|
+
end
|
18
|
+
|
19
|
+
def self._collapse(element)
|
20
|
+
hash = _get_attributes(element)
|
21
|
+
|
22
|
+
if element.has_elements?
|
23
|
+
element.each_element {|child| _merge_element!(hash, child) }
|
24
|
+
_merge_texts!(hash, element) unless _empty_content?(element)
|
25
|
+
hash
|
26
|
+
else
|
27
|
+
_merge_texts!(hash, element)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self._merge_texts!(hash, element)
|
32
|
+
unless element.has_text?
|
33
|
+
hash
|
34
|
+
else
|
35
|
+
# must use value to prevent double-escaping
|
36
|
+
_merge!(
|
37
|
+
hash,
|
38
|
+
CONTENT_KEY,
|
39
|
+
element.texts.map { |t| t.value}.join
|
40
|
+
)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def self._merge!(hash, key, value)
|
45
|
+
if hash.has_key?(key)
|
46
|
+
if hash[key].instance_of?(Array)
|
47
|
+
hash[key] << value
|
48
|
+
else
|
49
|
+
hash[key] = [hash[key], value]
|
50
|
+
end
|
51
|
+
elsif value.instance_of?(Array)
|
52
|
+
hash[key] = [value]
|
53
|
+
else
|
54
|
+
hash[key] = value
|
55
|
+
end
|
56
|
+
hash
|
57
|
+
end
|
58
|
+
|
59
|
+
def self._get_attributes(element)
|
60
|
+
attributes = {}
|
61
|
+
element.attributes.each { |n,v| attributes[n] = v }
|
62
|
+
attributes
|
63
|
+
end
|
64
|
+
|
65
|
+
def self._empty_content?(element)
|
66
|
+
element.texts.join.strip == ""
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|