moonclerk 1.0.1

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.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +3 -0
  5. data/Gemfile +4 -0
  6. data/README.md +118 -0
  7. data/Rakefile +1 -0
  8. data/bin/console +14 -0
  9. data/bin/setup +7 -0
  10. data/lib/moonclerk.rb +110 -0
  11. data/lib/moonclerk/api_operations/list.rb +40 -0
  12. data/lib/moonclerk/api_operations/request.rb +30 -0
  13. data/lib/moonclerk/api_resource.rb +42 -0
  14. data/lib/moonclerk/customer.rb +11 -0
  15. data/lib/moonclerk/errors/api_error.rb +4 -0
  16. data/lib/moonclerk/errors/authentication_error.rb +4 -0
  17. data/lib/moonclerk/errors/invalid_request_error.rb +10 -0
  18. data/lib/moonclerk/errors/moonclerk_error.rb +26 -0
  19. data/lib/moonclerk/form.rb +5 -0
  20. data/lib/moonclerk/list_object.rb +98 -0
  21. data/lib/moonclerk/moonclerk_object.rb +309 -0
  22. data/lib/moonclerk/payment.rb +10 -0
  23. data/lib/moonclerk/util.rb +49 -0
  24. data/lib/moonclerk/version.rb +3 -0
  25. data/moonclerk.gemspec +34 -0
  26. data/spec/dummy/README.rdoc +28 -0
  27. data/spec/dummy/Rakefile +6 -0
  28. data/spec/dummy/app/assets/images/.keep +0 -0
  29. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  30. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  31. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  32. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  33. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  34. data/spec/dummy/app/mailers/.keep +0 -0
  35. data/spec/dummy/app/models/.keep +0 -0
  36. data/spec/dummy/app/models/concerns/.keep +0 -0
  37. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  38. data/spec/dummy/bin/bundle +3 -0
  39. data/spec/dummy/bin/rails +4 -0
  40. data/spec/dummy/bin/rake +4 -0
  41. data/spec/dummy/bin/setup +29 -0
  42. data/spec/dummy/config.ru +4 -0
  43. data/spec/dummy/config/application.rb +26 -0
  44. data/spec/dummy/config/boot.rb +5 -0
  45. data/spec/dummy/config/database.yml +25 -0
  46. data/spec/dummy/config/environment.rb +5 -0
  47. data/spec/dummy/config/environments/development.rb +41 -0
  48. data/spec/dummy/config/environments/production.rb +79 -0
  49. data/spec/dummy/config/environments/test.rb +42 -0
  50. data/spec/dummy/config/initializers/assets.rb +11 -0
  51. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  52. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  53. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  54. data/spec/dummy/config/initializers/inflections.rb +16 -0
  55. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  56. data/spec/dummy/config/initializers/session_store.rb +3 -0
  57. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  58. data/spec/dummy/config/locales/en.yml +23 -0
  59. data/spec/dummy/config/routes.rb +56 -0
  60. data/spec/dummy/config/secrets.yml +22 -0
  61. data/spec/dummy/db/test.sqlite3 +0 -0
  62. data/spec/dummy/lib/assets/.keep +0 -0
  63. data/spec/dummy/log/.keep +0 -0
  64. data/spec/dummy/public/404.html +67 -0
  65. data/spec/dummy/public/422.html +67 -0
  66. data/spec/dummy/public/500.html +66 -0
  67. data/spec/dummy/public/favicon.ico +0 -0
  68. data/spec/models/moonclerk/customer_spec.rb +8 -0
  69. data/spec/moonclerk_spec.rb +7 -0
  70. data/spec/spec_helper.rb +63 -0
  71. metadata +314 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 93079434871ea140ae8925cb37243440f8462638
