paddle 1.0.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +7 -0
  2. data/.env.example +3 -0
  3. data/Gemfile +13 -0
  4. data/Gemfile.lock +31 -0
  5. data/README.md +404 -0
  6. data/Rakefile +8 -10
  7. data/bin/console +25 -0
  8. data/bin/setup +8 -0
  9. data/lib/paddle/classic/client.rb +87 -0
  10. data/lib/paddle/classic/collection.rb +29 -0
  11. data/lib/paddle/classic/objects/charge.rb +6 -0
  12. data/lib/paddle/classic/objects/coupon.rb +6 -0
  13. data/lib/paddle/classic/objects/license.rb +6 -0
  14. data/lib/paddle/classic/objects/modifier.rb +6 -0
  15. data/lib/paddle/classic/objects/pay_link.rb +6 -0
  16. data/lib/paddle/classic/objects/payment.rb +6 -0
  17. data/lib/paddle/classic/objects/payment_refund.rb +6 -0
  18. data/lib/paddle/classic/objects/plan.rb +6 -0
  19. data/lib/paddle/classic/objects/product.rb +6 -0
  20. data/lib/paddle/classic/objects/transaction.rb +6 -0
  21. data/lib/paddle/classic/objects/user.rb +6 -0
  22. data/lib/paddle/classic/objects/webhook.rb +6 -0
  23. data/lib/paddle/classic/resource.rb +63 -0
  24. data/lib/paddle/classic/resources/charges.rb +13 -0
  25. data/lib/paddle/classic/resources/coupons.rb +33 -0
  26. data/lib/paddle/classic/resources/licenses.rb +15 -0
  27. data/lib/paddle/classic/resources/modifiers.rb +26 -0
  28. data/lib/paddle/classic/resources/pay_links.rb +13 -0
  29. data/lib/paddle/classic/resources/payments.rb +24 -0
  30. data/lib/paddle/classic/resources/plans.rb +21 -0
  31. data/lib/paddle/classic/resources/products.rb +12 -0
  32. data/lib/paddle/classic/resources/transactions.rb +12 -0
  33. data/lib/paddle/classic/resources/users.rb +42 -0
  34. data/lib/paddle/classic/resources/webhooks.rb +12 -0
  35. data/lib/paddle/client.rb +71 -0
  36. data/lib/paddle/collection.rb +27 -0
  37. data/lib/paddle/configuration.rb +32 -0
  38. data/lib/paddle/error.rb +4 -0
  39. data/lib/paddle/models/address.rb +30 -0
  40. data/lib/paddle/models/adjustment.rb +20 -0
  41. data/lib/paddle/models/business.rb +30 -0
  42. data/lib/paddle/models/customer.rb +30 -0
  43. data/lib/paddle/models/discount.rb +30 -0
  44. data/lib/paddle/models/event.rb +14 -0
  45. data/lib/paddle/models/event_type.rb +14 -0
  46. data/lib/paddle/models/notification.rb +30 -0
  47. data/lib/paddle/models/notification_log.rb +4 -0
  48. data/lib/paddle/models/notification_setting.rb +34 -0
  49. data/lib/paddle/models/price.rb +30 -0
  50. data/lib/paddle/models/pricing_preview.rb +15 -0
  51. data/lib/paddle/models/product.rb +30 -0
  52. data/lib/paddle/models/subscription.rb +56 -0
  53. data/lib/paddle/models/transaction.rb +43 -0
  54. data/lib/paddle/object.rb +19 -0
  55. data/lib/paddle/version.rb +5 -0
  56. data/lib/paddle.rb +73 -5
  57. data/paddle.gemspec +30 -0
  58. metadata +100 -124
  59. data/.autotest +0 -23
  60. data/CHANGELOG.rdoc +0 -6
  61. data/Manifest.txt +0 -16
  62. data/README.rdoc +0 -62
  63. data/lib/images/ruby.png +0 -0
  64. data/lib/rdoc/discover.rb +0 -1
  65. data/lib/rdoc/generator/paddle.rb +0 -144
  66. data/lib/templates/classfile.html.erb +0 -115
  67. data/lib/templates/container.xml +0 -7
  68. data/lib/templates/content.opf.erb +0 -34
  69. data/lib/templates/cover.html.erb +0 -18
  70. data/lib/templates/title.html.erb +0 -18
  71. data/lib/templates/toc.ncx.erb +0 -36
  72. data/test/test_paddle.rb +0 -13
