recurly 2.17.5 → 4.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (280) hide show
  1. checksums.yaml +4 -4
  2. data/.bumpversion.cfg +15 -0
  3. data/.changelog_config.yaml +11 -0
  4. data/.github/ISSUE_TEMPLATE/bug-report.md +30 -0
  5. data/.github/ISSUE_TEMPLATE/question-or-other.md +10 -0
  6. data/.github/workflows/ci.yml +29 -0
  7. data/.github/workflows/docs.yml +28 -0
  8. data/.gitignore +15 -0
  9. data/.rspec +2 -0
  10. data/.yardopts +2 -0
  11. data/CHANGELOG.md +295 -0
  12. data/CODE_OF_CONDUCT.md +130 -0
  13. data/CONTRIBUTING.md +106 -0
  14. data/GETTING_STARTED.md +330 -0
  15. data/Gemfile +4 -0
  16. data/LICENSE.txt +21 -0
  17. data/README.md +9 -148
  18. data/Rakefile +6 -0
  19. data/benchmark.rb +16 -0
  20. data/lib/data/ca-certificates.crt +3466 -0
  21. data/lib/recurly/client/operations.rb +4079 -0
  22. data/lib/recurly/client.rb +400 -0
  23. data/lib/recurly/connection_pool.rb +42 -0
  24. data/lib/recurly/errors/api_errors.rb +90 -0
  25. data/lib/recurly/errors/network_errors.rb +7 -0
  26. data/lib/recurly/errors.rb +51 -0
  27. data/lib/recurly/http.rb +50 -0
  28. data/lib/recurly/pager.rb +136 -0
  29. data/lib/recurly/request.rb +31 -0
  30. data/lib/recurly/requests/account_acquisition_cost.rb +18 -0
  31. data/lib/recurly/requests/account_acquisition_update.rb +26 -0
  32. data/lib/recurly/requests/account_create.rb +98 -0
  33. data/lib/recurly/requests/account_purchase.rb +98 -0
  34. data/lib/recurly/requests/account_update.rb +86 -0
  35. data/lib/recurly/requests/add_on_create.rb +102 -0
  36. data/lib/recurly/requests/add_on_pricing.rb +26 -0
  37. data/lib/recurly/requests/add_on_update.rb +78 -0
  38. data/lib/recurly/requests/address.rb +38 -0
  39. data/lib/recurly/requests/billing_info_create.rb +134 -0
  40. data/lib/recurly/requests/billing_info_verify.rb +14 -0
  41. data/lib/recurly/requests/coupon_bulk_create.rb +14 -0
  42. data/lib/recurly/requests/coupon_create.rb +102 -0
  43. data/lib/recurly/requests/coupon_pricing.rb +18 -0
  44. data/lib/recurly/requests/coupon_redemption_create.rb +22 -0
  45. data/lib/recurly/requests/coupon_update.rb +34 -0
  46. data/lib/recurly/requests/custom_field.rb +18 -0
  47. data/lib/recurly/requests/dunning_campaigns_bulk_update.rb +18 -0
  48. data/lib/recurly/requests/external_refund.rb +22 -0
  49. data/lib/recurly/requests/external_transaction.rb +26 -0
  50. data/lib/recurly/requests/invoice_address.rb +54 -0
  51. data/lib/recurly/requests/invoice_collect.rb +22 -0
  52. data/lib/recurly/requests/invoice_create.rb +42 -0
  53. data/lib/recurly/requests/invoice_refund.rb +34 -0
  54. data/lib/recurly/requests/invoice_update.rb +34 -0
  55. data/lib/recurly/requests/item_create.rb +58 -0
  56. data/lib/recurly/requests/item_update.rb +58 -0
  57. data/lib/recurly/requests/line_item_create.rb +86 -0
  58. data/lib/recurly/requests/line_item_refund.rb +22 -0
  59. data/lib/recurly/requests/measured_unit_create.rb +22 -0
  60. data/lib/recurly/requests/measured_unit_update.rb +22 -0
  61. data/lib/recurly/requests/percentage_tier.rb +18 -0
  62. data/lib/recurly/requests/percentage_tiers_by_currency.rb +18 -0
  63. data/lib/recurly/requests/plan_create.rb +102 -0
  64. data/lib/recurly/requests/plan_hosted_pages.rb +26 -0
  65. data/lib/recurly/requests/plan_pricing.rb +26 -0
  66. data/lib/recurly/requests/plan_update.rb +94 -0
  67. data/lib/recurly/requests/pricing.rb +22 -0
  68. data/lib/recurly/requests/purchase_create.rb +78 -0
  69. data/lib/recurly/requests/shipping_address_create.rb +62 -0
  70. data/lib/recurly/requests/shipping_address_update.rb +66 -0
  71. data/lib/recurly/requests/shipping_fee_create.rb +22 -0
  72. data/lib/recurly/requests/shipping_method_create.rb +26 -0
  73. data/lib/recurly/requests/shipping_method_update.rb +26 -0
  74. data/lib/recurly/requests/shipping_purchase.rb +22 -0
  75. data/lib/recurly/requests/subscription_add_on_create.rb +46 -0
  76. data/lib/recurly/requests/subscription_add_on_percentage_tier.rb +18 -0
  77. data/lib/recurly/requests/subscription_add_on_tier.rb +26 -0
  78. data/lib/recurly/requests/subscription_add_on_update.rb +50 -0
  79. data/lib/recurly/requests/subscription_cancel.rb +14 -0
  80. data/lib/recurly/requests/subscription_change_billing_info_create.rb +14 -0
  81. data/lib/recurly/requests/subscription_change_create.rb +74 -0
  82. data/lib/recurly/requests/subscription_change_shipping_create.rb +30 -0
  83. data/lib/recurly/requests/subscription_create.rb +114 -0
  84. data/lib/recurly/requests/subscription_pause.rb +14 -0
  85. data/lib/recurly/requests/subscription_purchase.rb +70 -0
  86. data/lib/recurly/requests/subscription_shipping_create.rb +30 -0
  87. data/lib/recurly/requests/subscription_shipping_purchase.rb +22 -0
  88. data/lib/recurly/requests/subscription_shipping_update.rb +22 -0
  89. data/lib/recurly/requests/subscription_update.rb +70 -0
  90. data/lib/recurly/requests/tier.rb +22 -0
  91. data/lib/recurly/requests/tier_pricing.rb +22 -0
  92. data/lib/recurly/requests/usage_create.rb +26 -0
  93. data/lib/recurly/requests.rb +8 -0
  94. data/lib/recurly/resource.rb +23 -1092
  95. data/lib/recurly/resources/account.rb +138 -0
  96. data/lib/recurly/resources/account_acquisition.rb +46 -0
  97. data/lib/recurly/resources/account_acquisition_cost.rb +18 -0
  98. data/lib/recurly/resources/account_balance.rb +26 -0
  99. data/lib/recurly/resources/account_balance_amount.rb +22 -0
  100. data/lib/recurly/resources/account_mini.rb +50 -0
  101. data/lib/recurly/resources/account_note.rb +34 -0
  102. data/lib/recurly/resources/add_on.rb +122 -0
  103. data/lib/recurly/resources/add_on_mini.rb +54 -0
  104. data/lib/recurly/resources/add_on_pricing.rb +26 -0
  105. data/lib/recurly/resources/address.rb +38 -0
  106. data/lib/recurly/resources/address_with_name.rb +46 -0
  107. data/lib/recurly/resources/billing_info.rb +74 -0
  108. data/lib/recurly/resources/billing_info_updated_by.rb +18 -0
  109. data/lib/recurly/resources/binary_file.rb +14 -0
  110. data/lib/recurly/resources/coupon.rb +126 -0
  111. data/lib/recurly/resources/coupon_discount.rb +26 -0
  112. data/lib/recurly/resources/coupon_discount_pricing.rb +18 -0
  113. data/lib/recurly/resources/coupon_discount_trial.rb +18 -0
  114. data/lib/recurly/resources/coupon_mini.rb +42 -0
  115. data/lib/recurly/resources/coupon_redemption.rb +54 -0
  116. data/lib/recurly/resources/coupon_redemption_mini.rb +34 -0
  117. data/lib/recurly/resources/credit_payment.rb +66 -0
  118. data/lib/recurly/resources/custom_field.rb +18 -0
  119. data/lib/recurly/resources/custom_field_definition.rb +50 -0
  120. data/lib/recurly/resources/dunning_campaign.rb +50 -0
  121. data/lib/recurly/resources/dunning_campaigns_bulk_update_response.rb +18 -0
  122. data/lib/recurly/resources/dunning_cycle.rb +58 -0
  123. data/lib/recurly/resources/dunning_interval.rb +18 -0
  124. data/lib/recurly/resources/error.rb +22 -0
  125. data/lib/recurly/resources/error_may_have_transaction.rb +26 -0
  126. data/lib/recurly/resources/export_dates.rb +18 -0
  127. data/lib/recurly/resources/export_file.rb +22 -0
  128. data/lib/recurly/resources/export_files.rb +18 -0
  129. data/lib/recurly/resources/fraud_info.rb +22 -0
  130. data/lib/recurly/resources/invoice.rb +162 -0
  131. data/lib/recurly/resources/invoice_address.rb +54 -0
  132. data/lib/recurly/resources/invoice_collection.rb +22 -0
  133. data/lib/recurly/resources/invoice_mini.rb +30 -0
  134. data/lib/recurly/resources/invoice_template.rb +34 -0
  135. data/lib/recurly/resources/item.rb +82 -0
  136. data/lib/recurly/resources/item_mini.rb +34 -0
  137. data/lib/recurly/resources/line_item.rb +206 -0
  138. data/lib/recurly/resources/measured_unit.rb +46 -0
  139. data/lib/recurly/resources/payment_method.rb +70 -0
  140. data/lib/recurly/resources/percentage_tier.rb +18 -0
  141. data/lib/recurly/resources/percentage_tiers_by_currency.rb +18 -0
  142. data/lib/recurly/resources/plan.rb +122 -0
  143. data/lib/recurly/resources/plan_hosted_pages.rb +26 -0
  144. data/lib/recurly/resources/plan_mini.rb +26 -0
  145. data/lib/recurly/resources/plan_pricing.rb +26 -0
  146. data/lib/recurly/resources/pricing.rb +22 -0
  147. data/lib/recurly/resources/settings.rb +22 -0
  148. data/lib/recurly/resources/shipping_address.rb +82 -0
  149. data/lib/recurly/resources/shipping_method.rb +46 -0
  150. data/lib/recurly/resources/shipping_method_mini.rb +26 -0
  151. data/lib/recurly/resources/site.rb +54 -0
  152. data/lib/recurly/resources/subscription.rb +198 -0
  153. data/lib/recurly/resources/subscription_add_on.rb +78 -0
  154. data/lib/recurly/resources/subscription_add_on_percentage_tier.rb +18 -0
  155. data/lib/recurly/resources/subscription_add_on_tier.rb +26 -0
  156. data/lib/recurly/resources/subscription_change.rb +82 -0
  157. data/lib/recurly/resources/subscription_change_billing_info.rb +14 -0
  158. data/lib/recurly/resources/subscription_shipping.rb +26 -0
  159. data/lib/recurly/resources/tax_detail.rb +26 -0
  160. data/lib/recurly/resources/tax_info.rb +26 -0
  161. data/lib/recurly/resources/tier.rb +22 -0
  162. data/lib/recurly/resources/tier_pricing.rb +22 -0
  163. data/lib/recurly/resources/transaction.rb +162 -0
  164. data/lib/recurly/resources/transaction_error.rb +38 -0
  165. data/lib/recurly/resources/transaction_payment_gateway.rb +26 -0
  166. data/lib/recurly/resources/unique_coupon_code.rb +50 -0
  167. data/lib/recurly/resources/unique_coupon_code_params.rb +26 -0
  168. data/lib/recurly/resources/usage.rb +78 -0
  169. data/lib/recurly/resources/user.rb +42 -0
  170. data/lib/recurly/resources.rb +18 -0
  171. data/lib/recurly/schema/file_parser.rb +13 -0
  172. data/lib/recurly/schema/json_parser.rb +72 -0
  173. data/lib/recurly/schema/request_caster.rb +60 -0
  174. data/lib/recurly/schema/resource_caster.rb +46 -0
  175. data/lib/recurly/schema/schema_factory.rb +48 -0
  176. data/lib/recurly/schema/schema_validator.rb +144 -0
  177. data/lib/recurly/schema.rb +156 -0
  178. data/lib/recurly/version.rb +1 -15
  179. data/lib/recurly.rb +15 -138
  180. data/openapi/api.yaml +22879 -0
  181. data/recurly.gemspec +39 -0
  182. data/scripts/build +5 -0
  183. data/scripts/clean +6 -0
  184. data/scripts/format +12 -0
  185. data/scripts/prepare-release +50 -0
  186. data/scripts/release +17 -0
  187. data/scripts/test +15 -0
  188. metadata +217 -217
  189. data/lib/recurly/account.rb +0 -179
  190. data/lib/recurly/account_balance.rb +0 -21
  191. data/lib/recurly/add_on.rb +0 -30
  192. data/lib/recurly/address.rb +0 -25
  193. data/lib/recurly/adjustment.rb +0 -76
  194. data/lib/recurly/api/errors.rb +0 -208
  195. data/lib/recurly/api/net_http_adapter.rb +0 -111
  196. data/lib/recurly/api.rb +0 -101
  197. data/lib/recurly/billing_info.rb +0 -80
  198. data/lib/recurly/coupon.rb +0 -134
  199. data/lib/recurly/credit_payment.rb +0 -32
  200. data/lib/recurly/custom_field.rb +0 -15
  201. data/lib/recurly/delivery.rb +0 -19
  202. data/lib/recurly/error.rb +0 -13
  203. data/lib/recurly/gift_card.rb +0 -82
  204. data/lib/recurly/helper.rb +0 -51
  205. data/lib/recurly/invoice.rb +0 -273
  206. data/lib/recurly/invoice_collection.rb +0 -14
  207. data/lib/recurly/js.rb +0 -14
  208. data/lib/recurly/juris_detail.rb +0 -14
  209. data/lib/recurly/measured_unit.rb +0 -16
  210. data/lib/recurly/money.rb +0 -120
  211. data/lib/recurly/note.rb +0 -14
  212. data/lib/recurly/plan.rb +0 -40
  213. data/lib/recurly/purchase.rb +0 -219
  214. data/lib/recurly/redemption.rb +0 -46
  215. data/lib/recurly/resource/association.rb +0 -16
  216. data/lib/recurly/resource/errors.rb +0 -20
  217. data/lib/recurly/resource/pager.rb +0 -313
  218. data/lib/recurly/shipping_address.rb +0 -26
  219. data/lib/recurly/subscription/add_ons.rb +0 -77
  220. data/lib/recurly/subscription.rb +0 -328
  221. data/lib/recurly/subscription_add_on.rb +0 -50
  222. data/lib/recurly/tax_detail.rb +0 -14
  223. data/lib/recurly/tax_type.rb +0 -12
  224. data/lib/recurly/transaction/errors.rb +0 -107
  225. data/lib/recurly/transaction.rb +0 -129
  226. data/lib/recurly/usage.rb +0 -28
  227. data/lib/recurly/webhook/account_notification.rb +0 -10
  228. data/lib/recurly/webhook/billing_info_updated_notification.rb +0 -6
  229. data/lib/recurly/webhook/canceled_account_notification.rb +0 -6
  230. data/lib/recurly/webhook/canceled_subscription_notification.rb +0 -6
  231. data/lib/recurly/webhook/closed_credit_invoice_notification.rb +0 -6
  232. data/lib/recurly/webhook/closed_invoice_notification.rb +0 -6
  233. data/lib/recurly/webhook/credit_payment_notification.rb +0 -12
  234. data/lib/recurly/webhook/dunning_notification.rb +0 -14
  235. data/lib/recurly/webhook/expired_subscription_notification.rb +0 -6
  236. data/lib/recurly/webhook/failed_charge_invoice_notification.rb +0 -6
  237. data/lib/recurly/webhook/failed_payment_notification.rb +0 -6
  238. data/lib/recurly/webhook/gift_card_notification.rb +0 -8
  239. data/lib/recurly/webhook/invoice_notification.rb +0 -12
  240. data/lib/recurly/webhook/low_balance_gift_card_notification.rb +0 -6
  241. data/lib/recurly/webhook/new_account_notification.rb +0 -6
  242. data/lib/recurly/webhook/new_charge_invoice_notification.rb +0 -6
  243. data/lib/recurly/webhook/new_credit_invoice_notification.rb +0 -6
  244. data/lib/recurly/webhook/new_credit_payment_notification.rb +0 -6
  245. data/lib/recurly/webhook/new_dunning_event_notification.rb +0 -6
  246. data/lib/recurly/webhook/new_invoice_notification.rb +0 -6
  247. data/lib/recurly/webhook/new_subscription_notification.rb +0 -6
  248. data/lib/recurly/webhook/new_usage_notification.rb +0 -8
  249. data/lib/recurly/webhook/notification.rb +0 -18
  250. data/lib/recurly/webhook/paid_charge_invoice_notification.rb +0 -6
  251. data/lib/recurly/webhook/past_due_charge_invoice_notification.rb +0 -6
  252. data/lib/recurly/webhook/past_due_invoice_notification.rb +0 -6
  253. data/lib/recurly/webhook/processing_charge_invoice_notification.rb +0 -6
  254. data/lib/recurly/webhook/processing_credit_invoice_notification.rb +0 -6
  255. data/lib/recurly/webhook/processing_invoice_notification.rb +0 -6
  256. data/lib/recurly/webhook/processing_payment_notification.rb +0 -6
  257. data/lib/recurly/webhook/purchased_gift_card_notification.rb +0 -7
  258. data/lib/recurly/webhook/reactivated_account_notification.rb +0 -6
  259. data/lib/recurly/webhook/redeemed_gift_card_notification.rb +0 -7
  260. data/lib/recurly/webhook/renewed_subscription_notification.rb +0 -6
  261. data/lib/recurly/webhook/reopened_charge_invoice_notification.rb +0 -6
  262. data/lib/recurly/webhook/reopened_credit_invoice_notification.rb +0 -6
  263. data/lib/recurly/webhook/scheduled_payment_notification.rb +0 -6
  264. data/lib/recurly/webhook/subscription_notification.rb +0 -12
  265. data/lib/recurly/webhook/successful_payment_notification.rb +0 -6
  266. data/lib/recurly/webhook/successful_refund_notification.rb +0 -6
  267. data/lib/recurly/webhook/transaction_authorized_notification.rb +0 -6
  268. data/lib/recurly/webhook/transaction_notification.rb +0 -12
  269. data/lib/recurly/webhook/transaction_status_updated_notification.rb +0 -6
  270. data/lib/recurly/webhook/updated_account_notification.rb +0 -6
  271. data/lib/recurly/webhook/updated_balance_gift_card_notification.rb +0 -7
  272. data/lib/recurly/webhook/updated_invoice_notification.rb +0 -6
  273. data/lib/recurly/webhook/updated_subscription_notification.rb +0 -6
  274. data/lib/recurly/webhook/void_payment_notification.rb +0 -6
  275. data/lib/recurly/webhook/voided_credit_invoice_notification.rb +0 -6
  276. data/lib/recurly/webhook/voided_credit_payment_notification.rb +0 -6
  277. data/lib/recurly/webhook.rb +0 -91
  278. data/lib/recurly/xml/nokogiri.rb +0 -60
  279. data/lib/recurly/xml/rexml.rb +0 -52
  280. data/lib/recurly/xml.rb +0 -122
