moonclerk 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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