4
+ data.tar.gz: 3d8609c53bbf2fe1a9d40f420da14e5cd3c51455
5
+ SHA512:
6
+ metadata.gz: ed0e29fd060f41e5a43fcaa5baedbb58fe794a3b434857ca70e69c7fb22f78a00bc7dd52429fce2395e8752c0736f32899816bcf230ad5cdd4046c692fd08e1d
7
+ data.tar.gz: 7cb7b79a8bbd4168bc505be6e5b65309fe78b76424f22fff4898c9b44238dfc003a15f9b2f304db0de5b4c5ffae9956ff3c7d49baad095b50a39166bb943adc7
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.1
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in moonclerk.gemspec
4
+ gemspec
@@ -0,0 +1,118 @@
1
+ # Moonclerk
2
+
3
+ Moonclerk is a Ruby wrapper around Moonclerk's read-only REST API.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'moonclerk'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install moonclerk
20
+
21
+ ## Usage
22
+
23
+ Set your API key in an initializer (e.g. `config/initializers/moonclerk.rb`):
24
+
25
+ ```ruby
26
+ Moonclerk.api_key = "<API-KEY>"
27
+ ```
28
+
29
+ ### All
30
+
31
+ All objects have `find` and `list`, and some have `where` which allows filtering on certain attributes. For any `list` or `where` call, `next_page` and `previous_page` can be used to traverse the results returned from the API:
32
+
33
+ ```ruby
34
+ Moonclerk::Customer.list.next_page
35
+ ```
36
+
37
+ ### Customers (known as "Plans" in the MoonClerk UI)
38
+
39
+ To retrieve a customer:
40
+
41
+ ```ruby
42
+ Moonclerk::Customer.retrieve(id) # or Moonclerk::Customer.find(id)
43
+ ```
44
+
45
+ To list customers:
46
+
47
+ ```ruby
48
+ Moonclerk::Customer.list # or Moonclerk::Customer.all
49
+
50
+ # Options include count and offset
51
+ # NOTE: Count defaults to 10 (max is 100), and offset defaults to 0
52
+ ```
53
+
54
+ To filter customers:
55
+
56
+ ```ruby
57
+ Moonclerk::Customer.where(status: "active")
58
+
59
+ # Options include form_id, checkout_from, checkout_to, next_payment_from, next_payment_to, status, count, offset
60
+ # NOTE: Any parameter ending in _from or _to is expected to be a Date, Time or DateTime
61
+ # NOTE: Count defaults to 10 (max is 100), and offset defaults to 0
62
+ ```
63
+
64
+ ### Forms
65
+
66
+ To retrieve a form:
67
+
68
+ ```ruby
69
+ Moonclerk::Form.retrieve(id) # or Moonclerk::Form.find(id)
70
+ ```
71
+
72
+ To list forms:
73
+
74
+ ```ruby
75
+ Moonclerk::Form.list # or Moonclerk::Form.all
76
+
77
+ # Options include count and offset
78
+ # NOTE: Count defaults to 10 (max is 100), and offset defaults to 0
79
+ ```
80
+
81
+ ### Payments
82
+
83
+ To retrieve a payment:
84
+
85
+ ```ruby
86
+ Moonclerk::Payment.retrieve(id) # or Moonclerk::Payment.find(id)
87
+ ```
88
+
89
+ To list payments:
90
+
91
+ ```ruby
92
+ Moonclerk::Payment.list # or Moonclerk::Payment.all
93
+
94
+ # Options include count and offset
95
+ # NOTE: Count defaults to 10 (max is 100), and offset defaults to 0
96
+ ```
97
+
98
+ To filter payments:
99
+
100
+ ```ruby
101
+ Moonclerk::Payment.where(status: "active")
102
+
103
+ # Options include form_id, customer_id, date_from, date_to, status, count, offset
104
+ # NOTE: Any parameter ending in _from or _to is expected to be a Date, Time or DateTime
105
+ # NOTE: Count defaults to 10 (max is 100), and offset defaults to 0
106
+ ```
107
+
108
+ ## Development
109
+
110
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
111
+
112
+ ## Contributing
113
+
114
+ 1. Fork it ( https://github.com/[my-github-username]/moonclerk/fork )
115
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
116
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
117
+ 4. Push to the branch (`git push origin my-new-feature`)
118
+ 5. Create a new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "moonclerk"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ require "pry"
11
+ Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,110 @@
1
+ require "faraday"
2
+ require "json"
3
+
4
+ # Version
5
+ require "moonclerk/version"
6
+
7
+ # API Operations
8
+ require "moonclerk/api_operations/list"
9
+ require "moonclerk/api_operations/request"
10
+
11
+ # Resources
12
+ require "moonclerk/moonclerk_object"
13
+ require "moonclerk/api_resource"
14
+ require "moonclerk/list_object"
15
+ require "moonclerk/customer"
16
+ require "moonclerk/form"
17
+ require "moonclerk/payment"
18
+ require "moonclerk/util"
19
+
20
+ # Errors
21
+ require "moonclerk/errors/moonclerk_error"
22
+ require "moonclerk/errors/api_error"
23
+ require "moonclerk/errors/authentication_error"
24
+ require "moonclerk/errors/invalid_request_error"
25
+
26
+ module Moonclerk
27
+ API_BASE = "https://api.moonclerk.com"
28
+ API_VERSION = 1
29
+
30
+ class << self
31
+ attr_accessor :api_key
32
+ end
33
+
34
+ def self.request(method, url, params = {})
35
+ unless api_key
36
+ raise AuthenticationError.new('No API key provided. ' \
37
+ 'Set your API key using "Moonclerk.api_key = <API-KEY>". ' \
38
+ 'You can generate API keys from the Moonclerk web interface' \
39
+ 'by logging in at moonclerk.com.')
40
+ end
41
+
42
+ if api_key =~ /\s/
43
+ raise AuthenticationError.new('Your API key is invalid, as it contains ' \
44
+ 'whitespace. (HINT: You can double-check your API key from the ' \
45
+ 'MoonClerk web interface by logging in at moonclerk.com.)')
46
+ end
47
+
48
+ connection = Faraday.new
49
+ response = connection.send(method, url, params, headers)
50
+ parsed_response = JSON.parse(response.body)
51
+ symbolized_response = Util.symbolize_names(parsed_response)
52
+
53
+ if symbolized_response[:error]
54
+ handle_api_error(response)
55
+ else
56
+ symbolized_response[symbolized_response.keys.first]
57
+ end
58
+ end
59
+
60
+ def self.headers
61
+ {
62
+ "Authorization" => "Token token=#{api_key}",
63
+ "Accept" => "application/vnd.moonclerk+json;version=#{API_VERSION}"
64
+ }
65
+ end
66
+
67
+ def self.default_param_keys
68
+ [:count, :offset]
69
+ end
70
+
71
+ def self.general_api_error(status, response_body)
72
+ APIError.new("Invalid response object from API: #{response_body.inspect} " +
73
+ "(HTTP response code was #{status})", status, response_body)
74
+ end
75
+
76
+ def self.handle_api_error(response)
77
+ begin
78
+ error_obj = JSON.parse(response.body)
79
+ error_obj = Util.symbolize_names(error_obj)
80
+ error = error_obj[:error]
81
+ raise MoonclerkError.new unless error && error.is_a?(Hash)
82
+
83
+ rescue JSON::ParserError, StripeError
84
+ raise general_api_error(response.status, response.body)
85
+ end
86
+
87
+ case response.status
88
+ when 400, 404
89
+ raise invalid_request_error(error, response, error_obj)
90
+ when 401
91
+ raise authentication_error(error, response, error_obj)
92
+ else
93
+ raise api_error(error, response, error_obj)
94
+ end
95
+ end
96
+
97
+ def self.invalid_request_error(error, response, error_obj)
98
+ InvalidRequestError.new(error[:message], response.status, response.body, error_obj,
99
+ response.headers)
100
+ end
101
+
102
+ def self.authentication_error(error, response, error_obj)
103
+ AuthenticationError.new(error[:message], response.status, response.body, error_obj,
104
+ response.headers)
105
+ end
106
+
107
+ def self.api_error(error, response, error_obj)
108
+ APIError.new(error[:message], response.status, response.body, error_obj, response.headers)
109
+ end
110
+ end
@@ -0,0 +1,40 @@
1
+ module Moonclerk
2
+ module APIOperations
3
+ module List
4
+ def list(params = {})
5
+ response = request(:get, url, params)
6
+
7
+ klass = self.is_a?(Moonclerk::ListObject) ? self[:object] : self.class_name.downcase
8
+ obj = ListObject.construct_from({ data: response, object: klass })
9
+
10
+ # Set a count and offset so that we can fetch the same number when accessing the
11
+ # next and previous pages
12
+ obj.count = params[:count]
13
+ obj.offset = params[:offset]
14
+
15
+ obj
16
+ end
17
+
18
+ # This method returns a page of objects, so #all
19
+ # is not an appropriate method name, but it is
20
+ # aliased for convenience.
21
+ alias :all :list
22
+
23
+ def where(options = {})
24
+ options = Util.symbolize_names(options)
25
+ params = {}
26
+ (@permitted_attributes + default_param_keys).each do |key|
27
+ if options[key]
28
+ if key.to_s.split("_").last =~ /from|to/
29
+ params[key] = CGI.escape(options[key].strftime("%Y-%m-%d"))
30
+ else
31
+ params[key] = CGI.escape(options[key].to_s)
32
+ end
33
+ end
34
+ end
35
+
36
+ list(params)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,30 @@
1
+ module Moonclerk
2
+ module APIOperations
3
+ module Request
4
+ module ClassMethods
5
+
6
+ def request(method, url, params = {})
7
+ Moonclerk.request(method, url, params)
8
+ end
9
+
10
+ def default_param_keys
11
+ Moonclerk.default_param_keys
12
+ end
13
+ end
14
+
15
+ def self.included(base)
16
+ base.extend(ClassMethods)
17
+ end
18
+
19
+ protected
20
+
21
+ def request(method, url, params = {})
22
+ self.class.request(method, url, params)
23
+ end
24
+
25
+ def default_param_keys
26
+ self.class.default_param_keys
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,42 @@
1
+ module Moonclerk
2
+ class APIResource < MoonclerkObject
3
+ include Moonclerk::APIOperations::Request
4
+
5
+ def self.class_name
6
+ self.name.split('::')[-1]
7
+ end
8
+
9
+ def self.url
10
+ if self == APIResource
11
+ raise NotImplementedError.new('APIResource is an abstract class. You should perform actions on its subclasses (Customer, Payment, etc.)')
12
+ end
13
+ "#{API_BASE}/#{CGI.escape(class_name.downcase)}s"
14
+ end
15
+
16
+ def url
17
+ if self.is_a?(Moonclerk::ListObject)
18
+ return "#{API_BASE}/#{CGI.escape(self[:object])}s"
19
+ end
20
+
21
+ unless id = self['id']
22
+ raise InvalidRequestError.new("Could not determine which URL to request: #{self.class} instance has invalid ID: #{id.inspect}", 'id')
23
+ end
24
+ "#{self.class.url}/#{CGI.escape(id)}"
25
+ end
26
+
27
+ def refresh
28
+ response = request(:get, url, @retrieve_params)
29
+ refresh_from(response)
30
+ end
31
+
32
+ def self.retrieve(id)
33
+ instance = self.new(id.to_s)
34
+ instance.refresh
35
+ instance
36
+ end
37
+
38
+ def self.find(id)
39
+ self.retrieve(id)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,11 @@
1
+ module Moonclerk
2
+ class Customer < APIResource
3
+ extend Moonclerk::APIOperations::List
4
+ @permitted_attributes = [:form_id,
5
+ :checkout_from,
6
+ :checkout_to,
7
+ :next_payment_from,
8
+ :next_payment_to,
9
+ :status]
10
+ end
11
+ end
@@ -0,0 +1,4 @@
1
+ module Moonclerk
2
+ class APIError < MoonclerkError
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Moonclerk
2
+ class AuthenticationError < MoonclerkError
3
+ end
4
+ end
@@ -0,0 +1,10 @@
1
+ module Moonclerk
2
+ class InvalidRequestError < MoonclerkError
3
+ attr_accessor :param
4
+
5
+ def initialize(message, http_status=nil, http_body=nil, json_body=nil,
6
+ http_headers=nil)
7
+ super(message, http_status, http_body, json_body, http_headers)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,26 @@
1
+ module Moonclerk
2
+ class MoonclerkError < StandardError
3
+ attr_reader :message
4
+ attr_reader :http_status
5
+ attr_reader :http_body
6
+ attr_reader :http_headers
7
+ attr_reader :request_id
8
+ attr_reader :json_body
9
+
10
+ def initialize(message=nil, http_status=nil, http_body=nil, json_body=nil,
11
+ http_headers=nil)
12
+ @message = message
13
+ @http_status = http_status
14
+ @http_body = http_body
15
+ @http_headers = http_headers || {}
16
+ @json_body = json_body
17
+ @request_id = @http_headers["x-request-id"]
18
+ end
19
+
20
+ def to_s
21
+ status_string = @http_status.nil? ? "" : "(Status #{@http_status}) "
22
+ id_string = @request_id.nil? ? "" : "(Request #{@request_id}) "
23
+ "#{status_string}#{id_string}#{@message}"
24
+ end
25
+ end
26
+ end