spohlenz-freshbooksrb 0.3

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.
@@ -0,0 +1,50 @@
1
+ require 'httparty'
2
+ require 'builder'
3
+
4
+ module FreshBooks
5
+ class APIError < Exception; end
6
+
7
+ class API
8
+ include HTTParty
9
+
10
+ format :xml
11
+
12
+ def self.call(method, params={})
13
+ result = post('/xml-in', :body => build_request(method, params))['response']
14
+
15
+ if result['status'] == 'ok'
16
+ result
17
+ else
18
+ raise APIError.new(result['error'])
19
+ end
20
+ end
21
+
22
+ def self.configure(url, token)
23
+ self.base_uri "#{url}:443/api/2.1"
24
+ self.basic_auth token, 'X'
25
+ end
26
+
27
+ private
28
+ def self.build_request(method, params)
29
+ returning '' do |result|
30
+ xml = Builder::XmlMarkup.new(:target => result)
31
+ xml.instruct!
32
+ xml.request(:method => method) { build_xml(xml, params) }
33
+ end
34
+ end
35
+
36
+ def self.build_xml(xml, val={})
37
+ val.each do |k, v|
38
+ if v.is_a?(Hash)
39
+ xml.tag!(k) { build_xml(xml, v) }
40
+ elsif v.is_a?(Array)
41
+ xml.tag!(k) {
42
+ v.each { |i| xml.tag!(k.to_s.singularize, i) }
43
+ }
44
+ else
45
+ xml.tag!(k, v)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,105 @@
1
+ module FreshBooks
2
+ class Base
3
+ def initialize(attributes={})
4
+ @attributes = attributes.inject({}) { |result, pair|
5
+ result[pair.first] = self.class.normalize(*pair)
6
+ result
7
+ }
8
+ end
9
+
10
+ def method_missing(method, *args, &block)
11
+ if @attributes.has_key?(method.to_s)
12
+ @attributes[method.to_s]
13
+ else
14
+ super
15
+ end
16
+ end
17
+
18
+ def id
19
+ @attributes[self.class.id_field]
20
+ end
21
+
22
+ def self.map(mappings={})
23
+ @mappings ||= {}
24
+ @mappings.merge!(mappings)
25
+ end
26
+
27
+ # API methods
28
+
29
+ def self.create(params={})
30
+ call_api('create', params)[id_field]
31
+ end
32
+
33
+ def self.update(id, params={})
34
+ call_api_with_id('update', id, params)
35
+ end
36
+
37
+ def self.get(id)
38
+ result = call_api_with_id('get', id)
39
+ new(result[prefix]) if result[prefix]
40
+ end
41
+
42
+ def self.delete(id)
43
+ call_api_with_id('delete', id)
44
+ end
45
+
46
+ def self.list(params={})
47
+ result = call_api('list', params)
48
+ if result[prefix.pluralize] && result[prefix]
49
+ result[prefix.pluralize][prefix].map { |p| new(p) }
50
+ else
51
+ []
52
+ end
53
+ end
54
+
55
+ def self.has_many(model)
56
+ class_eval <<-EOF
57
+ def #{model}(params={})
58
+ FreshBooks::#{model.to_s.classify}.list(params.merge(self.class.id_field => id))
59
+ end
60
+ EOF
61
+ end
62
+
63
+ def self.belongs_to(model)
64
+ class_eval <<-EOF
65
+ def #{model}
66
+ FreshBooks::#{model.to_s.classify}.get(#{model}_id)
67
+ end
68
+ EOF
69
+ end
70
+
71
+ private
72
+ def self.call_api_with_id(method, id, params={})
73
+ call_api(method, params.merge(id_field => id))
74
+ end
75
+
76
+ def self.call_api(method, params={})
77
+ API.call("#{prefix}.#{method}", params)
78
+ end
79
+
80
+ def self.normalize(key, value)
81
+ return value.to_i if key.ends_with?('_id')
82
+
83
+ case @mappings[key.to_sym]
84
+ when :integer
85
+ value.to_i
86
+ when :float
87
+ value.to_f
88
+ when :date
89
+ Date.parse(value)
90
+ when :boolean
91
+ value == '1'
92
+ else
93
+ value
94
+ end
95
+ end
96
+
97
+ def self.prefix
98
+ self.name.demodulize.underscore
99
+ end
100
+
101
+ def self.id_field
102
+ "#{prefix}_id"
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,117 @@
1
+ require File.dirname(__FILE__) + '/freshbooks/api'
2
+ require File.dirname(__FILE__) + '/freshbooks/base'
3
+
4
+ module FreshBooks
5
+
6
+ # Must be called before invoking API methods
7
+
8
+ def self.setup(url, token)
9
+ @url = url
10
+ API.configure(url, token)
11
+ end
12
+
13
+ def self.url
14
+ @url
15
+ end
16
+
17
+ # Models exposed by FreshBooks API
18
+
19
+ class Client < Base
20
+ has_many :recurrings
21
+ has_many :invoices
22
+ has_many :estimates
23
+ has_many :payments
24
+ end
25
+
26
+ class Invoice < Base
27
+ map :date => :date,
28
+ :amount => :float,
29
+ :discount => :float
30
+
31
+ has_many :payments
32
+ belongs_to :client
33
+
34
+ def send_by_email
35
+ self.class.send_by_email(invoice_id)
36
+ end
37
+
38
+ def send_by_snail_mail
39
+ self.class.send_by_snail_mail(invoice_id)
40
+ end
41
+
42
+ def self.send_by_email(id)
43
+ call_api_with_id('sendByEmail', id)
44
+ end
45
+
46
+ def self.send_by_snail_mail(id)
47
+ call_api_with_id('sendBySnailMail', id)
48
+ end
49
+ end
50
+
51
+ class Estimate < Base
52
+ map :date => :date,
53
+ :amount => :float,
54
+ :discount => :float
55
+
56
+ belongs_to :client
57
+
58
+ def send_by_email
59
+ self.class.send_by_email(invoice_id)
60
+ end
61
+
62
+ def self.send_by_email(id)
63
+ call_api_with_id('sendByEmail', id)
64
+ end
65
+ end
66
+
67
+ class Recurring < Base
68
+ map :date => :date,
69
+ :discount => :float,
70
+ :send_email => :boolean,
71
+ :send_snail_mail => :boolean
72
+
73
+ has_many :invoices
74
+ belongs_to :client
75
+ end
76
+
77
+ class Item < Base
78
+ map :unit_cost => :float,
79
+ :quantity => :integer,
80
+ :inventory => :integer
81
+ end
82
+
83
+ class Payment < Base
84
+ map :date => :date,
85
+ :amount => :float
86
+
87
+ belongs_to :client
88
+ belongs_to :invoice
89
+
90
+ def self.delete(id)
91
+ raise 'Payments may not be deleted'
92
+ end
93
+ end
94
+
95
+ class TimeEntry < Base
96
+ map :hours => :float,
97
+ :date => :date
98
+
99
+ belongs_to :project
100
+ belongs_to :task
101
+ end
102
+
103
+ class Project < Base
104
+ map :rate => :float
105
+
106
+ has_many :time_entries
107
+ has_many :tasks
108
+ end
109
+
110
+ class Task < Base
111
+ map :rate => :float,
112
+ :billable => :boolean
113
+
114
+ has_many :time_entries
115
+ end
116
+
117
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: spohlenz-freshbooksrb
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.3"
5
+ platform: ruby
6
+ authors:
7
+ - Sam Pohlenz
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-08-13 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: builder
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 2.1.2
23
+ version:
24
+ - !ruby/object:Gem::Dependency
25
+ name: activesupport
26
+ version_requirement:
27
+ version_requirements: !ruby/object:Gem::Requirement
28
+ requirements:
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ version: 2.1.0
32
+ version:
33
+ - !ruby/object:Gem::Dependency
34
+ name: httparty
35
+ version_requirement:
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 0.1.2
41
+ version:
42
+ description: FreshBooksRb is a Ruby library for integrating your apps with the FreshBooks API (http://developers.freshbooks.com/overview/)
43
+ email: sam@sampohlenz.com
44
+ executables: []
45
+
46
+ extensions: []
47
+
48
+ extra_rdoc_files: []
49
+
50
+ files:
51
+ - lib/freshbooks/base.rb
52
+ - lib/freshbooks/api.rb
53
+ - lib/freshbooksrb.rb
54
+ has_rdoc: false
55
+ homepage: http://github.com/spohlenz/freshbooksrb
56
+ post_install_message:
57
+ rdoc_options: []
58
+
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: "0"
66
+ version:
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: "0"
72
+ version:
73
+ requirements: []
74
+
75
+ rubyforge_project:
76
+ rubygems_version: 1.2.0
77
+ signing_key:
78
+ specification_version: 2
79
+ summary: Ruby client for FreshBooks
80
+ test_files: []
81
+