@@ -0,0 +1,60 @@
1
+ require "date"
2
+
3
+ module Recurly
4
+ class Schema
5
+ # *Note*: This module is for internal use.
6
+ # The RequestCaster turns mixed data into a pure Hash
7
+ # so it can be serialized into JSON and used as the body of a request.
8
+ # This module is to be extended by the Request class.
9
+ module RequestCaster
10
+
11
+ # This method casts the data object (of mixed types) into a Hash ready for JSON
12
+ # serialization. The *schema* will default to the self's schema.
13
+ # You should pass in the schema where possible. This is because objects are serialized
14
+ # differently depending on the context in which they are being written.
15
+ #
16
+ # @example
17
+ # Recurly::Requests::AccountUpdatable.cast(code: 'benjamin')
18
+ # #=> {:code=>"benjamin"}
19
+ # @example
20
+ # # If you have some mixed data, like passing in an Address, it should cast that
21
+ # # address into a Hash based on the Schema defined in AccountUpdatable
22
+ # address = Recurly::Resources::Address.new(city: 'New Orleans')
23
+ # Recurly::Requests::AccountUpdatable.cast(account_code: 'benjamin', address: address)
24
+ # #=> {:account_code=>"benjamin", :address=>{:city=>"New Orleans"}}
25
+ #
26
+ # @param data [Hash,Resource,Request] The data to transform into a JSON Hash.
27
+ # @param schema [Schema] The schema to use to transform the data into a JSON Hash.
28
+ # @return [Hash] The pure Hash ready to be serialized into JSON.
29
+ def cast_request(data, schema = self.schema)
30
+ casted = {}
31
+ if data.is_a?(Resource) || data.is_a?(Request)
32
+ data = data.attributes.reject { |_k, v| v.nil? }
33
+ end
34
+
35
+ data.each do |k, v|
36
+ schema_attr = schema.get_attribute(k)
37
+ norm_val = if v.respond_to?(:attributes)
38
+ cast_request(v, v.class.schema)
39
+ elsif v.is_a?(Array)
40
+ v.map do |elem|
41
+ if elem.respond_to?(:attributes)
42
+ cast_request(elem, elem.class.schema)
43
+ else
44
+ elem
45
+ end
46
+ end
47
+ elsif v.is_a?(Hash) && schema_attr && schema_attr.is_a?(Schema::ResourceAttribute)
48
+ cast_request(v, schema_attr.recurly_class.schema)
49
+ else
50
+ v
51
+ end
52
+
53
+ casted[k] = norm_val
54
+ end
55
+
56
+ casted
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,46 @@
1
+ require "date"
2
+
3
+ module Recurly
4
+ class Schema
5
+ # The purpose of this class is to turn JSON parsed Hashes
6
+ # defined into Recurly ruby objects. It's to be used
7
+ # by the Resource as an extension.
8
+ module ResourceCaster
9
+
10
+ # Gives the class the ability to initialize itself
11
+ # given some json data.
12
+ #
13
+ # @example
14
+ # Recurly::Resources::Account.cast({"code" => "mycode"})
15
+ # #=> #<Recurly::Resources::Account @attributes={:code=>"mycode"}>
16
+ #
17
+ # @param attributes [Hash] A primitive Hash from JSON.parse of Recurly response.
18
+ # @return [Resource] the {Resource} (ruby object) representing the passed in JSON data.
19
+ def cast(attributes = {})
20
+ resource = new()
21
+ attributes.each do |attr_name, val|
22
+ schema_attr = self.schema.get_attribute(attr_name)
23
+
24
+ if schema_attr
25
+ val = if val.nil?
26
+ val
27
+ elsif schema_attr.is_valid?(val)
28
+ schema_attr.cast(val)
29
+ else
30
+ if Recurly::STRICT_MODE
31
+ msg = "#{self.class}##{attr_name} does not have the right type. Value: #{val.inspect} was expected to be a #{schema_attr}"
32
+ raise ArgumentError, msg
33
+ end
34
+ end
35
+
36
+ writer = "#{attr_name}="
37
+ resource.send(writer, val)
38
+ elsif Recurly::STRICT_MODE
39
+ raise ArgumentError, "#{resource.class.name} encountered json attribute #{attr_name.inspect}: #{val.inspect} but it's unknown to it's schema"
40
+ end
41
+ end
42
+ resource
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,48 @@
1
+ module Recurly
2
+ class Schema
3
+ # A mixin that allows a class to be treated like a recurly
4
+ # object. This gives the class the power to describe
5
+ # it's schema. It adds the *define_attribute* method
6
+ # and a *schema* reader.
7
+ module SchemaFactory
8
+
9
+ # Gets the schema for this class
10
+ # @return [Schema]
11
+ def schema
12
+ @schema ||= ::Recurly::Schema.new
13
+ end
14
+
15
+ protected
16
+
17
+ # Macro that allows this class to define it's schema and associated
18
+ # attribute getters and setters.
19
+ #
20
+ # @example
21
+ # class Account
22
+ # extend Schema::SchemaFactory
23
+ # define_attribute :code, String, {:read_only=>true}
24
+ # end
25
+ # account = Account.new(code: "mycode")
26
+ # account.schema
27
+ # #=> Recurly::Schema
28
+ # acount.code = "newcode" # this method protected since read_only = true
29
+ # account.code
30
+ # #=> "mycode"
31
+ def define_attribute(name, type, options = {})
32
+ attribute = schema.add_attribute(name, type, options)
33
+
34
+ # Define the reader
35
+ define_method(name) do
36
+ self.attributes[name]
37
+ end
38
+
39
+ # Define the writer
40
+ define_method("#{name}=") do |val|
41
+ self.attributes[name] = val
42
+ end
43
+
44
+ self
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,144 @@
1
+ module Recurly
2
+ class Schema
3
+ # This module is responsible for validating that the raw data
4
+ # passed in to *attributes* matches the schema belonging to this class.
5
+ # It should be mixed in to the Request class.
6
+ module SchemaValidator
7
+ # Validates the attributes and throws an error if something is wrong.
8
+ #
9
+ # @example
10
+ # Recurly::Requests::PlanCreate.new(code: 'plan123').validate!
11
+ # #=> {:code=>"plan123"}
12
+ # @example
13
+ # Recurly::Requests::PlanCreate.new(code: 3.14).validate!
14
+ # #=> ArgumentError: Attribute 'code' on the resource Recurly::Requests::PlanCreate is type Float but should be a String.
15
+ # @example
16
+ # Recurly::Requests::PlanCreate.new(kode: 'plan123').validate!
17
+ # #=> ArgumentError: Attribute 'kode' does not exist on request Recurly::Requests::PlanCreate. Did you mean 'code'?
18
+ #
19
+ # @raise [ArgumentError] if the attribute data does not match the schema.
20
+ def validate!
21
+ attributes.each do |attr_name, val|
22
+ schema_attr = schema.get_attribute(attr_name)
23
+ if schema_attr.nil?
24
+ err_msg = "Attribute '#{attr_name}' does not exist on request #{self.class.name}."
25
+ if did_you_mean = get_did_you_mean(schema, attr_name)
26
+ err_msg << " Did you mean '#{did_you_mean}'?"
27
+ end
28
+ raise ArgumentError, err_msg
29
+ else
30
+ validate_attribute!(attr_name, schema_attr, val)
31
+ end
32
+ end
33
+ end
34
+
35
+ # Validates an individual attribute
36
+ def validate_attribute!(name, schema_attr, val)
37
+ unless val.nil? || schema_attr.is_valid?(val)
38
+ # If it's safely castable, the json deserializer or server
39
+ # will take care of it for us
40
+ unless safely_castable?(val.class, schema_attr.type)
41
+ expected = case schema_attr
42
+ when Schema::ArrayAttribute
43
+ "Array of #{schema_attr.type}s"
44
+ else
45
+ schema_attr.type
46
+ end
47
+
48
+ raise ArgumentError, "Attribute '#{name}' on the resource #{self.class.name} is type #{val.class} but should be a #{expected}"
49
+ end
50
+ end
51
+
52
+ # This is the convention for a recurly object
53
+ if schema_attr.is_a?(Schema::ResourceAttribute) && val.is_a?(Hash)
54
+ # Using send because the initializer may be private
55
+ instance = schema_attr.recurly_class.send(:new, val)
56
+ instance.validate!
57
+ end
58
+ end
59
+
60
+ # Gets the closest term to the misspelled attribute
61
+ def get_did_you_mean(schema, misspelled_attr)
62
+ closest = schema.attributes.keys.sort_by do |v|
63
+ levenshtein_distance(v, misspelled_attr)
64
+ end.first
65
+
66
+ if closest && levenshtein_distance(closest, misspelled_attr) <= 4
67
+ closest
68
+ end
69
+ end
70
+
71
+ private
72
+
73
+ def safely_castable?(from_type, to_type)
74
+ # TODO we can drop this switch when 2.3 support is dropped
75
+ int_class = if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.4.0")
76
+ :Integer
77
+ else
78
+ :Fixnum
79
+ end
80
+ int_class = Kernel.const_get(int_class)
81
+
82
+ case [from_type, to_type]
83
+ when [Symbol, String]
84
+ true
85
+ when [int_class, Float]
86
+ true
87
+ else
88
+ false
89
+ end
90
+ end
91
+
92
+ # This code is copied directly from the did_you mean gem which is based
93
+ # directly on the Text gem implementation.
94
+ #
95
+ # did_you_mean: Copyright (c) 2014-2016 Yuki Nishijima.
96
+ # Text: Copyright (c) 2006-2013 Paul Battley, Michael Neumann, Tim Fletcher.
97
+ #
98
+ # Returns a value representing the "cost" of transforming str1 into str2
99
+ def levenshtein_distance(str1, str2)
100
+ str1 = str1.to_s unless str1.is_a? String
101
+ str2 = str2.to_s unless str2.is_a? String
102
+ n = str1.length
103
+ m = str2.length
104
+ return m if n.zero?
105
+ return n if m.zero?
106
+
107
+ d = (0..m).to_a
108
+ x = nil
109
+
110
+ # to avoid duplicating an enumerable object, create it outside of the loop
111
+ str2_codepoints = str2.codepoints
112
+
113
+ str1.each_codepoint.with_index(1) do |char1, i|
114
+ j = 0
115
+ while j < m
116
+ cost = (char1 == str2_codepoints[j]) ? 0 : 1
117
+ x = min3(
118
+ d[j + 1] + 1, # insertion
119
+ i + 1, # deletion
120
+ d[j] + cost # substitution
121
+ )
122
+ d[j] = i
123
+ i = x
124
+
125
+ j += 1
126
+ end
127
+ d[m] = x
128
+ end
129
+
130
+ x
131
+ end
132
+
133
+ def min3(a, b, c)
134
+ if a < b && a < c
135
+ a
136
+ elsif b < c
137
+ b
138
+ else
139
+ c
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,156 @@
1
+ module Recurly
2
+ # The class responsible for describing a schema.
3
+ # This is used for requests and resources.
4
+ class Schema
5
+ # The attributes in the schema
6
+ # @return [Hash<String,Attribute>]
7
+ attr_reader :attributes
8
+
9
+ def initialize
10
+ @attributes = {}
11
+ end
12
+
13
+ # Adds an attribute to the schema definition
14
+ #
15
+ # @param name [Symbol] The name of the attribute
16
+ # @param type [Class,Symbol] The type of the attribute. Use capitalized symbol for Recurly class. Example: :Account.
17
+ # @param options [Schema::Attribute] The created and registered attribute object.
18
+ def add_attribute(name, type, options)
19
+ attribute = Attribute.build(type, options)
20
+ @attributes[name.to_s] = attribute
21
+ attribute
22
+ end
23
+
24
+ # Gets an attribute from this schema given a name
25
+ #
26
+ # @param name [String,Symbol] The name/key of the attribute
27
+ # @return [Attribute,nil] The found Attribute. nil if not found.
28
+ def get_attribute(name)
29
+ @attributes[name.to_s]
30
+ end
31
+
32
+ # Gets a recurly class given a symbol name.
33
+ #
34
+ # @example
35
+ # Schema.get_recurly_class(:Account)
36
+ # #=> Recurly::Resources::Account
37
+ #
38
+ # @param type [Symbol] The name of the class you wish to find
39
+ # @return [Request,Resource]
40
+ # @raise ArgumentError If class can't be found.
41
+ def self.get_recurly_class(type)
42
+ raise ArgumentError, "#{type.inspect} must be a symbol but is a #{type.class}" unless type.is_a?(Symbol)
43
+
44
+ if type == :Address
45
+ Recurly::Resources::Address
46
+ elsif Recurly::Requests.const_defined?(type, false)
47
+ Recurly::Requests.const_get(type, false)
48
+ elsif Recurly::Resources.const_defined?(type, false)
49
+ Recurly::Resources.const_get(type, false)
50
+ else
51
+ raise ArgumentError, "Recurly type '#{type}' is unknown"
52
+ end
53
+ end
54
+
55
+ class Attribute
56
+ # The type of the attribute. Might be a class like `DateTime`
57
+ # or could be a Recurly object. In this case a symbol should be used.
58
+ # Example: :Account. To get the Recurly type use #recurly_class
59
+ # @return [Class,Symbol]
60
+ attr_reader :type
61
+
62
+ PRIMITIVE_TYPES = [
63
+ String,
64
+ Integer,
65
+ Float,
66
+ Hash,
67
+ ].freeze
68
+
69
+ def self.build(type, options = {})
70
+ if PRIMITIVE_TYPES.include? type
71
+ PrimitiveAttribute.new(type)
72
+ elsif type == :Boolean
73
+ BooleanAttribute.new
74
+ elsif type == DateTime
75
+ DateTimeAttribute.new
76
+ elsif type.is_a? Symbol
77
+ ResourceAttribute.new(type)
78
+ elsif type == Array
79
+ item_attr = build(options[:item_type])
80
+ ArrayAttribute.new(item_attr)
81
+ else
82
+ throw ArgumentError
83
+ end
84
+ end
85
+
86
+ def initialize(type = nil)
87
+ @type = type
88
+ end
89
+
90
+ def cast(value)
91
+ value
92
+ end
93
+
94
+ def recurly_class
95
+ @recurly_class ||= Schema.get_recurly_class(type)
96
+ end
97
+ end
98
+
99
+ class PrimitiveAttribute < Attribute
100
+ def is_valid?(value)
101
+ value.is_a? self.type
102
+ end
103
+ end
104
+
105
+ class BooleanAttribute < Attribute
106
+ def is_valid?(value)
107
+ [true, false].include? value
108
+ end
109
+ end
110
+
111
+ class DateTimeAttribute < Attribute
112
+ def is_valid?(value)
113
+ value.is_a?(String) || value.is_a?(DateTime)
114
+ end
115
+
116
+ def cast(value)
117
+ if value.is_a?(DateTime)
118
+ value
119
+ else
120
+ DateTime.parse(value)
121
+ end
122
+ end
123
+
124
+ def type
125
+ DateTime
126
+ end
127
+ end
128
+
129
+ class ResourceAttribute < Attribute
130
+ def is_valid?(value)
131
+ value.is_a? Hash
132
+ end
133
+
134
+ def cast(value)
135
+ self.recurly_class.cast(value)
136
+ end
137
+ end
138
+
139
+ class ArrayAttribute < Attribute
140
+ def is_valid?(value)
141
+ value.is_a? Array
142
+ end
143
+
144
+ def cast(value)
145
+ value.map do |v|
146
+ self.type.cast(v)
147
+ end
148
+ end
149
+ end
150
+ end
151
+
152
+ require_relative "./schema/schema_factory"
153
+ require_relative "./schema/schema_validator"
154
+ require_relative "./schema/resource_caster"
155
+ require_relative "./schema/request_caster"
156
+ end
@@ -1,17 +1,3 @@
1
1
  module Recurly
