scribesend 0.0.4

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.
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: []