@@ -0,0 +1,6 @@
1
+ module Paddle
2
+ module Classic
3
+ class Plan < Object
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module Paddle
2
+ module Classic
3
+ class Product < Object
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module Paddle
2
+ module Classic
3
+ class Transaction < Object
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module Paddle
2
+ module Classic
3
+ class User < Object
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module Paddle
2
+ module Classic
3
+ class Webhook < Object
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,63 @@
1
+ module Paddle
2
+ module Classic
3
+ class Resource
4
+ attr_reader :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ private
11
+
12
+ # def get_request(url, params: {}, headers: {})
13
+ # handle_response client.connection.get(url, params, headers)
14
+ # end
15
+
16
+ def post_request(url, body: {}, headers: {})
17
+ attrs = {vendor_id: client.vendor_id, vendor_auth_code: client.vendor_auth_code}
18
+ handle_response client.connection.post(url, attrs.merge(body), headers)
19
+ end
20
+
21
+ # def patch_request(url, body:, headers: {})
22
+ # handle_response client.connection.patch(url, body, headers)
23
+ # end
24
+
25
+ # def put_request(url, body:, headers: {})
26
+ # handle_response client.connection.put(url, body, headers)
27
+ # end
28
+
29
+ # def delete_request(url, params: {}, headers: {})
30
+ # handle_response client.connection.delete(url, params, headers)
31
+ # end
32
+
33
+ def handle_response(response)
34
+ case response.status
35
+ when 400
36
+ raise Error, "Error 400: Your request was malformed. '#{response.body["message"]}'"
37
+ when 401
38
+ raise Error, "Error 401: You did not supply valid authentication credentials. '#{response.body["error"]}'"
39
+ when 403
40
+ raise Error, "Error 403: You are not allowed to perform that action. '#{response.body["error"]}'"
41
+ when 404
42
+ raise Error, "Error 404: No results were found for your request. '#{response.body["error"]}'"
43
+ when 409
44
+ raise Error, "Error 409: Your request was a conflict. '#{response.body["message"]}'"
45
+ when 429
46
+ raise Error, "Error 429: Your request exceeded the API rate limit. '#{response.body["error"]}'"
47
+ when 500
48
+ raise Error, "Error 500: We were unable to perform the request due to server-side problems. '#{response.body["error"]}'"
49
+ when 503
50
+ raise Error, "Error 503: You have been rate limited for sending more than 20 requests per second. '#{response.body["error"]}'"
51
+ when 501
52
+ raise Error, "Error 501: This resource has not been implemented. '#{response.body["error"]}'"
53
+ end
54
+
55
+ if response.body && response.body["error"]
56
+ raise Error, "Error #{response.body["error"]["code"]} - #{response.body["error"]["message"]}"
57
+ end
58
+
59
+ response
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,13 @@
1
+ module Paddle
2
+ module Classic
3
+ class ChargesResource < Resource
4
+
5
+ def create(subscription_id:, amount:, charge_name:)
6
+ attrs = {amount: amount, charge_name: charge_name}
7
+ response = post_request("2.0/subscription/#{subscription_id}/charge", body: attrs)
8
+ Charge.new(response.body["response"])
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,33 @@
1
+ module Paddle
2
+ module Classic
3
+ class CouponsResource < Resource
4
+
5
+ def list(product_id:)
6
+ response = post_request("2.0/product/list_coupons", body: {product_id: product_id})
7
+ Collection.from_response(response, type: Coupon)
8
+ end
9
+
10
+ def create(coupon_type:, discount_type:, discount_amount:, **params)
11
+ attrs = {coupon_type: coupon_type, discount_type: discount_type, discount_amount: discount_amount}
12
+
13
+ response = post_request("2.1/product/create_coupon", body: attrs.merge(params))
14
+
15
+ coupons = response.body["response"]["coupon_codes"]
16
+
17
+ coupons.map {|c| Paddle::Coupon.new(code: c)}
18
+ end
19
+
20
+ def delete(coupon_code:, product_id:)
21
+ attrs = {coupon_code: coupon_code, product_id: product_id}
22
+ response = post_request("2.0/product/delete_coupon", body: attrs)
23
+ return true if response.success?
24
+ end
25
+
26
+ def update(**params)
27
+ response = post_request("2.1/product/update_coupon", body: params)
28
+ return true if response.success?
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,15 @@
1
+ module Paddle
2
+ module Classic
3
+ class LicensesResource < Resource
4
+
5
+ def generate(product_id:, allowed_uses:, **params)
6
+ attrs = {product_id: product_id, allowed_uses: allowed_uses}
7
+
8
+ response = post_request("2.0/product/generate_license", body: attrs.merge(params))
9
+
10
+ License.new(response.body["response"]) if response.success?
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,26 @@
1
+ module Paddle
2
+ module Classic
3
+ class ModifiersResource < Resource
4
+
5
+ def list(**params)
6
+ response = post_request("2.0/subscription/modifiers", body: params)
7
+ Collection.from_response(response, type: Modifier)
8
+ end
9
+
10
+ def create(subscription_id:, modifier_amount:, **params)
11
+ attrs = {subscription_id: subscription_id, modifier_amount: modifier_amount}
12
+ create_response = post_request("2.0/subscription/modifiers/create", body: attrs.merge(params))
13
+
14
+ response = post_request("2.0/subscription/modifiers", body: {subscription_id: subscription_id} )
15
+ Collection.from_response(response, type: Modifier)
16
+ end
17
+
18
+ def delete(modifier_id:)
19
+ attrs = {modifier_id: modifier_id}
20
+ response = post_request("2.0/subscription/modifiers/delete", body: attrs)
21
+ return true if response.success?
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,13 @@
1
+ module Paddle
2
+ module Classic
3
+ class PayLinksResource < Resource
4
+
5
+ def generate(**params)
6
+ response = post_request("2.0/product/generate_pay_link", body: params)
7
+
8
+ PayLink.new(response.body["response"]) if response.success?
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,24 @@
1
+ module Paddle
2
+ module Classic
3
+ class PaymentsResource < Resource
4
+
5
+ def list(**params)
6
+ response = post_request("2.0/subscription/payments", body: params)
7
+ Collection.from_response(response, type: Payment)
8
+ end
9
+
10
+ def reschedule(payment_id:, date:)
11
+ attrs = {payment_id: payment_id, date: date}
12
+ response = post_request("2.0/subscription/payments_reschedule", body: attrs)
13
+ return true if response.success?
14
+ end
15
+
16
+ def refund(order_id:, **params)
17
+ attrs = {order_id: order_id}
18
+ response = post_request("2.0/payment/refund", body: attrs.merge(params))
19
+ PaymentRefund.new(response.body["response"])
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,21 @@
1
+ module Paddle
2
+ module Classic
3
+ class PlansResource < Resource
4
+
5
+ def list
6
+ response = post_request("2.0/subscription/plans")
7
+ Collection.from_response(response, type: Plan)
8
+ end
9
+
10
+ def create(name:, type:, **params)
11
+ attrs = {plan_name: name, plan_type: type}
12
+ create_response = post_request("2.0/subscription/plans_create", body: attrs.merge(params))
13
+
14
+ # After creating the Plan, because it doesn't return the whole record, grab it from the API and return that
15
+ response = post_request("2.0/subscription/plans", body: {plan: create_response.body["response"]["product_id"]} )
16
+ Plan.new(response.body.dig("response")[0]) if response.success?
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,12 @@
1
+ module Paddle
2
+ module Classic
3
+ class ProductsResource < Resource
4
+
5
+ def list
6
+ response = post_request("2.0/product/get_products")
7
+ Collection.from_response(response, type: Product, key: "products")
8
+ end
9
+
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module Paddle
2
+ module Classic
3
+ class TransactionsResource < Resource
4
+
5
+ def list(entity:, id:)
6
+ response = post_request("2.0/#{entity}/#{id}/transactions")
7
+ Collection.from_response(response, type: Transaction)
8
+ end
9
+
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,42 @@
1
+ module Paddle
2
+ module Classic
3
+ class UsersResource < Resource
4
+
5
+ def list(**params)
6
+ response = post_request("2.0/subscription/users", body: params)
7
+ Collection.from_response(response, type: User)
8
+ end
9
+
10
+ def update(subscription_id:, **params)
11
+ attrs = {subscription_id: subscription_id}
12
+ response = post_request("2.0/subscription/users/update", body: attrs.merge(params))
13
+ User.new(response.body["response"]) if response.success?
14
+ end
15
+
16
+ def pause(subscription_id:, **params)
17
+ attrs = {subscription_id: subscription_id, pause: true}
18
+ response = post_request("2.0/subscription/users/update", body: attrs.merge(params))
19
+ User.new(response.body["response"]) if response.success?
20
+ end
21
+
22
+ def unpause(subscription_id:, **params)
23
+ attrs = {subscription_id: subscription_id, pause: false}
24
+ response = post_request("2.0/subscription/users/update", body: attrs.merge(params))
25
+ User.new(response.body["response"]) if response.success?
26
+ end
27
+
28
+ def update_postcode(subscription_id:, postcode:)
29
+ attrs = {subscription_id: subscription_id, postcode: postcode}
30
+ response = post_request("2.0/subscription/users/postcode", body: attrs)
31
+ return true if response.success?
32
+ end
33
+
34
+ def cancel(subscription_id:)
35
+ attrs = {subscription_id: subscription_id}
36
+ response = post_request("2.0/subscription/users_cancel", body: attrs)
37
+ return true if response.success?
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,12 @@
1
+ module Paddle
2
+ module Classic
3
+ class WebhooksResource < Resource
4
+
5
+ def list(**params)
6
+ response = post_request("2.0/alert/webhooks", body: params)
7
+ Collection.from_response(response, type: Webhook, key: "data")
8
+ end
9
+
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,71 @@
1
+ module Paddle
2
+ class Client
3
+
4
+ class << self
5
+
6
+ def connection
7
+ @connection ||= Faraday.new(Paddle.config.url) do |conn|
8
+ conn.request :authorization, :Bearer, Paddle.config.api_key
9
+
10
+ conn.headers = {
11
+ "User-Agent" => "paddlerb/v#{VERSION} (github.com/deanpcmad/paddlerb)"
12
+ }
13
+
14
+ conn.request :json
15
+
16
+ conn.response :json, content_type: "application/json"
17
+ end
18
+ end
19
+
20
+
21
+ def get_request(url, params: {}, headers: {})
22
+ handle_response connection.get(url, params, headers)
23
+ end
24
+
25
+ def post_request(url, body: {}, headers: {})
26
+ handle_response connection.post(url, body, headers)
27
+ end
28
+
29
+ def patch_request(url, body:, headers: {})
30
+ handle_response connection.patch(url, body, headers)
31
+ end
32
+
33
+ def delete_request(url, headers: {})
34
+ handle_response connection.delete(url, headers)
35
+ end
36
+
37
+ def handle_response(response)
38
+ case response.status
39
+ when 400
40
+ raise Error, "Error 400: Your request was malformed. '#{response.body["error"]["code"]}'"
41
+ when 401
42
+ raise Error, "Error 401: You did not supply valid authentication credentials. '#{response.body["error"]}'"
43
+ when 403
44
+ raise Error, "Error 403: You are not allowed to perform that action. '#{response.body["error"]["code"]}'"
45
+ when 404
46
+ raise Error, "Error 404: No results were found for your request. '#{response.body["error"]["code"]}'"
47
+ when 409
48
+ raise Error, "Error 409: Your request was a conflict. '#{response.body["error"]["code"]}'"
49
+ when 429
50
+ raise Error, "Error 429: Your request exceeded the API rate limit. '#{response.body["error"]["code"]}'"
51
+ when 500
52
+ raise Error, "Error 500: We were unable to perform the request due to server-side problems. '#{response.body["error"]["code"]}'"
53
+ when 503
54
+ raise Error, "Error 503: You have been rate limited for sending more than 20 requests per second. '#{response.body["error"]["code"]}'"
55
+ when 501
56
+ raise Error, "Error 501: This resource has not been implemented. '#{response.body["error"]["code"]}'"
57
+ when 204
58
+ return true
59
+ end
60
+
61
+ if response.body && response.body["error"]
62
+ raise Error, "Error #{response.body["error"]["code"]} - #{response.body["errors"]["message"]}"
63
+ end
64
+
65
+ response
66
+ end
67
+
68
+ end
69
+
70
+ end
71
+ end
@@ -0,0 +1,27 @@
1
+ module Paddle
2
+ class Collection
3
+ attr_reader :data, :total
4
+
5
+ def self.from_response(response, type:)
6
+ body = response.body
7
+
8
+ data = body["data"].map { |attrs| type.new(attrs) }
9
+
10
+ if body["meta"]["pagination"]
11
+ total = body["meta"]["pagination"]["estimated_total"]
12
+ else
13
+ total = body["data"].count
14
+ end
15
+
16
+ new(
17
+ data: data,
18
+ total: total
19
+ )
20
+ end
21
+
22
+ def initialize(data:, total:)
23
+ @data = data
24
+ @total = total
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Paddle
4
+ class Configuration
5
+
6
+ attr_reader :environment
7
+
8
+ attr_accessor :api_key
9
+
10
+ def initialize
11
+ @environment ||= :production
12
+ end
13
+
14
+ def environment=(env)
15
+ env = env.to_sym
16
+ unless [:development, :sandbox, :production].include?(env)
17
+ raise ArgumentError, "#{env.inspect} is not a valid environment"
18
+ end
19
+ @environment = env
20
+ end
21
+
22
+ def url
23
+ case @environment
24
+ when :production
25
+ "https://api.paddle.com"
26
+ when :development, :sandbox
27
+ "https://sandbox-api.paddle.com"
28
+ end
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,4 @@
1
+ module Paddle
2
+ class Error < StandardError
3
+ end
4
+ end
@@ -0,0 +1,30 @@
1
+ module Paddle
2
+ class Address < Object
3
+
4
+ class << self
5
+
6
+ def list(customer:, **params)
7
+ response = Client.get_request("customers/#{customer}/addresses", params: params)
8
+ Collection.from_response(response, type: Address)
9
+ end
10
+
11
+ def create(customer:, country_code:, postal_code:, **params)
12
+ attrs = {country_code: country_code, postal_code: postal_code}
13
+ response = Client.post_request("customers/#{customer}/addresses", body: attrs.merge(params))
14
+ Address.new(response.body["data"])
15
+ end
16
+
17
+ def retrieve(customer:, id:)
18
+ response = Client.get_request("customers/#{customer}/addresses/#{id}")
19
+ Address.new(response.body["data"])
20
+ end
21
+
22
+ def update(customer:, id:, **params)
23
+ response = Client.patch_request("customers/#{customer}/addresses/#{id}", body: params)
24
+ Address.new(response.body["data"])
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,20 @@
1
+ module Paddle
2
+ class Adjustment < Object
3
+
4
+ class << self
5
+
6
+ def list(**params)
7
+ response = Client.get_request("adjustments", params: params)
8
+ Collection.from_response(response, type: Adjustment)
9
+ end
10
+
11
+ def create(transaction_id:, action:, reason:, items:, **params)
12
+ attrs = {transaction_id: transaction_id, action: action, reason: reason, items: items}
13
+ response = Client.post_request("adjustments", body: attrs.merge(params))
14
+ Adjustment.new(response.body["data"])
15
+ end
16
+
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,30 @@
1
+ module Paddle
2
+ class Business < Object
3
+
4
+ class << self
5
+
6
+ def list(customer:, **params)
7
+ response = Client.get_request("customers/#{customer}/businesses", params: params)
8
+ Collection.from_response(response, type: Business)
9
+ end
10
+
11
+ def create(customer:, name:, **params)
12
+ attrs = {name: name}
13
+ response = Client.post_request("customers/#{customer}/businesses", body: attrs.merge(params))
14
+ Business.new(response.body["data"])
15
+ end
16
+
17
+ def retrieve(customer:, id:)
18
+ response = Client.get_request("customers/#{customer}/businesses/#{id}")
19
+ Business.new(response.body["data"])
20
+ end
21
+
22
+ def update(customer:, id:, **params)
23
+ response = Client.patch_request("customers/#{customer}/businesses/#{id}", body: params)
24
+ Business.new(response.body["data"])
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ module Paddle
2
+ class Customer < Object
3
+
4
+ class << self
5
+
6
+ def list(**params)
7
+ response = Client.get_request("customers", params: params)
8
+ Collection.from_response(response, type: Customer)
9
+ end
10
+
11
+ def create(email:, **params)
12
+ attrs = {email: email}
13
+ response = Client.post_request("customers", body: attrs.merge(params))
14
+ Customer.new(response.body["data"])
15
+ end
16
+
17
+ def retrieve(id:)
18
+ response = Client.get_request("customers/#{id}")
19
+ Customer.new(response.body["data"])
20
+ end
21
+
22
+ def update(id:, **params)
23
+ response = Client.patch_request("customers/#{id}", body: params)
24
+ Customer.new(response.body["data"])
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ module Paddle
2
+ class Discount < Object
3
+
4
+ class << self
5
+
6
+ def list(**params)
7
+ response = Client.get_request("discounts", params: params)
8
+ Collection.from_response(response, type: Discount)
9
+ end
10
+
11
+ def create(amount:, description:, type:, **params)
12
+ attrs = {amount: amount, description: description, type: type}
13
+ response = Client.post_request("discounts", body: attrs.merge(params))
14
+ Discount.new(response.body["data"])
15
+ end
16
+
17
+ def retrieve(id:)
18
+ response = Client.get_request("discounts/#{id}")
19
+ Discount.new(response.body["data"])
20
+ end
21
+
22
+ def update(id:, **params)
23
+ response = Client.patch_request("discounts/#{id}", body: params)
24
+ Discount.new(response.body["data"])
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,14 @@
1
+ module Paddle
2
+ class Event < Object
3
+
4
+ class << self
5
+
6
+ def list(**params)
7
+ response = Client.get_request("events", params: params)
8
+ Collection.from_response(response, type: Event)
9
+ end
10
+
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module Paddle
2
+ class EventType < Object
3
+
4
+ class << self
5
+
6
+ def list
7
+ response = Client.get_request("event-types")
8
+ Collection.from_response(response, type: EventType)
9
+ end
10
+
11
+ end
12
+
13
+ end
14
+ end