2
- module Version
3
- MAJOR = 2
4
- MINOR = 17
5
- PATCH = 5
6
- PRE = nil
7
-
8
- VERSION = [MAJOR, MINOR, PATCH, PRE].compact.join('.').freeze
9
-
10
- class << self
11
- def inspect
12
- VERSION.dup
13
- end
14
- alias to_s inspect
15
- end
16
- end
2
+ VERSION = "4.18.0"
17
3
  end
data/lib/recurly.rb CHANGED
@@ -1,141 +1,18 @@
1
- # Recurly is a Ruby client for Recurly's REST API.
2
- module Recurly
3
- require 'recurly/error'
4
- require 'recurly/helper'
5
- require 'recurly/api'
6
- require 'recurly/resource'
7
- require 'recurly/shipping_address'
8
- require 'recurly/billing_info'
9
- require 'recurly/custom_field'
10
- require 'recurly/account'
11
- require 'recurly/account_balance'
12
- require 'recurly/add_on'
13
- require 'recurly/address'
14
- require 'recurly/tax_detail'
15
- require 'recurly/tax_type'
16
- require 'recurly/juris_detail'
17
- require 'recurly/adjustment'
18
- require 'recurly/coupon'
19
- require 'recurly/credit_payment'
20
- require 'recurly/helper'
21
- require 'recurly/invoice'
22
- require 'recurly/invoice_collection'
23
- require 'recurly/js'
24
- require 'recurly/money'
25
- require 'recurly/measured_unit'
26
- require 'recurly/note'
27
- require 'recurly/plan'
28
- require 'recurly/redemption'
29
- require 'recurly/subscription'
30
- require 'recurly/subscription_add_on'
31
- require 'recurly/transaction'
32
- require 'recurly/usage'
33
- require 'recurly/version'
34
- require 'recurly/xml'
35
- require 'recurly/delivery'
36
- require 'recurly/gift_card'
37
- require 'recurly/purchase'
38
- require 'recurly/webhook'
39
-
40
- @subdomain = nil
41
-
42
- # This exception is raised if Recurly has not been configured.
43
- class ConfigurationError < Error
44
- end
45
-
46
- class << self
47
- # Set a config based on current thread context.
48
- # Any default set will say in effect unless overwritten in the config_params.
49
- # Call this method with out any arguments to have it unset the thread context config values.
50
- # @param config_params - Hash with the following keys: subdomain, api_key, default_currency
51
- def config(config_params = nil)
52
- Thread.current[:recurly_config] = config_params
53
- end
54
-
55
- # @return [String] A subdomain.
56
- def subdomain
57
- if Thread.current[:recurly_config] && Thread.current[:recurly_config][:subdomain]
58
- return Thread.current[:recurly_config][:subdomain]
59
- end
60
- @subdomain || 'api'
61
- end
62
- attr_writer :subdomain
63
-
64
- # @return [String] An API key.
65
- # @raise [ConfigurationError] If not configured.
66
- def api_key
67
- if Thread.current[:recurly_config] && Thread.current[:recurly_config][:api_key]
68
- return Thread.current[:recurly_config][:api_key]
69
- end
70
-
71
- defined? @api_key and @api_key or raise(
72
- ConfigurationError, "Recurly.api_key not configured"
73
- )
74
- end
75
- attr_writer :api_key
1
+ require "recurly/version"
2
+ require "recurly/schema"
3
+ require "recurly/request"
4
+ require "recurly/resource"
5
+ require "recurly/pager"
6
+ require "recurly/requests"
7
+ require "recurly/resources"
8
+ require "recurly/http"
9
+ require "recurly/errors"
10
+ require "recurly/connection_pool"
11
+ require "recurly/client"
76
12
 
