scribesend 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 81015fd2ae5f77f5e6045f9d95d77edb912cb993
4
+ data.tar.gz: ea7630f5d1f27d7ba2f57522545c2264fd1bfc0a
5
+ SHA512:
6
+ metadata.gz: 1c5fd6b0a1f1c9f8831e4ce5e74a05796893584da95316f7b37beb875cf6d82fa0f9c84fca05ca21d8913745b565715000842488505559810fe665b6a96ed340
7
+ data.tar.gz: 102319d7d4eec6f712b6f254bdd2c52b09c18a26ac57e4eb75af7cdeac5508f77d61894344d481eb42d5cd9734c86e8959a873c8ebb9620b8dc11e63c4052e94
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ /scribesend-*.gem
2
+ /Gemfile.lock
3
+ .rvmrc
4
+ Gemfile.lock
data/CHANGELOG ADDED
@@ -0,0 +1,3 @@
1
+ === 0.0.1 2015-05-13
2
+
3
+ * Initial release
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2015- Scribesend
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,17 @@
1
+ # Scribesend
2
+
3
+ Ruby bindings for [https://scribesend.com](https://scribesend.com)
4
+
5
+ ## Documentation
6
+
7
+ [API Docs](https://scribesend.com/docs/ruby)
8
+
9
+ ## Installation
10
+
11
+ To install, simply run:
12
+
13
+ gem install scribesend
14
+
15
+ And then import the client in your application:
16
+
17
+ require 'scribesend'
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.4
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
3
+
4
+ libs = " -r irb/completion"
5
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/scribesend'}"
6
+ puts "Initializing Scribesend..."
7
+ exec "#{irb} #{libs} --simple-prompt"
@@ -0,0 +1,9 @@
1
+ module Scribesend
2
+ class Account < APIResource
3
+ def self.retrieve_entry_lines(id, opts={})
4
+ instance = self.new(id, opts)
5
+ response, opts = instance.class.request(:get, instance.url + "/entry_lines", opts)
6
+ Util.convert_to_scribesend_object(response, opts)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,77 @@
1
+ module Scribesend
2
+ class APIResource < ScribesendObject
3
+ def self.class_name
4
+ self.name.split('::')[-1]
5
+ end
6
+
7
+ def self.url
8
+ if self == APIResource
9
+ raise NotImplementedError.new("APIResource is an abstract class. You should perform actions on its subclasses (Account, Entry, etc.)")
10
+ end
11
+ if (self.class_name[-1..-1] == 's' || self.class_name[-1..-1] == 'h')
12
+ return "/#{CGI.escape(self.class_name.downcase)}es"
13
+ elsif (self.class_name[-1..-1] == 'y')
14
+ return "/#{CGI.escape(self.class_name.downcase[0..-2])}ies"
15
+ else
16
+ return "/#{CGI.escape(class_name.downcase)}s"
17
+ end
18
+ end
19
+
20
+ def url
21
+ unless id = self['id']
22
+ raise Error.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
+ # API Methods
28
+
29
+ def self.request(method, url, params={}, opts={})
30
+ headers = opts.clone
31
+ api_key = headers.delete(:api_key)
32
+ api_base = headers.delete(:api_base)
33
+
34
+ response, opts = Scribesend.request(method, url, api_key, params, headers, api_base)
35
+ [response, opts]
36
+ end
37
+
38
+ def refresh
39
+ response, opts = self.class.request(:get, url, @retrieve_params)
40
+ refresh_from(response, opts)
41
+ end
42
+
43
+ def self.retrieve(id, opts={})
44
+ instance = self.new(id, opts)
45
+ instance.refresh
46
+ instance
47
+ end
48
+
49
+ def self.all(filters={}, opts={})
50
+ response, opts = self.request(:get, url, filters, opts)
51
+ Util.convert_to_scribesend_object(response, opts)
52
+ end
53
+
54
+ def self.create(params={}, opts={})
55
+ response, opts = self.request(:post, url, params, opts)
56
+ Util.convert_to_scribesend_object(response, opts)
57
+ end
58
+
59
+ # def delete(params={}, opts={})
60
+ # response, opts = self.class.request(:delete, url, params, opts)
61
+ # refresh_from(response, opts)
62
+ # self
63
+ # end
64
+
65
+ def save(params={})
66
+ values = self.class.serialize_params(self).merge(params)
67
+
68
+ if values.length > 0
69
+ values.delete(:id)
70
+
71
+ response, opts = self.class.request(:post, url, values)
72
+ refresh_from(response, opts)
73
+ end
74
+ self
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,27 @@
1
+ module Scribesend
2
+ class Entry < APIResource
3
+ def self.create(params={}, opts={})
4
+ opts = {
5
+ :content_type => 'application/json'
6
+ }.merge(opts)
7
+
8
+ response, opts = self.request(:post, url, params.to_json, opts)
9
+ Util.convert_to_scribesend_object(response, opts)
10
+ end
11
+
12
+ def self.create_charge_entry(params, opts={})
13
+ response, opts = self.request(:post, url + "/charge", params, opts)
14
+ Util.convert_to_scribesend_object(response, opts)
15
+ end
16
+
17
+ def self.create_capture_entry(params, opts={})
18
+ response, opts = self.request(:post, url + "/capture", params, opts)
19
+ Util.convert_to_scribesend_object(response, opts)
20
+ end
21
+
22
+ def self.create_charge_and_capture_entry(params, opts={})
23
+ response, opts = self.request(:post, url + "/charge_and_capture", params, opts)
24
+ Util.convert_to_scribesend_object(response, opts)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,30 @@
1
+ module Scribesend
2
+ class EntryLine < APIResource
3
+ def self.url
4
+ return nil
5
+ end
6
+
7
+ def url
8
+ return nil
9
+ end
10
+
11
+ def self.retrieve(id, opts=nil)
12
+ raise NotImplementedError.new("Entry_lines cannot be retrieved individually. Retrieve an entry instead.")
13
+ end
14
+
15
+ def self.all
16
+ raise NotImplementedError.new("Entry_lines cannot be retrieved individually. Retrieve an entry instead.")
17
+ end
18
+
19
+ def self.create
20
+ raise NotImplementedError.new("Entry_lines cannot be created outside of an entry.")
21
+ end
22
+
23
+ def save
24
+ raise NotImplementedError.new("Entry_lines cannot be updated within an entry. " \
25
+ "If you want to make changes to an entry_line, you will need to create " \
26
+ "a new entry that adjusts the corresponding accounts and values. " \
27
+ "See https://scribesend.com/docs for more details.")
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,17 @@
1
+ module Scribesend
2
+ class Error < StandardError
3
+ attr_reader :message, :http_status, :http_body, :json_body
4
+
5
+ def initialize(message=nil, http_status=nil, http_body=nil, json_body=nil)
6
+ @message = message
7
+ @http_status = http_status
8
+ @http_body = http_body
9
+ @json_body = json_body
10
+ end
11
+
12
+ def to_s
13
+ s = @http_status.nil? ? "" : "(Status #{@http_status}): "
14
+ "#{s}#{@message}"
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ module Scribesend
2
+ class ListObject < ScribesendObject
3
+ def [](k)
4
+ case k
5
+ when String, Symbol
6
+ super
7
+ else
8
+ raise ArgumentError.new("You tried to access the #{k.inspect} index, but ListObject types only support String keys. (HINT: List calls return an object with a 'data' (which is the data array). You likely want to call #data[#{k.inspect}])")
9
+ end
10
+ end
11
+
12
+ def each(&blk)
13
+ self.data.each(&blk)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,241 @@
1
+ module Scribesend
2
+ class ScribesendObject
3
+ include Enumerable
4
+
5
+ @@immutable_values = Set.new([:id])
6
+
7
+ # The default :id method is deprecated and isn't useful to us
8
+ if method_defined?(:id)
9
+ undef :id
10
+ end
11
+
12
+ def initialize(id=nil, opts={})
13
+ if id.kind_of?(Hash)
14
+ @retrieve_params = id.dup
15
+ @retrieve_params.delete(:id)
16
+ id = id[:id]
17
+ else
18
+ @retrieve_params = {}
19
+ end
20
+
21
+ @opts = opts
22
+ @values = {}
23
+ @unsaved_values = Set.new
24
+ @transient_values = Set.new
25
+ @values[:id] = id if id
26
+ end
27
+
28
+ def self.construct_from(values, opts={})
29
+ self.new(values[:id]).refresh_from(values, opts)
30
+ end
31
+
32
+ def to_s(*args)
33
+ JSON.pretty_generate(@values)
34
+ end
35
+
36
+ def inspect
37
+ id_string = (self.respond_to?(:id) && !self.id.nil?) ? " id=#{self.id}" : ""
38
+ "#<#{self.class}:0x#{self.object_id.to_s(16)}#{id_string}> JSON: " + JSON.pretty_generate(@values)
39
+ end
40
+
41
+ def refresh_from(values, opts, partial=false)
42
+ @opts = opts
43
+ @original_values = Marshal.load(Marshal.dump(values))
44
+ removed = partial ? Set.new : Set.new(@values.keys - values.keys)
45
+ added = Set.new(values.keys - @values.keys)
46
+
47
+ instance_eval do
48
+ remove_accessors(removed)
49
+ add_accessors(added)
50
+ end
51
+ removed.each do |k|
52
+ @values.delete(k)
53
+ @transient_values.add(k)
54
+ @unsaved_values.delete(k)
55
+ end
56
+ values.each do |k, v|
57
+ @values[k] = Util.convert_to_scribesend_object(v, @opts)
58
+ @transient_values.delete(k)
59
+ @unsaved_values.delete(k)
60
+ end
61
+
62
+ return self
63
+ end
64
+
65
+ def [](k)
66
+ @values[k.to_sym]
67
+ end
68
+
69
+ def []=(k, v)
70
+ send(:"#{k}=", v)
71
+ end
72
+
73
+ def keys
74
+ @values.keys
75
+ end
76
+
77
+ def values
78
+ @values.values
79
+ end
80
+
81
+ def to_json(*a)
82
+ JSON.generate(@values)
83
+ end
84
+
85
+ def as_json(*a)
86
+ @values.as_json(*a)
87
+ end
88
+
89
+ def to_hash
90
+ @values.inject({}) do |acc, (key, value)|
91
+ acc[key] = value.respond_to?(:to_hash) ? value.to_hash : value
92
+ acc
93
+ end
94
+ end
95
+
96
+ def each(&blk)
97
+ @values.each(&blk)
98
+ end
99
+
100
+ def serialize_nested_object(key)
101
+ new_value = @values[key]
102
+ if new_value.is_a?(APIResource)
103
+ return {}
104
+ end
105
+
106
+ if @unsaved_values.include?(key)
107
+ # the object has been reassigned
108
+ # e.g. as object.key = {foo => bar}
109
+ update = new_value
110
+ new_keys = update.keys.map(&:to_sym)
111
+
112
+ # remove keys at the server, but not known locally
113
+ if @original_values.include?(key)
114
+ keys_to_unset = @original_values[key].keys - new_keys
115
+ keys_to_unset.each {|key| update[key] = ''}
116
+ end
117
+
118
+ update
119
+ else
120
+ # can be serialized normally
121
+ self.class.serialize_params(new_value)
122
+ end
123
+ end
124
+
125
+ def self.serialize_params(obj, original_value=nil)
126
+ case obj
127
+ when nil
128
+ ''
129
+ when ScribesendObject
130
+ unsaved_keys = obj.instance_variable_get(:@unsaved_values)
131
+ obj_values = obj.instance_variable_get(:@values)
132
+ update_hash = {}
133
+
134
+ unsaved_keys.each do |k|
135
+ update_hash[k] = serialize_params(obj_values[k])
136
+ end
137
+
138
+ obj_values.each do |k, v|
139
+ if v.is_a?(ScribesendObject) || v.is_a?(Hash)
140
+ update_hash[k] = obj.serialize_nested_object(k)
141
+ elsif v.is_a?(Array)
142
+ original_value = obj.instance_variable_get(:@original_values)[k]
143
+ if original_value && original_value.length > v.length
144
+ # url params provide no mechanism for deleting an item in an array,
145
+ # just overwriting the whole array or adding new items. So let's not
146
+ # allow deleting without a full overwrite until we have a solution.
147
+ raise ArgumentError.new(
148
+ "You cannot delete an item from an array, you must instead set a new array"
149
+ )
150
+ end
151
+ update_hash[k] = serialize_params(v, original_value)
152
+ end
153
+ end
154
+
155
+ update_hash
156
+ when Array
157
+ update_hash = {}
158
+ obj.each_with_index do |value, index|
159
+ update = serialize_params(value)
160
+ if update != {} && (!original_value || update != original_value[index])
161
+ update_hash[index] = update
162
+ end
163
+ end
164
+
165
+ if update_hash == {}
166
+ nil
167
+ else
168
+ update_hash
169
+ end
170
+ else
171
+ obj
172
+ end
173
+ end
174
+
175
+ protected
176
+
177
+ def metaclass
178
+ class << self; self; end
179
+ end
180
+
181
+ def remove_accessors(keys)
182
+ metaclass.instance_eval do
183
+ keys.each do |k|
184
+ next if @@immutable_values.include?(k)
185
+ k_eq = :"#{k}="
186
+ remove_method(k) if method_defined?(k)
187
+ remove_method(k_eq) if method_defined?(k_eq)
188
+ end
189
+ end
190
+ end
191
+
192
+ def add_accessors(keys)
193
+ metaclass.instance_eval do
194
+ keys.each do |k|
195
+ next if @@immutable_values.include?(k)
196
+ k_eq = :"#{k}="
197
+ define_method(k) { @values[k] }
198
+ define_method(k_eq) do |v|
199
+ if v == ""
200
+ raise ArgumentError.new(
201
+ "You cannot set #{k} to an empty string." \
202
+ "We interpret empty strings as nil in requests." \
203
+ "You may set #{self}.#{k} = nil to delete the property.")
204
+ end
205
+ @values[k] = v
206
+ @unsaved_values.add(k)
207
+ end
208
+ end
209
+ end
210
+ end
211
+
212
+ def method_missing(name, *args)
213
+ if name.to_s.end_with?('=')
214
+ attr = name.to_s[0...-1].to_sym
215
+ add_accessors([attr])
216
+ begin
217
+ mth = method(name)
218
+ rescue NameError
219
+ raise NoMethodError.new("Cannot set #{attr} on this object. HINT: you can't set: #{@@permanent_attributes.to_a.join(', ')}")
220
+ end
221
+ return mth.call(args[0])
222
+ else
223
+ return @values[name] if @values.has_key?(name)
224
+ end
225
+
226
+ begin
227
+ super
228
+ rescue NoMethodError => e
229
+ if @transient_values.include?(name)
230
+ raise NoMethodError.new(e.message + ". HINT: The '#{name}' attribute was set in the past, however. It was then wiped when refreshing the object with the result returned by Scribesend's API, probably as a result of a save(). The attributes currently available on this object are: #{@values.keys.join(', ')}")
231
+ else
232
+ raise
233
+ end
234
+ end
235
+ end
236
+
237
+ def respond_to_missing?(symbol, include_private = false)
238
+ @values && @values.has_key?(symbol) || super
239
+ end
240
+ end
241
+ end
@@ -0,0 +1,89 @@
1
+ module Scribesend
2
+ module Util
3
+ def self.objects_to_ids(h)
4
+ case h
5
+ when APIResource
6
+ h.id
7
+ when Hash
8
+ res = {}
9
+ h.each { |k, v| res[k] = objects_to_ids(v) unless v.nil? }
10
+ res
11
+ when Array
12
+ h.map { |v| objects_to_ids(v) }
13
+ else
14
+ h
15
+ end
16
+ end
17
+
18
+ def self.object_classes
19
+ @object_classes ||= {
20
+ 'list' => ListObject,
21
+
22
+ 'account' => Account,
23
+ 'entry' => Entry,
24
+ 'entry_line' => EntryLine
25
+ }
26
+ end
27
+
28
+ def self.convert_to_scribesend_object(resp, opts)
29
+ case resp
30
+ when Array
31
+ resp.map { |i| convert_to_scribesend_object(i, opts) }
32
+ when Hash
33
+ # Convert to a known object class, or ScribesendObject if not found
34
+ object_classes.fetch(resp[:object], ScribesendObject).construct_from(resp, opts)
35
+ else
36
+ resp
37
+ end
38
+ end
39
+
40
+ def self.symbolize_names(object)
41
+ case object
42
+ when Hash
43
+ new_hash = {}
44
+ object.each do |key, value|
45
+ key = (key.to_sym rescue key) || key
46
+ new_hash[key] = symbolize_names(value)
47
+ end
48
+ new_hash
49
+ when Array
50
+ object.map { |value| symbolize_names(value) }
51
+ else
52
+ object
53
+ end
54
+ end
55
+
56
+ def self.url_encode(key)
57
+ URI.escape(key.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
58
+ end
59
+
60
+ def self.flatten_params(params, parent_key=nil)
61
+ result = []
62
+ params.each do |key, value|
63
+ calculated_key = parent_key ? "#{parent_key}[#{url_encode(key)}]" : url_encode(key)
64
+ if value.is_a?(Hash)
65
+ result += flatten_params(value, calculated_key)
66
+ elsif value.is_a?(Array)
67
+ result += flatten_params_array(value, calculated_key)
68
+ else
69
+ result << [calculated_key, value]
70
+ end
71
+ end
72
+ result
73
+ end
74
+
75
+ def self.flatten_params_array(value, calculated_key)
76
+ result = []
77
+ value.each do |elem|
78
+ if elem.is_a?(Hash)
79
+ result += flatten_params(elem, calculated_key)
80
+ elsif elem.is_a?(Array)
81
+ result += flatten_params_array(elem, calculated_key)
82
+ else
83
+ result << ["#{calculated_key}[]", elem]
84
+ end
85
+ end
86
+ result
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,3 @@
1
+ module Scribesend
2
+ VERSION = File.open(File.expand_path("../../../VERSION", __FILE__)).read().strip
3
+ end
data/lib/scribesend.rb ADDED
@@ -0,0 +1,173 @@
1
+ # Scribesend Ruby bindings (https://scribesend.com/docs)
2
+
3
+ # Third-party libraries
4
+ require 'cgi'
5
+ require 'set'
6
+ require 'rest-client'
7
+ require 'json'
8
+
9
+ # API Resources
10
+ require 'scribesend/version'
11
+ require 'scribesend/util'
12
+ require 'scribesend/scribesend_object'
13
+ require 'scribesend/api_resource'
14
+ require 'scribesend/list_object'
15
+ require 'scribesend/account'
16
+ require 'scribesend/entry'
17
+ require 'scribesend/entry_line'
18
+
19
+ # Errors
20
+ require 'scribesend/error'
21
+
22
+ module Scribesend
23
+ @api_key = nil
24
+ @api_base = "https://api.scribesend.com/v0"
25
+
26
+ def self.api_url(url='', api_base_url=nil)
27
+ (api_base_url || @api_base) + url
28
+ end
29
+
30
+ def self.api_key=(api_key)
31
+ @api_key = api_key
32
+ end
33
+
34
+ def self.api_key
35
+ @api_key
36
+ end
37
+
38
+ def self.api_base
39
+ @api_base
40
+ end
41
+
42
+ def self.request(method, url, api_key, params={}, headers={}, api_base_url=nil)
43
+ api_base_url = api_base_url || @api_base
44
+
45
+ unless api_key ||= @api_key
46
+ raise Error.new("No API key provided. " \
47
+ "Set your API key using 'Scribesend.api_key = <API-KEY>'.")
48
+ end
49
+
50
+ params = Util.objects_to_ids(params)
51
+ url = api_url(url, api_base_url)
52
+
53
+ case method.to_s.downcase.to_sym
54
+ when :get, :head, :delete
55
+ # Make params into GET parameters
56
+ url += "#{URI.parse(url).query ? '&' : '?'}#{uri_encode(params)}" if params && params.any?
57
+ payload = nil
58
+ else
59
+ if headers[:content_type] && (headers[:content_type] == "multipart/form-data" || headers[:content_type] == "application/json")
60
+ payload = params
61
+ else
62
+ payload = uri_encode(params)
63
+ end
64
+ end
65
+
66
+ if headers[:content_type] && headers[:content_type] == "application/json"
67
+ content_type = headers[:content_type]
68
+ else
69
+ content_type = "application/x-www-form-urlencoded"
70
+ end
71
+
72
+ headers = {
73
+ :user_agent => "Scribesend/v0 RubyClient/#{Scribesend::VERSION}",
74
+ :authorization => "Bearer #{api_key}",
75
+ :content_type => content_type
76
+ }.update(headers)
77
+
78
+ request_opts = {
79
+ :method => method,
80
+ :headers => headers,
81
+ :url => url,
82
+ :payload => payload,
83
+ :verify_ssl => false,
84
+ :open_timeout => 30,
85
+ :timeout => 80,
86
+ }
87
+
88
+ begin
89
+ response = execute_request(request_opts)
90
+ rescue SocketError => e
91
+ handle_restclient_error(e, api_base_url)
92
+ rescue NoMethodError => e
93
+ # Work around RestClient bug
94
+ if e.message =~ /\WRequestFailed\W/
95
+ e = Error.new('Unexpected HTTP response code')
96
+ handle_restclient_error(e, api_base_url)
97
+ else
98
+ raise
99
+ end
100
+ rescue RestClient::ExceptionWithResponse => e
101
+ if rcode = e.http_code and rbody = e.http_body
102
+ handle_api_error(rcode, rbody)
103
+ else
104
+ handle_restclient_error(e, api_base_url)
105
+ end
106
+ rescue RestClient::Exception, Errno::ECONNREFUSED => e
107
+ handle_restclient_error(e, api_base_url)
108
+ end
109
+
110
+ [parse(response), api_key]
111
+ end
112
+
113
+ protected
114
+
115
+ def self.uri_encode(params)
116
+ Util.flatten_params(params).
117
+ map { |k,v| "#{k}=#{Util.url_encode(v)}" }.join('&')
118
+ end
119
+
120
+ def self.execute_request(opts)
121
+ RestClient::Request.execute(opts)
122
+ end
123
+
124
+ def self.parse(response)
125
+ begin
126
+ response = JSON.parse(response.body)
127
+ rescue JSON::ParserError
128
+ raise Error.new('Unexpected API response', response.code, response.body)
129
+ end
130
+
131
+ Util.symbolize_names(response)
132
+ end
133
+
134
+ def self.handle_api_error(rcode, rbody)
135
+ begin
136
+ error_obj = JSON.parse(rbody)
137
+ error_obj = Util.symbolize_names(error_obj)
138
+ error = error_obj[:error][:message] or raise Error.new # escape from parsing
139
+ rescue JSON::ParserError, Error
140
+ raise Error.new("Invalid response object from API: #{rbody.inspect} " +
141
+ "(HTTP response code was #{rcode})", rcode, rbody)
142
+ end
143
+
144
+ raise Error.new(error, rcode, rbody, error_obj)
145
+ end
146
+
147
+ def self.handle_restclient_error(e, api_base_url=nil)
148
+ api_base_url = @api_base unless api_base_url
149
+ connection_message = "Please check your internet connection and try again. " \
150
+ "If this problem persists, let us know at team@scribesend.com."
151
+
152
+ case e
153
+ when RestClient::RequestTimeout
154
+ message = "Could not connect to Scribesend (#{api_base_url}). #{connection_message}"
155
+
156
+ when RestClient::ServerBrokeConnection
157
+ message = "The connection to the server (#{api_base_url}) broke before the " \
158
+ "request completed. #{connection_message}"
159
+
160
+ when SocketError
161
+ message = "Unexpected error communicating when trying to connect to Scribesend. " \
162
+ "You may be seeing this message because your DNS is not working. " \
163
+ "To check, try running 'host scribesend.com' from the command line."
164
+
165
+ else
166
+ message = "Unexpected error communicating with Scribesend. " \
167
+ "If this problem persists, let us know at team@scribesend.com."
168
+
169
+ end
170
+
171
+ raise Error.new(message + "\n\n(Network error: #{e.message})")
172
+ end
173
+ end
@@ -0,0 +1,24 @@
1
+ $:.unshift(File.join(File.dirname(__FILE__), 'lib'))
2
+
3
+ require 'scribesend/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'scribesend'
7
+ spec.version = Scribesend::VERSION
8
+ spec.date = Time.now.strftime("%Y-%m-%d")
9
+ spec.summary = 'Ruby bindings for Scribesend'
10
+ spec.description = 'Scribesend builds APIs for accounting and business management: https://scribesend.com'
11
+ spec.authors = ['Frank Wu']
12
+ spec.email = 'frank@scribesend.com'
13
+ spec.homepage = 'https://www.scribesend.com/docs'
14
+ spec.license = 'MIT'
15
+
16
+ spec.add_dependency 'rest-client', '~> 1.4'
17
+ spec.add_dependency 'mime-types', '>= 1.25', '< 3.0'
18
+ spec.add_dependency 'json', '~> 1.8.1'
19
+
20
+ spec.files = `git ls-files`.split("\n")
21
+ spec.test_files = `git ls-files -- test/*`.split("\n")
22
+ spec.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
23
+ spec.require_paths = ['lib']
24
+ end
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: scribesend
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.4
5
+ platform: ruby
6
+ authors:
7
+ - Frank Wu
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-05-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rest-client
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: mime-types
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '1.25'
34
+ - - "<"
35
+ - !ruby/object:Gem::Version
36
+ version: '3.0'
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '1.25'
44
+ - - "<"
45
+ - !ruby/object:Gem::Version
46
+ version: '3.0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: json
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: 1.8.1
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: 1.8.1
61
+ description: 'Scribesend builds APIs for accounting and business management: https://scribesend.com'
62
+ email: frank@scribesend.com
63
+ executables:
64
+ - scribesend-console
65
+ extensions: []
66
+ extra_rdoc_files: []
67
+ files:
68
+ - ".gitignore"
69
+ - CHANGELOG
70
+ - Gemfile
71
+ - LICENSE
72
+ - README.markdown
73
+ - VERSION
74
+ - bin/scribesend-console
75
+ - lib/scribesend.rb
76
+ - lib/scribesend/account.rb
77
+ - lib/scribesend/api_resource.rb
78
+ - lib/scribesend/entry.rb
79
+ - lib/scribesend/entry_line.rb
80
+ - lib/scribesend/error.rb
81
+ - lib/scribesend/list_object.rb
82
+ - lib/scribesend/scribesend_object.rb
83
+ - lib/scribesend/util.rb
84
+ - lib/scribesend/version.rb
85
+ - scribesend.gemspec
86
+ homepage: https://www.scribesend.com/docs
87
+ licenses:
88
+ - MIT
89
+ metadata: {}
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ requirements: []
105
+ rubyforge_project:
106
+ rubygems_version: 2.4.5
107
+ signing_key:
108
+ specification_version: 4
109
+ summary: Ruby bindings for Scribesend
110
+ test_files: []