turingstudio-freshbooks-rb 3.0.1 → 3.0.2
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.
- data/README.rdoc +1 -1
- data/lib/freshbooks.rb +4 -0
- data/lib/freshbooks/base.rb +109 -183
- data/lib/freshbooks/category.rb +3 -3
- data/lib/freshbooks/client.rb +2 -2
- data/lib/freshbooks/estimate.rb +19 -24
- data/lib/freshbooks/expense.rb +2 -2
- data/lib/freshbooks/invoice.rb +2 -7
- data/lib/freshbooks/item.rb +1 -1
- data/lib/freshbooks/line.rb +1 -1
- data/lib/freshbooks/payment.rb +2 -2
- data/lib/freshbooks/project.rb +2 -7
- data/lib/freshbooks/recurring.rb +5 -9
- data/lib/freshbooks/staff.rb +5 -5
- data/lib/freshbooks/task.rb +2 -2
- data/lib/freshbooks/time_entry.rb +2 -2
- data/lib/freshbooks/version.rb +1 -1
- data/spec/lib/freshbooks/base_spec.rb +59 -26
- data/spec/lib/freshbooks/time_entry_spec.rb +2 -2
- data/spec/spec_helper.rb +1 -1
- metadata +5 -6
data/README.rdoc
CHANGED
@@ -10,7 +10,7 @@ A Ruby interface to the FreshBooks API. It exposes easy-to-use classes and metho
|
|
10
10
|
|
11
11
|
Initialization:
|
12
12
|
|
13
|
-
FreshBooks::Base.
|
13
|
+
FreshBooks::Base.establish_connection!('sample.freshbooks.com', 'mytoken')
|
14
14
|
|
15
15
|
Updating a client name:
|
16
16
|
|
data/lib/freshbooks.rb
CHANGED
@@ -24,6 +24,7 @@ require 'rexml/document'
|
|
24
24
|
require 'activesupport'
|
25
25
|
|
26
26
|
require 'freshbooks/version'
|
27
|
+
require 'freshbooks/connection'
|
27
28
|
require 'freshbooks/response'
|
28
29
|
require 'freshbooks/base'
|
29
30
|
require 'freshbooks/category'
|
@@ -33,9 +34,12 @@ require 'freshbooks/expense'
|
|
33
34
|
require 'freshbooks/invoice'
|
34
35
|
require 'freshbooks/item'
|
35
36
|
require 'freshbooks/line'
|
37
|
+
require 'freshbooks/links'
|
38
|
+
require 'freshbooks/list_proxy'
|
36
39
|
require 'freshbooks/payment'
|
37
40
|
require 'freshbooks/project'
|
38
41
|
require 'freshbooks/recurring'
|
39
42
|
require 'freshbooks/staff'
|
40
43
|
require 'freshbooks/task'
|
41
44
|
require 'freshbooks/time_entry'
|
45
|
+
require 'freshbooks/xml_serializer'
|
data/lib/freshbooks/base.rb
CHANGED
@@ -4,140 +4,82 @@ module FreshBooks
|
|
4
4
|
class AuthenticationError < Exception; end;
|
5
5
|
class UnknownSystemError < Exception; end;
|
6
6
|
class InvalidParameterError < Exception; end;
|
7
|
+
class InvalidAccountUrlError < Exception; end;
|
7
8
|
|
8
|
-
attr_accessor :resp
|
9
|
-
|
10
9
|
def initialize(data = {})
|
11
|
-
@old_values = {}
|
12
10
|
@changed_fields = []
|
11
|
+
self.class.attributes.each do |name, options|
|
12
|
+
send("#{name}=", []) if options[:type] == :array
|
13
|
+
end
|
13
14
|
data.each do |name, value|
|
14
15
|
send("#{name}=", value) if self.class.attributes[name]
|
15
16
|
end
|
17
|
+
#@changed_fields = [] # reset changed fields
|
16
18
|
end
|
17
|
-
|
18
|
-
# Convert an instance of this class to an XML element
|
19
|
+
|
19
20
|
def to_xml(elem_name = nil)
|
20
21
|
root = REXML::Element.new(node_name)
|
21
|
-
|
22
22
|
self.class.attributes.each do |name, options|
|
23
|
-
next if options[:read_only] || !send("#{name}_changed?")
|
24
|
-
|
25
23
|
value = send(name)
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
node.add_element(item.to_xml)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
elsif !value.nil?
|
37
|
-
root.add_element(name.to_s).text = value
|
24
|
+
next if options[:read_only] || !send("#{name}_changed?") || value.nil?
|
25
|
+
|
26
|
+
if options[:to_xml]
|
27
|
+
send(options[:to_xml], root)
|
28
|
+
else
|
29
|
+
element = XmlSerializer.to_node(name.to_s, value, options[:type])
|
30
|
+
root.add_element(element) if element != nil
|
38
31
|
end
|
39
32
|
end
|
40
|
-
|
41
33
|
root
|
42
34
|
end
|
43
35
|
|
44
|
-
def create
|
45
|
-
self.class.ensure_allowed! :create
|
46
|
-
resp = Base.call_api("#{node_name}.create", node_name => self)
|
47
|
-
if resp.success?
|
48
|
-
self.send("#{node_name}_id=", resp.elements[1].text.to_i)
|
49
|
-
end
|
50
|
-
resp.success? ? self.send("#{node_name}_id") : nil
|
51
|
-
end
|
52
|
-
|
53
|
-
def update
|
54
|
-
self.class.ensure_allowed! :update
|
55
|
-
resp = Base.call_api("#{node_name}.update", node_name => self)
|
56
|
-
resp.success?
|
57
|
-
end
|
58
|
-
|
59
|
-
def delete
|
60
|
-
self.class.delete(self.send("#{node_name}_id"))
|
61
|
-
end
|
62
|
-
|
63
|
-
def send_by_email
|
64
|
-
self.class.send_by_email(self.send("#{node_name}_id"))
|
65
|
-
end
|
66
|
-
|
67
|
-
def send_by_snail_mail
|
68
|
-
self.class.send_by_snail_mail(self.send("#{node_name}_id"))
|
69
|
-
end
|
70
|
-
|
71
|
-
def node_name
|
72
|
-
self.class.node_name
|
73
|
-
end
|
74
|
-
|
75
36
|
class << self
|
76
|
-
|
77
|
-
@@account_url = account_url
|
78
|
-
@@auth_token = auth_token
|
79
|
-
@@request_headers = request_headers
|
80
|
-
@@response = nil
|
81
|
-
end
|
37
|
+
attr_reader :connection
|
82
38
|
|
83
|
-
def
|
84
|
-
|
85
|
-
resp = Base.call_api("#{node_name}.list", options)
|
86
|
-
return nil unless resp.success?
|
87
|
-
|
88
|
-
elems = resp.elements[1].elements
|
89
|
-
elems.map { |elem| self.new_from_xml(elem) }
|
39
|
+
def establish_connection!(account_url, auth_token, request_headers = {})
|
40
|
+
@connection = Connection.new(account_url, auth_token, request_headers)
|
90
41
|
end
|
91
42
|
|
92
|
-
def
|
93
|
-
|
94
|
-
|
95
|
-
|
43
|
+
def new_from_xml(xml_root)
|
44
|
+
object = self.new
|
45
|
+
attributes.each do |name, options|
|
46
|
+
node = xml_root.elements[name.to_s]
|
47
|
+
next if node.nil?
|
48
|
+
|
49
|
+
value = FreshBooks::XmlSerializer.to_value(node, options[:type])
|
50
|
+
object.send("#{name}=", value)
|
51
|
+
end
|
52
|
+
object
|
96
53
|
end
|
97
54
|
|
98
|
-
def delete(id)
|
99
|
-
ensure_allowed! :delete
|
100
|
-
resp = Base.call_api("#{node_name}.delete", "#{node_name}_id" => id)
|
101
|
-
resp.success?
|
102
|
-
end
|
103
|
-
|
104
|
-
def send_by_email(id)
|
105
|
-
ensure_allowed! :send_by_email
|
106
|
-
resp = Base.call_api("#{node_name}.sendByEmail", "#{node_name}_id" => id)
|
107
|
-
resp.success?
|
108
|
-
end
|
109
|
-
|
110
|
-
def send_by_snail_mail(id)
|
111
|
-
ensure_allowed! :send_by_snail_mail
|
112
|
-
resp = Base.call_api("#{node_name}.sendBySnailMail", "#{node_name}_id" => id)
|
113
|
-
resp.success?
|
114
|
-
end
|
115
|
-
|
116
55
|
def node_name
|
117
56
|
self.to_s.split('::').last.underscore
|
118
57
|
end
|
119
58
|
|
120
59
|
#
|
121
|
-
# Defines structure attribute. Generates attr_accessor and #{name}_changed? method
|
60
|
+
# Defines structure attribute. Generates attr_accessor and #{name}_changed? method.
|
61
|
+
# Options are:
|
62
|
+
# :read_only - specifies that attribute is read-only and attr_writer is protected
|
63
|
+
# :to_xml - overrides xml generation for this attribute
|
122
64
|
#
|
123
|
-
#
|
124
|
-
#
|
125
|
-
#
|
65
|
+
# class Test < Base
|
66
|
+
# attribute :test_id, :integer
|
67
|
+
# end
|
126
68
|
#
|
127
|
-
#
|
128
|
-
#
|
129
|
-
#
|
130
|
-
#
|
131
|
-
#
|
69
|
+
# test = Test.new
|
70
|
+
# test.test_id_changed? # false
|
71
|
+
# test.test_id = 1
|
72
|
+
# test.test_id_changed? # true
|
73
|
+
# test.test_id # 1
|
132
74
|
#
|
133
75
|
def attribute(name, type, options = {})
|
134
76
|
self.attributes[name] = options.update(:type => type)
|
135
77
|
|
136
78
|
define_method("#{name}=") do |value|
|
137
79
|
@changed_fields << name
|
138
|
-
@old_values[name] = send(name) if @old_values[name].nil?
|
139
80
|
instance_variable_set("@#{name}", value)
|
140
81
|
end
|
82
|
+
self.send(:protected, "#{name}=") if options[:read_only]
|
141
83
|
|
142
84
|
define_method("#{name}_changed?") do
|
143
85
|
@changed_fields.include?(name)
|
@@ -179,108 +121,92 @@ module FreshBooks
|
|
179
121
|
# Defines allowed API methods.
|
180
122
|
#
|
181
123
|
# class Test < Base
|
182
|
-
#
|
124
|
+
# actions :list
|
183
125
|
# end
|
184
126
|
#
|
185
127
|
# Test.list # [...]
|
186
128
|
# Test.get(1) # "Error: Method get is not allowed for Test"
|
187
129
|
#
|
188
|
-
def
|
189
|
-
|
190
|
-
end
|
191
|
-
|
192
|
-
def methods
|
193
|
-
@methods ||= []
|
194
|
-
end
|
195
|
-
|
196
|
-
def ensure_allowed!(name)
|
197
|
-
unless methods.include?(name)
|
198
|
-
raise "Error: Method #{name} is not allowed for #{self}"
|
199
|
-
end
|
200
|
-
end
|
201
|
-
|
202
|
-
# Create a new instance of this class from an XML element
|
203
|
-
def new_from_xml(xml_root)
|
204
|
-
object = self.new
|
205
|
-
attributes.each do |name, options|
|
206
|
-
node = xml_root.elements[name.to_s]
|
207
|
-
next if node.nil?
|
130
|
+
def define_class_method(name, &block)
|
131
|
+
self.class.send(:define_method, name, &block)
|
132
|
+
end
|
208
133
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
value = node.text.to_f
|
214
|
-
when :array
|
215
|
-
value = node.elements.map do |item|
|
216
|
-
FreshBooks::const_get(item.name.classify)::new_from_xml(item)
|
217
|
-
end
|
218
|
-
else
|
219
|
-
value = node.text.to_s
|
220
|
-
end
|
221
|
-
|
222
|
-
object.send("#{name}=", value)
|
223
|
-
end
|
224
|
-
object
|
225
|
-
end
|
226
|
-
|
227
|
-
def call_api(method, elems = [])
|
228
|
-
doc = REXML::Document.new '<?xml version="1.0" encoding="UTF-8"?>'
|
229
|
-
request = doc.add_element 'request'
|
230
|
-
request.attributes['method'] = method
|
134
|
+
def actions(*args)
|
135
|
+
args.each do |action|
|
136
|
+
method = action.to_s
|
137
|
+
api_method = method.camelize(:lower)
|
231
138
|
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
139
|
+
case method
|
140
|
+
when 'list'
|
141
|
+
define_class_method(method) do |*args|
|
142
|
+
api_list_action(api_method, *args)
|
143
|
+
end
|
144
|
+
when 'get'
|
145
|
+
define_class_method(method) do |id|
|
146
|
+
api_get_action(api_method, id)
|
147
|
+
end
|
148
|
+
when 'create'
|
149
|
+
define_method(method) do
|
150
|
+
api_create_action(api_method)
|
151
|
+
end
|
152
|
+
when 'update'
|
153
|
+
define_method(method) do
|
154
|
+
api_update_action(api_method)
|
155
|
+
end
|
156
|
+
when 'delete'
|
157
|
+
define_class_method(method) do |id|
|
158
|
+
api_delete_action(api_method, id)
|
159
|
+
end
|
160
|
+
define_method(method) do
|
161
|
+
self.class.api_delete_action(api_method, self.send("#{node_name}_id"))
|
162
|
+
end
|
236
163
|
else
|
237
|
-
|
164
|
+
define_class_method(method) do |id|
|
165
|
+
api_action(api_method, id)
|
166
|
+
end
|
167
|
+
define_method(method) do
|
168
|
+
self.class.api_action(api_method, self.send("#{node_name}_id"))
|
169
|
+
end
|
238
170
|
end
|
239
171
|
end
|
240
|
-
|
241
|
-
result = self.post(request.to_s)
|
242
172
|
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
# Failure
|
247
|
-
#
|
248
|
-
if @@response.fail?
|
249
|
-
error_msg = @@response.error_msg
|
250
|
-
|
251
|
-
raise InternalError.new, error_msg if error_msg =~ /not formatted correctly/
|
252
|
-
raise AuthenticationError.new, error_msg if error_msg =~ /[Aa]uthentication failed/
|
253
|
-
raise UnknownSystemError.new, error_msg if error_msg =~ /does not exist/
|
254
|
-
raise InvalidParameterError.new, error_msg if error_msg =~ /Invalid parameter: (.*)/
|
255
|
-
|
256
|
-
# Raise an exception for unexpected errors
|
257
|
-
raise error_msg
|
173
|
+
def api_list_action(method, options = {})
|
174
|
+
resp = Base.connection.call_api("#{node_name}.#{method}", options)
|
175
|
+
resp.success? ? ListProxy.new_from_xml(self, resp) : nil
|
258
176
|
end
|
259
177
|
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
def response
|
264
|
-
@@response
|
265
|
-
end
|
266
|
-
|
267
|
-
def post(body)
|
268
|
-
connection = Net::HTTP.new(@@account_url, 443)
|
269
|
-
connection.use_ssl = true
|
270
|
-
connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
271
|
-
|
272
|
-
request = Net::HTTP::Post.new(FreshBooks::SERVICE_URL)
|
273
|
-
request.basic_auth @@auth_token, 'X'
|
274
|
-
request.body = body
|
275
|
-
request.content_type = 'application/xml'
|
276
|
-
@@request_headers.each_pair do |name, value|
|
277
|
-
request[name.to_s] = value
|
178
|
+
def api_get_action(method, id)
|
179
|
+
resp = Base.connection.call_api("#{node_name}.#{method}", "#{node_name}_id" => id)
|
180
|
+
resp.success? ? self.new_from_xml(resp.elements[1]) : nil
|
278
181
|
end
|
279
182
|
|
280
|
-
|
183
|
+
def api_delete_action(method, id)
|
184
|
+
resp = Base.connection.call_api("#{node_name}.#{method}", "#{node_name}_id" => id)
|
185
|
+
resp.success?
|
186
|
+
end
|
281
187
|
|
282
|
-
|
188
|
+
def api_action(method, id)
|
189
|
+
resp = Base.connection.call_api("#{node_name}.#{method}", "#{node_name}_id" => id)
|
190
|
+
resp.success?
|
191
|
+
end
|
283
192
|
end
|
284
193
|
end
|
194
|
+
|
195
|
+
protected
|
196
|
+
|
197
|
+
def node_name
|
198
|
+
self.class.node_name
|
199
|
+
end
|
200
|
+
|
201
|
+
def api_create_action(method)
|
202
|
+
resp = Base.connection.call_api("#{node_name}.#{method}", node_name => self)
|
203
|
+
self.send("#{node_name}_id=", resp.elements[1].text.to_i) if resp.success?
|
204
|
+
resp.success?
|
205
|
+
end
|
206
|
+
|
207
|
+
def api_update_action(method)
|
208
|
+
resp = Base.connection.call_api("#{node_name}.#{method}", node_name => self)
|
209
|
+
resp.success?
|
210
|
+
end
|
285
211
|
end
|
286
|
-
end
|
212
|
+
end
|
data/lib/freshbooks/category.rb
CHANGED
@@ -2,10 +2,10 @@ module FreshBooks
|
|
2
2
|
class Category < Base
|
3
3
|
attribute :category_id, :integer
|
4
4
|
attribute :name, :string
|
5
|
-
attribute :tax1, :
|
6
|
-
attribute :tax2, :
|
5
|
+
attribute :tax1, :float
|
6
|
+
attribute :tax2, :float
|
7
7
|
|
8
8
|
has_many :expenses
|
9
|
-
|
9
|
+
actions :list, :get, :create, :update, :delete
|
10
10
|
end
|
11
11
|
end
|
data/lib/freshbooks/client.rb
CHANGED
@@ -24,9 +24,9 @@ module FreshBooks
|
|
24
24
|
attribute :s_state, :string
|
25
25
|
attribute :s_country, :string
|
26
26
|
attribute :s_code, :string
|
27
|
-
attribute :
|
27
|
+
attribute :links, :object, :read_only => true
|
28
28
|
|
29
29
|
has_many :expenses, :estimates, :invoices, :payments, :projects, :recurrings
|
30
|
-
|
30
|
+
actions :list, :get, :create, :update, :delete
|
31
31
|
end
|
32
32
|
end
|
data/lib/freshbooks/estimate.rb
CHANGED
@@ -1,30 +1,25 @@
|
|
1
1
|
module FreshBooks
|
2
2
|
class Estimate < Base
|
3
|
-
attribute :estimate_id,
|
4
|
-
attribute :client_id,
|
5
|
-
attribute :status,
|
6
|
-
attribute :date,
|
7
|
-
attribute :po_number,
|
8
|
-
attribute :discount,
|
9
|
-
attribute :notes,
|
10
|
-
attribute :terms,
|
11
|
-
attribute :first_name,
|
12
|
-
attribute :last_name,
|
13
|
-
attribute :organization,
|
14
|
-
attribute :p_street1,
|
15
|
-
attribute :p_street2,
|
16
|
-
attribute :p_city,
|
17
|
-
attribute :p_state,
|
18
|
-
attribute :p_country,
|
19
|
-
attribute :p_code,
|
20
|
-
attribute :lines,
|
3
|
+
attribute :estimate_id, :integer
|
4
|
+
attribute :client_id, :integer
|
5
|
+
attribute :status, :string
|
6
|
+
attribute :date, :date
|
7
|
+
attribute :po_number, :integer
|
8
|
+
attribute :discount, :float
|
9
|
+
attribute :notes, :string
|
10
|
+
attribute :terms, :string
|
11
|
+
attribute :first_name, :string
|
12
|
+
attribute :last_name, :string
|
13
|
+
attribute :organization, :string
|
14
|
+
attribute :p_street1, :string
|
15
|
+
attribute :p_street2, :string
|
16
|
+
attribute :p_city, :string
|
17
|
+
attribute :p_state, :string
|
18
|
+
attribute :p_country, :string
|
19
|
+
attribute :p_code, :string
|
20
|
+
attribute :lines, :array
|
21
21
|
|
22
22
|
belongs_to :client
|
23
|
-
|
24
|
-
|
25
|
-
def initialize(data = {})
|
26
|
-
super
|
27
|
-
self.lines ||= []
|
28
|
-
end
|
23
|
+
actions :list, :get, :create, :update, :delete, :send_by_email
|
29
24
|
end
|
30
25
|
end
|
data/lib/freshbooks/expense.rb
CHANGED
@@ -6,11 +6,11 @@ module FreshBooks
|
|
6
6
|
attribute :project_id, :integer
|
7
7
|
attribute :client_id, :integer
|
8
8
|
attribute :amount, :float
|
9
|
-
attribute :date, :
|
9
|
+
attribute :date, :date
|
10
10
|
attribute :notes, :string
|
11
11
|
attribute :status, :string
|
12
12
|
|
13
13
|
belongs_to :staff, :category, :project, :client
|
14
|
-
|
14
|
+
actions :list, :get, :create, :update, :delete, :send_by_email
|
15
15
|
end
|
16
16
|
end
|
data/lib/freshbooks/invoice.rb
CHANGED
@@ -3,7 +3,7 @@ module FreshBooks
|
|
3
3
|
attribute :invoice_id, :integer
|
4
4
|
attribute :client_id, :integer
|
5
5
|
attribute :number, :string
|
6
|
-
attribute :date, :
|
6
|
+
attribute :date, :date
|
7
7
|
attribute :po_number, :integer
|
8
8
|
attribute :terms, :string
|
9
9
|
attribute :first_name, :string
|
@@ -24,11 +24,6 @@ module FreshBooks
|
|
24
24
|
|
25
25
|
belongs_to :client
|
26
26
|
has_many :payments
|
27
|
-
|
28
|
-
|
29
|
-
def initialize(data = {})
|
30
|
-
super
|
31
|
-
self.lines ||= []
|
32
|
-
end
|
27
|
+
actions :list, :get, :create, :update, :delete, :send_by_email, :send_by_snail_mail
|
33
28
|
end
|
34
29
|
end
|
data/lib/freshbooks/item.rb
CHANGED
data/lib/freshbooks/line.rb
CHANGED
data/lib/freshbooks/payment.rb
CHANGED
@@ -11,12 +11,12 @@ module FreshBooks
|
|
11
11
|
attribute :payment_id, :integer
|
12
12
|
attribute :client_id, :integer
|
13
13
|
attribute :invoice_id, :integer
|
14
|
-
attribute :date, :
|
14
|
+
attribute :date, :date
|
15
15
|
attribute :amount, :float
|
16
16
|
attribute :type, :string
|
17
17
|
attribute :notes, :string
|
18
18
|
|
19
19
|
belongs_to :client, :invoice
|
20
|
-
|
20
|
+
actions :list, :get, :create, :update
|
21
21
|
end
|
22
22
|
end
|
data/lib/freshbooks/project.rb
CHANGED
@@ -15,16 +15,11 @@ module FreshBooks
|
|
15
15
|
attribute :bill_method, :string
|
16
16
|
attribute :rate, :float
|
17
17
|
attribute :description, :string
|
18
|
-
attribute :tasks, :array
|
18
|
+
attribute :tasks, :array, :to_xml => :tasks_to_xml
|
19
19
|
|
20
20
|
belongs_to :client
|
21
21
|
has_many :expenses
|
22
|
-
|
23
|
-
|
24
|
-
def initialize(data = {})
|
25
|
-
super
|
26
|
-
self.tasks ||= []
|
27
|
-
end
|
22
|
+
actions :list, :get, :create, :update, :delete
|
28
23
|
|
29
24
|
protected
|
30
25
|
|
data/lib/freshbooks/recurring.rb
CHANGED
@@ -18,7 +18,7 @@ module FreshBooks
|
|
18
18
|
class Recurring < Base
|
19
19
|
attribute :recurring_id, :integer
|
20
20
|
attribute :client_id, :integer
|
21
|
-
attribute :date, :
|
21
|
+
attribute :date, :date
|
22
22
|
attribute :po_number, :integer
|
23
23
|
attribute :terms, :string
|
24
24
|
attribute :first_name, :string
|
@@ -37,15 +37,11 @@ module FreshBooks
|
|
37
37
|
attribute :notes, :string
|
38
38
|
attribute :occurrences, :integer
|
39
39
|
attribute :frequency, :string
|
40
|
-
attribute :
|
41
|
-
attribute :
|
40
|
+
attribute :stopped, :boolean
|
41
|
+
attribute :send_email, :boolean
|
42
|
+
attribute :send_snail_mail, :boolean
|
42
43
|
|
43
44
|
belongs_to :client
|
44
|
-
|
45
|
-
|
46
|
-
def initialize(data = {})
|
47
|
-
super
|
48
|
-
self.lines ||= []
|
49
|
-
end
|
45
|
+
actions :list, :get, :create, :update, :delete
|
50
46
|
end
|
51
47
|
end
|
data/lib/freshbooks/staff.rb
CHANGED
@@ -7,10 +7,10 @@ module FreshBooks
|
|
7
7
|
attribute :email, :string
|
8
8
|
attribute :business_phone, :string
|
9
9
|
attribute :mobile_phone, :string
|
10
|
-
attribute :rate, :
|
11
|
-
attribute :last_login, :
|
12
|
-
attribute :number_of_logins,:
|
13
|
-
attribute :signup_date, :
|
10
|
+
attribute :rate, :float
|
11
|
+
attribute :last_login, :date_time
|
12
|
+
attribute :number_of_logins,:integer
|
13
|
+
attribute :signup_date, :date_time
|
14
14
|
attribute :street1, :string
|
15
15
|
attribute :street2, :string
|
16
16
|
attribute :city, :string
|
@@ -19,6 +19,6 @@ module FreshBooks
|
|
19
19
|
attribute :code, :string
|
20
20
|
|
21
21
|
has_many :expenses
|
22
|
-
|
22
|
+
actions :list, :get
|
23
23
|
end
|
24
24
|
end
|
data/lib/freshbooks/task.rb
CHANGED
@@ -2,10 +2,10 @@ module FreshBooks
|
|
2
2
|
class Task < Base
|
3
3
|
attribute :task_id, :integer
|
4
4
|
attribute :name, :string
|
5
|
-
attribute :billable, :
|
5
|
+
attribute :billable, :boolean
|
6
6
|
attribute :rate, :float
|
7
7
|
attribute :description, :string
|
8
8
|
|
9
|
-
|
9
|
+
actions :list, :get, :create, :update, :delete
|
10
10
|
end
|
11
11
|
end
|
@@ -5,9 +5,9 @@ module FreshBooks
|
|
5
5
|
attribute :task_id, :integer
|
6
6
|
attribute :hours, :float
|
7
7
|
attribute :notes, :string
|
8
|
-
attribute :date, :
|
8
|
+
attribute :date, :date
|
9
9
|
|
10
10
|
belongs_to :project, :task
|
11
|
-
|
11
|
+
actions :list, :get, :create, :update, :delete
|
12
12
|
end
|
13
13
|
end
|
data/lib/freshbooks/version.rb
CHANGED
@@ -18,7 +18,7 @@ describe FreshBooks::Base do
|
|
18
18
|
|
19
19
|
class Test < FreshBooks::Base
|
20
20
|
attribute :test_id, :integer
|
21
|
-
|
21
|
+
actions :list, :get, :create, :update, :delete, :send_by_email, :send_by_snail_mail
|
22
22
|
end
|
23
23
|
|
24
24
|
class TestWithoutMethods < FreshBooks::Base
|
@@ -116,7 +116,7 @@ describe FreshBooks::Base do
|
|
116
116
|
it "should not include unchanged attributes in xml" do
|
117
117
|
@xml.to_s.should_not =~ Regexp.new(Regexp.escape('<amount>'))
|
118
118
|
end
|
119
|
-
|
119
|
+
|
120
120
|
it "should return correct xml and initialize correctly from xml" do
|
121
121
|
first = First.new_from_xml(@xml)
|
122
122
|
first.first_id.should == 1
|
@@ -136,11 +136,13 @@ describe FreshBooks::Base do
|
|
136
136
|
@elem = mock('XmlObj', :text => '123')
|
137
137
|
@resp = mock('XmlResp', :elements => [nil, @elem])
|
138
138
|
@resp.stub!(:success?).and_return(true)
|
139
|
-
|
139
|
+
@connection = mock('Connection')
|
140
|
+
FreshBooks::Base.stub!(:connection).and_return(@connection)
|
141
|
+
@connection.stub!(:call_api).and_return(@resp)
|
140
142
|
end
|
141
143
|
|
142
144
|
it "should call api method" do
|
143
|
-
|
145
|
+
@connection.should_receive(:call_api).with('test.create', 'test' => @object).and_return(@resp)
|
144
146
|
@object.create
|
145
147
|
end
|
146
148
|
|
@@ -156,12 +158,12 @@ describe FreshBooks::Base do
|
|
156
158
|
end
|
157
159
|
|
158
160
|
it "should return id if call succeeded" do
|
159
|
-
@object.create.should
|
161
|
+
@object.create.should be_true
|
160
162
|
end
|
161
163
|
|
162
164
|
it "should return nil if call failed" do
|
163
165
|
@resp.stub!(:success?).and_return(false)
|
164
|
-
@object.create.should
|
166
|
+
@object.create.should be_false
|
165
167
|
end
|
166
168
|
end
|
167
169
|
|
@@ -169,11 +171,13 @@ describe FreshBooks::Base do
|
|
169
171
|
before(:each) do
|
170
172
|
@resp = mock('XmlResp')
|
171
173
|
@resp.stub!(:success?).and_return(true)
|
172
|
-
|
174
|
+
@connection = mock('Connection')
|
175
|
+
FreshBooks::Base.stub!(:connection).and_return(@connection)
|
176
|
+
@connection.stub!(:call_api).and_return(@resp)
|
173
177
|
end
|
174
178
|
|
175
179
|
it "should call api method" do
|
176
|
-
|
180
|
+
@connection.should_receive(:call_api).with('test.update', 'test' => @object).and_return(@resp)
|
177
181
|
@object.update
|
178
182
|
end
|
179
183
|
|
@@ -193,11 +197,13 @@ describe FreshBooks::Base do
|
|
193
197
|
@object.test_id = 1
|
194
198
|
@resp = mock('XmlResp')
|
195
199
|
@resp.stub!(:success?).and_return(true)
|
196
|
-
|
200
|
+
@connection = mock('Connection')
|
201
|
+
FreshBooks::Base.stub!(:connection).and_return(@connection)
|
202
|
+
@connection.stub!(:call_api).and_return(@resp)
|
197
203
|
end
|
198
204
|
|
199
205
|
it "should call api method" do
|
200
|
-
|
206
|
+
@connection.should_receive(:call_api).with('test.delete', 'test_id' => 1).and_return(@resp)
|
201
207
|
@object.delete
|
202
208
|
end
|
203
209
|
|
@@ -216,11 +222,13 @@ describe FreshBooks::Base do
|
|
216
222
|
before(:each) do
|
217
223
|
@resp = mock('XmlResp')
|
218
224
|
@resp.stub!(:success?).and_return(true)
|
219
|
-
|
225
|
+
@connection = mock('Connection')
|
226
|
+
FreshBooks::Base.stub!(:connection).and_return(@connection)
|
227
|
+
@connection.stub!(:call_api).and_return(@resp)
|
220
228
|
end
|
221
229
|
|
222
230
|
it "should call api method" do
|
223
|
-
|
231
|
+
@connection.should_receive(:call_api).with('test.delete', 'test_id' => 1).and_return(@resp)
|
224
232
|
Test.delete(1)
|
225
233
|
end
|
226
234
|
|
@@ -239,12 +247,14 @@ describe FreshBooks::Base do
|
|
239
247
|
before(:each) do
|
240
248
|
@resp = mock('XmlResp', :elements => [])
|
241
249
|
@resp.stub!(:success?).and_return(true)
|
242
|
-
|
250
|
+
@connection = mock('Connection')
|
251
|
+
FreshBooks::Base.stub!(:connection).and_return(@connection)
|
252
|
+
@connection.stub!(:call_api).and_return(@resp)
|
243
253
|
Test.stub!(:new_from_xml).and_return(true)
|
244
254
|
end
|
245
255
|
|
246
256
|
it "should call api method" do
|
247
|
-
|
257
|
+
@connection.should_receive(:call_api).with('test.get', 'test_id' => 1).and_return(@resp)
|
248
258
|
Test.get(1)
|
249
259
|
end
|
250
260
|
|
@@ -264,15 +274,25 @@ describe FreshBooks::Base do
|
|
264
274
|
before(:each) do
|
265
275
|
@elem1 = mock('XmlElement')
|
266
276
|
@elem2 = mock('XmlElement')
|
267
|
-
@elems = mock('XmlElements',
|
277
|
+
@elems = mock('XmlElements',
|
278
|
+
:elements => [@elem1, @elem2],
|
279
|
+
:attributes => {
|
280
|
+
'page' => 1,
|
281
|
+
'pages' => 10,
|
282
|
+
'per_page' => 30,
|
283
|
+
'total' => 300
|
284
|
+
}
|
285
|
+
)
|
268
286
|
@resp = mock('XmlResp', :elements => [nil, @elems])
|
269
287
|
@resp.stub!(:success?).and_return(true)
|
270
|
-
|
288
|
+
@connection = mock('Connection')
|
289
|
+
FreshBooks::Base.stub!(:connection).and_return(@connection)
|
290
|
+
@connection.stub!(:call_api).and_return(@resp)
|
271
291
|
Test.stub!(:new_from_xml).and_return(true)
|
272
292
|
end
|
273
293
|
|
274
294
|
it "should call api method" do
|
275
|
-
|
295
|
+
@connection.should_receive(:call_api).with('test.list', :name => 'value').and_return(@resp)
|
276
296
|
Test.list(:name => 'value')
|
277
297
|
end
|
278
298
|
|
@@ -280,12 +300,17 @@ describe FreshBooks::Base do
|
|
280
300
|
@resp.stub!(:success?).and_return(false)
|
281
301
|
Test.list.should be_nil
|
282
302
|
end
|
303
|
+
|
304
|
+
it "should create a list proxy if request succeeded" do
|
305
|
+
@resp.stub!(:success?).and_return(true)
|
306
|
+
Test.list.should be_a(FreshBooks::ListProxy)
|
307
|
+
end
|
283
308
|
|
284
309
|
it "should build instances from response records if request succeeded" do
|
285
310
|
@resp.stub!(:success?).and_return(true)
|
286
311
|
Test.should_receive(:new_from_xml).with(@elem1).and_return(1)
|
287
312
|
Test.should_receive(:new_from_xml).with(@elem2).and_return(2)
|
288
|
-
Test.list.should == [1, 2]
|
313
|
+
Test.list.to_a.should == [1, 2]
|
289
314
|
end
|
290
315
|
end
|
291
316
|
|
@@ -294,11 +319,13 @@ describe FreshBooks::Base do
|
|
294
319
|
@object.test_id = 1
|
295
320
|
@resp = mock('XmlResp')
|
296
321
|
@resp.stub!(:success?).and_return(true)
|
297
|
-
|
322
|
+
@connection = mock('Connection')
|
323
|
+
FreshBooks::Base.stub!(:connection).and_return(@connection)
|
324
|
+
@connection.stub!(:call_api).and_return(@resp)
|
298
325
|
end
|
299
326
|
|
300
327
|
it "should call api method" do
|
301
|
-
|
328
|
+
@connection.should_receive(:call_api).with('test.sendByEmail', 'test_id' => 1).and_return(@resp)
|
302
329
|
@object.send_by_email
|
303
330
|
end
|
304
331
|
|
@@ -317,11 +344,13 @@ describe FreshBooks::Base do
|
|
317
344
|
before(:each) do
|
318
345
|
@resp = mock('XmlResp')
|
319
346
|
@resp.stub!(:success?).and_return(true)
|
320
|
-
|
347
|
+
@connection = mock('Connection')
|
348
|
+
FreshBooks::Base.stub!(:connection).and_return(@connection)
|
349
|
+
@connection.stub!(:call_api).and_return(@resp)
|
321
350
|
end
|
322
351
|
|
323
352
|
it "should call api method" do
|
324
|
-
|
353
|
+
@connection.should_receive(:call_api).with('test.sendByEmail', 'test_id' => 1).and_return(@resp)
|
325
354
|
Test.send_by_email(1)
|
326
355
|
end
|
327
356
|
|
@@ -341,11 +370,13 @@ describe FreshBooks::Base do
|
|
341
370
|
@object.test_id = 1
|
342
371
|
@resp = mock('XmlResp')
|
343
372
|
@resp.stub!(:success?).and_return(true)
|
344
|
-
|
373
|
+
@connection = mock('Connection')
|
374
|
+
FreshBooks::Base.stub!(:connection).and_return(@connection)
|
375
|
+
@connection.stub!(:call_api).and_return(@resp)
|
345
376
|
end
|
346
377
|
|
347
378
|
it "should call api method" do
|
348
|
-
|
379
|
+
@connection.should_receive(:call_api).with('test.sendBySnailMail', 'test_id' => 1).and_return(@resp)
|
349
380
|
@object.send_by_snail_mail
|
350
381
|
end
|
351
382
|
|
@@ -364,11 +395,13 @@ describe FreshBooks::Base do
|
|
364
395
|
before(:each) do
|
365
396
|
@resp = mock('XmlResp')
|
366
397
|
@resp.stub!(:success?).and_return(true)
|
367
|
-
|
398
|
+
@connection = mock('Connection')
|
399
|
+
FreshBooks::Base.stub!(:connection).and_return(@connection)
|
400
|
+
@connection.stub!(:call_api).and_return(@resp)
|
368
401
|
end
|
369
402
|
|
370
403
|
it "should call api method" do
|
371
|
-
|
404
|
+
@connection.should_receive(:call_api).with('test.sendBySnailMail', 'test_id' => 1).and_return(@resp)
|
372
405
|
Test.send_by_snail_mail(1)
|
373
406
|
end
|
374
407
|
|
@@ -74,8 +74,8 @@ describe FreshBooks::TimeEntry do
|
|
74
74
|
|
75
75
|
# it "should update an existing time_entry" do
|
76
76
|
# time_entries = FreshBooks::TimeEntry.list
|
77
|
-
# @time_entry.hours
|
78
|
-
#
|
77
|
+
# time_entry = FreshBooks::TimeEntry.new(:time_entry_id => @time_entry.time_entry_id, :hours => 2)
|
78
|
+
# time_entry.update.should be_true
|
79
79
|
# FreshBooks::TimeEntry.get(@time_entry.time_entry_id).hours.should == 2
|
80
80
|
# FreshBooks::TimeEntry.list.size.should == time_entries.size
|
81
81
|
# end
|
data/spec/spec_helper.rb
CHANGED
@@ -8,4 +8,4 @@ end
|
|
8
8
|
|
9
9
|
require File.dirname(__FILE__) + '/../lib/freshbooks.rb'
|
10
10
|
|
11
|
-
FreshBooks::Base.
|
11
|
+
FreshBooks::Base.establish_connection!('flatsourcingtest.freshbooks.com', 'a2e32a3dcb2444c2f0fc47a5ae7227d2')
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: turingstudio-freshbooks-rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.
|
4
|
+
version: 3.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- The Turing Studio, Inc.
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-03-
|
12
|
+
date: 2009-03-04 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -81,9 +81,8 @@ files:
|
|
81
81
|
has_rdoc: true
|
82
82
|
homepage: http://github.com/turingstudio/freshbooks-rb/
|
83
83
|
post_install_message:
|
84
|
-
rdoc_options:
|
85
|
-
|
86
|
-
- README.rdoc
|
84
|
+
rdoc_options: []
|
85
|
+
|
87
86
|
require_paths:
|
88
87
|
- lib
|
89
88
|
required_ruby_version: !ruby/object:Gem::Requirement
|
@@ -100,7 +99,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
100
99
|
version:
|
101
100
|
requirements: []
|
102
101
|
|
103
|
-
rubyforge_project:
|
102
|
+
rubyforge_project:
|
104
103
|
rubygems_version: 1.2.0
|
105
104
|
signing_key:
|
106
105
|
specification_version: 2
|