77
- # @return [String, nil] A default currency.
78
- def default_currency
79
- if Thread.current[:recurly_config] && Thread.current[:recurly_config][:default_currency]
80
- return Thread.current[:recurly_config][:default_currency]
81
- end
82
-
83
- return @default_currency if defined? @default_currency
84
- @default_currency = 'USD'
85
- end
86
- attr_writer :default_currency
87
-
88
- # @return [JS] The Recurly.js module.
89
- def js
90
- JS
91
- end
92
-
93
- # Assigns a logger to log requests/responses and more.
94
- # The logger can only be set if the environment variable
95
- # `RECURLY_INSECURE_DEBUG` equals `true`.
96
- #
97
- # @return [Logger, nil]
98
- # @example
99
- # require 'logger'
100
- # Recurly.logger = Logger.new STDOUT
101
- # @example Rails applications automatically log to the Rails log:
102
- # Recurly.logger = Rails.logger
103
- # @example Turn off logging entirely:
104
- # Recurly.logger = nil # Or Recurly.logger = Logger.new nil
105
- attr_accessor :logger
106
-
107
- def logger=(logger)
108
- if ENV['RECURLY_INSECURE_DEBUG'].to_s.downcase == 'true'
109
- @logger = logger
110
- puts <<-MSG
111
- [WARNING] Recurly logger enabled. The logger has the potential to leak
112
- PII and should never be used in production environments.
113
- MSG
114
- else
115
- puts <<-MSG
116
- [WARNING] Recurly logger has been disabled. If you wish to use it,
117
- only do so in a non-production environment and make sure
118
- the `RECURLY_INSECURE_DEBUG` environment variable is set to `true`.
119
- MSG
120
- end
121
- end
122
-
123
- # Convenience logging method includes a Logger#progname dynamically.
124
- # @return [true, nil]
125
- def log level, message
126
- logger.send(level, name) { message }
127
- end
128
-
129
- if RUBY_VERSION <= "1.9.0"
130
- def const_defined? sym, inherit = false
131
- raise ArgumentError, "inherit must be false" if inherit
132
- super sym
133
- end
134
-
135
- def const_get sym, inherit = false
136
- raise ArgumentError, "inherit must be false" if inherit
137
- super sym
138
- end
139
- end
13
+ module Recurly
14
+ STRICT_MODE = ENV["RECURLY_STRICT_MODE"] && ENV["RECURLY_STRICT_MODE"].downcase == "true"
15
+ if STRICT_MODE
16
+ puts "[Recurly] [WARNING] STRICT_MODE enabled. This should only be used for testing purposes."
140
17
  end
141
18
  end