zoho_invoice 0.0.1

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/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ .rvmrc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage/*
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ bundler_args: --without development
2
+ language: ruby
3
+ rvm:
4
+ - 1.9.2
5
+ - 1.9.3
6
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,17 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rake'
4
+
5
+ group :development do
6
+ gem 'pry'
7
+ end
8
+
9
+ group :test do
10
+ gem 'pry'
11
+ gem 'coveralls', :require => false
12
+ gem 'rspec', '>= 2.11'
13
+ gem 'simplecov', :require => false
14
+ gem 'webmock'
15
+ end
16
+
17
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Rimas Silkaitis
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # ZohoInvoice
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'zoho_invoice'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install zoho_invoice
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :test => :spec
@@ -0,0 +1,31 @@
1
+ require 'faraday'
2
+ require 'faraday_middleware'
3
+ require 'nokogiri'
4
+ require 'multi_xml'
5
+
6
+ require 'zoho_invoice/configurable'
7
+ require 'zoho_invoice/auth_token'
8
+ require 'zoho_invoice/defaults'
9
+
10
+ require 'zoho_invoice/client'
11
+ require 'zoho_invoice/version'
12
+ require 'zoho_invoice/base'
13
+ require 'zoho_invoice/customer'
14
+ require 'zoho_invoice/item'
15
+ require 'zoho_invoice/invoice_item'
16
+ require 'zoho_invoice/invoice'
17
+
18
+ module ZohoInvoice
19
+ class << self
20
+ include ZohoInvoice::Configurable
21
+ end
22
+
23
+ # Default for connecting to Zoho Invoice API
24
+ #
25
+ @client_options = {
26
+ :url => ZohoInvoice::Defaults::URL
27
+ }
28
+
29
+ @scope = ZohoInvoice::Defaults::SCOPE
30
+
31
+ end
@@ -0,0 +1,7 @@
1
+ module ZohoInvoice
2
+ module API
3
+ module Customer
4
+
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,28 @@
1
+ module ZohoInvoice
2
+ module AuthToken
3
+ class AuthTokenResult < Struct.new(:authtoken, :cause)
4
+ def success?
5
+ return true if !authtoken.nil?
6
+ false
7
+ end
8
+ end
9
+
10
+ def self.generate_authtoken(email_id, password)
11
+ client = ZohoInvoice::Client.new(:client_options => {:url => 'https://accounts.zoho.com'})
12
+ response = client.request(
13
+ :get,
14
+ '/apiauthtoken/nb/create',
15
+ {
16
+ :SCOPE => 'invoiceapi',
17
+ :EMAIL_ID => email_id,
18
+ :PASSWORD => password
19
+ }
20
+ )
21
+
22
+ result = ZohoInvoice::AuthToken::AuthTokenResult.new(nil, nil)
23
+ result.cause = response.body.match(/\nCAUSE=(.+)\n/)[1] if response.body =~ /RESULT=FALSE/
24
+ result.authtoken = response.body.match(/AUTHTOKEN=(.+)\n/)[1] if response.body =~ /RESULT=TRUE/
25
+ result
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,151 @@
1
+ module ZohoInvoice
2
+ class Error < Struct.new(:message, :code, :status, :http_status)
3
+ end
4
+
5
+ # Used for instances when some of the representations in zoho
6
+ # dont have their own end points in the API
7
+ #
8
+ class ActionNotSupportedError < StandardError; end
9
+
10
+ class Base
11
+
12
+ attr_reader :client
13
+
14
+ def self.define_object_attrs(*attrs)
15
+ @attributes = attrs
16
+ create_attributes(attrs)
17
+ end
18
+
19
+ # TODO Create an Association class to manage the relationship
20
+ #
21
+ def self.has_many(*attrs)
22
+ @reflections = attrs
23
+ create_attributes(attrs)
24
+ end
25
+
26
+ def self.create(client, options = {})
27
+ self.new(client, options).save
28
+ end
29
+
30
+ def initialize(client, options = {})
31
+ @client = client
32
+
33
+ # Assign all of the single attribtues
34
+ #
35
+ if !self.attributes.empty?
36
+ (self.attributes & options.keys).each do |attribute|
37
+ self.send("#{attribute}=", options[attribute])
38
+ end
39
+ end
40
+
41
+ # Assign all of the associations. Not the most advanced
42
+ #
43
+ if self.reflections.is_a?(Array)
44
+ self.reflections.each { |r| self.send("#{r}=", []) }
45
+ (self.reflections & options.keys).each do |reflection|
46
+ options[reflection].each do |reflection_obj|
47
+ klass = Kernel.const_get(camel_case(reflection.to_s[0..-2]))
48
+ if reflection_obj.is_a?(Hash)
49
+ self.send("#{reflection}") << klass.new(@client, reflection_obj)
50
+ elsif reflection_obj.is_a?(klass)
51
+ self.send("#{reflection}") << reflection_obj
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ def reflections
59
+ self.class.instance_variable_get(:'@reflections') || []
60
+ end
61
+
62
+ def attributes
63
+ self.class.instance_variable_get(:'@attributes') || []
64
+ end
65
+
66
+ def errors
67
+ @errors ||= []
68
+ end
69
+
70
+ # TODO Determining the resource to use will need to change
71
+ #
72
+ def save
73
+ @errors = []
74
+
75
+ action = 'create'
76
+ action = 'update' if !send("#{self.class.to_s.downcase}_id").nil?
77
+
78
+ result = client.post("/api/#{self.class.to_s.downcase + 's'}/#{action}", :XMLString => self.to_xml)
79
+
80
+ if action == 'create' && !result.body.nil? && !result.body[self.class.to_s].nil?
81
+ self.send("#{self.class.to_s.downcase}_id=", result.body[self.class.to_s]["#{self.class}ID"])
82
+ end
83
+
84
+ self
85
+ rescue Faraday::Error::ClientError => e
86
+ if e.response[:body]
87
+ self.errors << process_error(e.response)
88
+ end
89
+ return self
90
+ end
91
+
92
+ # This needs to be a Nokogiri::XML::Builder
93
+ #
94
+ def to_xml(*args)
95
+ build_attributes.to_xml(*args)
96
+ end
97
+
98
+ protected
99
+
100
+ def process_error(request_result)
101
+ self.class.process_error(request_result)
102
+ end
103
+
104
+ def self.process_error(request_result)
105
+ error = Error.new
106
+ response_body = request_result[:body]
107
+ error.status = response_body['Response']['status']
108
+ error.code = response_body['Response']['Code']
109
+ error.message = response_body['Response']['Message']
110
+ error.http_status = request_result[:status]
111
+ error
112
+ end
113
+
114
+ def self.camel_case(str)
115
+ return str if str !~ /_/ && str =~ /[A-Z]+.*/
116
+ str.split('_').map{|e| e.capitalize}.join
117
+ end
118
+
119
+ def camel_case(str)
120
+ self.class.camel_case(str)
121
+ end
122
+
123
+ def build_attributes
124
+ Nokogiri::XML::Builder.new do |xml|
125
+ xml.send("#{self.class}") {
126
+ self.attributes.each do |attr|
127
+ vals = self.send(attr)
128
+ if !vals.nil? && !vals.is_a?(Array)
129
+ xml.send("#{camel_case(attr.to_s)}_", self.send(attr))
130
+ end
131
+ end
132
+ self.reflections.each do |refl|
133
+ if !refl.empty?
134
+ xml.send(camel_case(refl.to_s)) {
135
+ self.send(refl).each { |x| xml << x.to_xml(:save_with => Nokogiri::XML::Node::SaveOptions::NO_DECLARATION) }
136
+ }
137
+ end
138
+ end
139
+ }
140
+ end
141
+ end
142
+
143
+ private
144
+
145
+ def self.create_attributes(attrs)
146
+ attrs.each do |attr|
147
+ attr_accessor attr
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,39 @@
1
+ module ZohoInvoice
2
+ class Client
3
+ include ZohoInvoice::Configurable
4
+
5
+ def initialize(options = {})
6
+ ZohoInvoice::Configurable.keys.each do |key|
7
+ instance_variable_set(:"@#{key}", options[key] || ZohoInvoice.instance_variable_get(:"@#{key}"))
8
+ end
9
+ @client_options = ZohoInvoice.instance_variable_get(:'@client_options').merge(options[:client_options] || {})
10
+ end
11
+
12
+ def get(path, params={})
13
+ request(:get, path, credentials.merge(params))
14
+ end
15
+
16
+ def post(path, params={})
17
+ request(:post, path, credentials.merge(params))
18
+ end
19
+
20
+ def request(verb, path, params={})
21
+ connection.send(verb, path, params)
22
+ end
23
+
24
+ private
25
+
26
+ def connection
27
+ @connection ||= Faraday.new(@client_options) do |c|
28
+ c.use Faraday::Response::RaiseError
29
+ c.response :xml, :content_type => 'application/xml'
30
+
31
+ c.request :multipart
32
+ c.request :url_encoded
33
+
34
+ c.adapter Faraday.default_adapter
35
+ end
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,49 @@
1
+ module ZohoInvoice
2
+ module Configurable
3
+
4
+ # @return [String] The permanent authtoken used to authenticate each API request
5
+ attr_accessor :authtoken
6
+
7
+ # @return [String] The value for this parameter has to be invoiceapi
8
+ attr_accessor :scope
9
+
10
+ # @return [String] The key used to authenticate the API caller
11
+ attr_accessor :apikey
12
+
13
+ # @return [Hash] Client configuration to pass to Faraday
14
+ attr_accessor :client_options
15
+
16
+ class << self
17
+
18
+ def keys
19
+ @keys ||= [
20
+ :authtoken,
21
+ :scope,
22
+ :apikey,
23
+ :client_options
24
+ ]
25
+ end
26
+
27
+ end
28
+
29
+ def configure
30
+ yield self
31
+ self
32
+ end
33
+
34
+ def config_hash
35
+ Hash[ZohoInvoice::Configurable.keys.map{ |key| [key, instance_variable_get(:"@#{key}")] }]
36
+ end
37
+
38
+ private
39
+
40
+ def credentials
41
+ {
42
+ :authtoken => @authtoken,
43
+ :scope => @scope,
44
+ :apikey => @apikey
45
+ }
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,22 @@
1
+ require 'zoho_invoice/base'
2
+
3
+ module ZohoInvoice
4
+ class Contact < Base
5
+
6
+ define_object_attrs :salutation,
7
+ :first_name,
8
+ :last_name,
9
+ :e_mail,
10
+ :phone,
11
+ :mobile
12
+
13
+ def self.create(client, options = {})
14
+ raise ZohoInvoice::ActionNotSupportedError
15
+ end
16
+
17
+ def save
18
+ raise ZohoInvoice::ActionNotSupportedError
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,29 @@
1
+ require 'zoho_invoice/base'
2
+ require 'zoho_invoice/contact'
3
+
4
+ module ZohoInvoice
5
+ class Customer < Base
6
+
7
+ define_object_attrs :name,
8
+ :payments_due,
9
+ :currency_code,
10
+ :billing_address,
11
+ :billing_city,
12
+ :billing_zip,
13
+ :billing_country,
14
+ :billing_fax,
15
+ :shipping_address,
16
+ :shipping_city,
17
+ :shipping_state,
18
+ :shipping_country,
19
+ :shipping_fax,
20
+ :customer_id
21
+
22
+ has_many :contacts
23
+
24
+ def self.search(client, input_text)
25
+
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,9 @@
1
+ module ZohoInvoice
2
+ module Defaults
3
+
4
+ URL = 'https://invoice.zoho.com'
5
+
6
+ SCOPE = 'invoiceapi'
7
+
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ module ZohoInvoice
2
+ class Error
3
+
4
+ attr_accessor :message, :code
5
+
6
+ end
7
+ end
@@ -0,0 +1,25 @@
1
+ require 'zoho_invoice/invoice_item'
2
+
3
+ module ZohoInvoice
4
+ class Invoice < Base
5
+
6
+ define_object_attrs :invoice_id,
7
+ :created_time, :exchange_rate,
8
+ :last_modified_time, :l_f_name,
9
+ :last_sync_time, :late_fee_amount,
10
+ :source, :total,
11
+ :reference_id, :invoice_item_total,
12
+ :invoice_number, :tax_total,
13
+ :p_o_number, :balance,
14
+ :status,
15
+ :customer_id,
16
+ :customer_name,
17
+ :invoice_date,
18
+ :payments_due,
19
+ :due_date,
20
+ :currency_code
21
+
22
+ has_many :invoice_items
23
+
24
+ end
25
+ end
@@ -0,0 +1,7 @@
1
+ module ZohoInvoice
2
+ class InvoiceItem < Item
3
+
4
+ primary_key :item_id
5
+
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ module ZohoInvoice
2
+ class Item < Base
3
+
4
+ define_object_attrs :name,
5
+ :description,
6
+ :rate
7
+
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module ZohoInvoice
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1 @@
1
+ <Response status="0"><Code>2</Code><Message><![CDATA[Invalid value passed for XMLString]]></Message></Response>
@@ -0,0 +1 @@
1
+ <Response status="0"><Code>1</Code><Message><![CDATA[API can be called only over an HTTPS Post request]]></Message></Response>
@@ -0,0 +1 @@
1
+ <?xml version="1.0" encoding="UTF-8"?><Response status="0"><Code>3004</Code><Message>The Customer field can neither be blank nor be incorrect. Please enter a correct customer name.</Message></Response>
@@ -0,0 +1,4 @@
1
+ #
2
+ #Mon Mar 18 15:33:32 PDT 2013
3
+ CAUSE=INVALID_PASSWORD
4
+ RESULT=FALSE
@@ -0,0 +1 @@
1
+ <Response status="0"><Code>5</Code><Message><![CDATA[Invalid URL Passed]]></Message></Response>
@@ -0,0 +1,4 @@
1
+ #
2
+ #Mon Mar 18 15:34:33 PDT 2013
3
+ CAUSE=NO_SUCH_USER
4
+ RESULT=FALSE
@@ -0,0 +1 @@
1
+ <?xml version="1.0" encoding="UTF-8"?><Response status="0"><Code>4016</Code><Message>Please select an item</Message></Response>
@@ -0,0 +1,4 @@
1
+ #
2
+ #Mon Mar 18 15:30:44 PDT 2013
3
+ AUTHTOKEN=aaaa2222bbbb3333cccc4444dddd5555
4
+ RESULT=TRUE
@@ -0,0 +1,55 @@
1
+ # Get everything loaded up per gemspec
2
+ #
3
+ require 'simplecov'
4
+ require 'coveralls'
5
+
6
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
7
+ SimpleCov::Formatter::HTMLFormatter,
8
+ Coveralls::SimpleCov::Formatter
9
+ ]
10
+ SimpleCov.start
11
+
12
+ require 'rspec'
13
+ require 'zoho_invoice'
14
+ require 'webmock/rspec'
15
+ require 'pry'
16
+
17
+ WebMock.disable_net_connect!
18
+
19
+ RSpec.configure do |config|
20
+ config.expect_with(:rspec) do |c|
21
+ c.syntax = :expect
22
+ end
23
+ end
24
+
25
+ def stub_get(path)
26
+ stub_request(:get, ZohoInvoice::Defaults::URL + path)
27
+ end
28
+
29
+ def a_get(path)
30
+ a_request(:get, ZohoInvoice::Defaults::URL + path)
31
+ end
32
+
33
+ def stub_post(path)
34
+ stub_request(:post, ZohoInvoice::Defaults::URL + path)
35
+ end
36
+
37
+ def a_post(path)
38
+ a_request(:post, ZohoInvoice::Defaults::URL + path)
39
+ end
40
+
41
+ def fixture_path
42
+ File.expand_path('../fixtures', __FILE__)
43
+ end
44
+
45
+ def fixture(file)
46
+ File.new(fixture_path + '/' + file)
47
+ end
48
+
49
+ def default_credentials
50
+ {
51
+ :authtoken => '1234',
52
+ :scope => ZohoInvoice::Defaults::SCOPE,
53
+ :apikey => '5678'
54
+ }
55
+ end
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+
3
+ describe ZohoInvoice::AuthToken do
4
+
5
+ it "should be able to generate a new authtoken" do
6
+ email_id = 'big_tuna@example.com'
7
+ password = 'tuna'
8
+
9
+ stub_request(:get, 'https://accounts.zoho.com/apiauthtoken/nb/create').
10
+ with(:query => default_auth_options(email_id, password)).
11
+ to_return(:body => fixture('successful_authtoken'))
12
+
13
+ result = ZohoInvoice::AuthToken.generate_authtoken(email_id, password)
14
+ expect(result.success?).to be_true
15
+ expect(result).to be_a ZohoInvoice::AuthToken::AuthTokenResult
16
+ expect(a_request(:get, 'https://accounts.zoho.com/apiauthtoken/nb/create')
17
+ .with(:query => default_auth_options(email_id, password)))
18
+ .to have_been_made
19
+ end
20
+
21
+ describe "failure cases" do
22
+
23
+ it "should be unccessful if user name is incorrect" do
24
+ bad_request('invalid_user', 'tuna@example.com', 'tuna') do |auth_token_result|
25
+ expect(auth_token_result.cause).to eq('NO_SUCH_USER')
26
+ end
27
+ end
28
+
29
+ it "should be unccessful if user name is incorrect" do
30
+ bad_request('invalid_password', 'big_tuna@example.com', 'tu') do |auth_token_result|
31
+ expect(auth_token_result.cause).to eq('INVALID_PASSWORD')
32
+ end
33
+ end
34
+
35
+ def bad_request(fixture_to_use, email_id, password)
36
+ stub_request(:get, 'https://accounts.zoho.com/apiauthtoken/nb/create').
37
+ with(:query => default_auth_options(email_id, password)).
38
+ to_return(:body => fixture(fixture_to_use))
39
+
40
+ result = ZohoInvoice::AuthToken.generate_authtoken(email_id, password)
41
+ expect(result.success?).to be_false
42
+ expect(result).to be_a ZohoInvoice::AuthToken::AuthTokenResult
43
+ expect(a_request(:get, 'https://accounts.zoho.com/apiauthtoken/nb/create')
44
+ .with(:query => default_auth_options(email_id, password)))
45
+ .to have_been_made
46
+
47
+ yield result
48
+ end
49
+
50
+ end
51
+
52
+ it "should define an auth token result" do
53
+ result = ZohoInvoice::AuthToken::AuthTokenResult.new(nil, nil)
54
+ expect(result.respond_to?(:authtoken)).to be_true
55
+ expect(result.respond_to?(:cause)).to be_true
56
+
57
+ expect(result.success?).to be_false
58
+
59
+ result.authtoken = 'asdf'
60
+ expect(result.success?).to be_true
61
+ end
62
+
63
+ private
64
+
65
+ def default_auth_options(email, password)
66
+ {
67
+ :SCOPE => 'invoiceapi',
68
+ :EMAIL_ID => email,
69
+ :PASSWORD => password
70
+ }
71
+ end
72
+
73
+ end
@@ -0,0 +1,182 @@
1
+ require 'spec_helper'
2
+
3
+ describe ZohoInvoice::Base do
4
+
5
+ before do
6
+ @client = ZohoInvoice::Client.new(default_credentials)
7
+ end
8
+
9
+ it "requires a client upon initialization" do
10
+ expect{ ZohoInvoice::Base.new }.to raise_error(StandardError)
11
+ end
12
+
13
+ it "can define the attributes of an instance object and assign them on initialization" do
14
+ class TestClass < ZohoInvoice::Base
15
+ define_object_attrs :test_it
16
+ end
17
+
18
+ test_obj = TestClass.new(@client)
19
+ expect(test_obj.respond_to?('test_it')).to be_true
20
+ expect(test_obj.respond_to?('test_it=')).to be_true
21
+ expect(test_obj.attributes).to eq([:test_it])
22
+
23
+ test_obj1 = TestClass.new(@client, :test_it => 1)
24
+ expect(test_obj1.test_it).to eq(1)
25
+ end
26
+
27
+ it "can define associations to itself" do
28
+ class TestClass < ZohoInvoice::Base
29
+ has_many :things,
30
+ :more_things
31
+ end
32
+
33
+ test_obj = TestClass.new(@client)
34
+ expect(test_obj.things.is_a?(Array)).to be_true
35
+ expect(test_obj.things.length).to eq(0)
36
+ expect(test_obj.reflections).to eq([:things, :more_things])
37
+ end
38
+
39
+ describe "generating an xml representation" do
40
+ before do
41
+ class Something < ZohoInvoice::Base
42
+ define_object_attrs :test_it,
43
+ :blah,
44
+ :something_id
45
+
46
+ has_many :things,
47
+ :more_things
48
+ end
49
+
50
+ @test_obj = Something.new(@client)
51
+ end
52
+
53
+ it "should specific an xml doctype" do
54
+ xml = @test_obj.to_xml
55
+ expect(xml).to include("<?xml version=\"1.0\"?>")
56
+ end
57
+
58
+ it "should have a root element" do
59
+ xml = Nokogiri::XML(@test_obj.to_xml)
60
+ expect(xml.children.length).to eq(1)
61
+ expect(xml.children.first.name).to eq("Something")
62
+
63
+ @test_obj.test_it = 1234
64
+ xml = Nokogiri::XML(@test_obj.to_xml)
65
+ expect(xml.children.length).to eq(1)
66
+ expect(xml.children.first.name).to eq("Something")
67
+ end
68
+
69
+ it "should create the attributes correctly" do
70
+ @test_obj.test_it = 1234
71
+ xml = Nokogiri::XML(@test_obj.to_xml)
72
+ expect(xml.xpath('//TestIt').length).to eq(1)
73
+ expect(xml.xpath('//TestIt').first.text).to eq('1234')
74
+
75
+ @test_obj.test_it = nil
76
+ xml = Nokogiri::XML(@test_obj.to_xml)
77
+ expect(xml.xpath('//TestIt')).to be_empty
78
+ end
79
+
80
+ it "should create the associations correctly" do
81
+ class Thing < Struct.new(:stuff)
82
+ def to_xml(*args)
83
+ "<Thing>#{stuff}</Thing>"
84
+ end
85
+ end
86
+ @test_obj.things << Thing.new('asdf')
87
+ @test_obj.things << Thing.new('asdf')
88
+
89
+ @test_obj.more_things << Thing.new('1234')
90
+
91
+ xml = Nokogiri::XML(@test_obj.to_xml)
92
+ expect(xml.xpath('//Things').length).to eq(1)
93
+ expect(xml.xpath('//Things/Thing').length).to eq(2)
94
+ expect(xml.xpath('//MoreThings/Thing').length).to eq(1)
95
+ end
96
+ end
97
+
98
+ describe "saving an object" do
99
+ before do
100
+ class Something < ZohoInvoice::Base
101
+ define_object_attrs :something_id, :blah
102
+ end
103
+ @test_obj = Something.new(@client)
104
+ end
105
+
106
+ it "calls the create path if its a new record" do
107
+ @test_obj.something_id = nil
108
+ body_params = default_credentials.merge(:XMLString => @test_obj.to_xml)
109
+ stub_post('/api/somethings/create').
110
+ with(:body => body_params).
111
+ to_return(:status => 200, :body => successful_something_response('5555'), :headers => {:content_type => 'application/xml'})
112
+ @test_obj.save
113
+ expect(a_post('/api/somethings/create').with(:body => body_params)).to have_been_made
114
+ end
115
+
116
+ it "calls the update path if its a dirty record" do
117
+ @test_obj.something_id = '123456'
118
+ body_params = default_credentials.merge(:XMLString => @test_obj.to_xml)
119
+ stub_post('/api/somethings/update').
120
+ with(:body => body_params).
121
+ to_return(:status => 200, :body => successful_something_response('123456'), :headers => {:content_type => 'application/xml'})
122
+ @test_obj.save
123
+ expect(a_post('/api/somethings/update').with(:body => body_params)).to have_been_made
124
+ end
125
+
126
+ it "can happen via .create" do
127
+ @test_obj.blah = '1234'
128
+ body_params = default_credentials.merge(:XMLString => @test_obj.to_xml)
129
+ stub_post('/api/somethings/create').
130
+ with(:body => body_params).
131
+ to_return(:status => 200, :body => successful_something_response("1234"), :headers => { :content_type => 'application/xml' })
132
+ test_obj = Something.create(@client, :blah => '1234')
133
+ expect(a_post('/api/somethings/create').with(:body => body_params)).to have_been_made
134
+ expect(test_obj.something_id).to eq('1')
135
+ end
136
+
137
+ it "returns the object and has an error method" do
138
+ @test_obj.blah = '1234'
139
+ body_params = default_credentials.merge(:XMLString => @test_obj.to_xml)
140
+ stub_post('/api/somethings/create').with(:body => body_params).to_return(:status => 500, :body => fixture('500_internal_server_error'), :headers => { :content_type => 'application/xml' })
141
+ test_obj = Something.create(@client, :blah => '1234')
142
+ expect(test_obj.something_id).to be_nil
143
+ expect(test_obj.errors.length).to eq(1)
144
+ error = test_obj.errors.first
145
+ expect(error.message).to eq("Invalid value passed for XMLString")
146
+ expect(error.code).to eq('2')
147
+ expect(error.status).to eq('0')
148
+ expect(error.http_status).to eq(500)
149
+ end
150
+
151
+ def successful_something_response(blah_payload)
152
+ "<Something><SomethingID>1</SomethingID><Blah>#{blah_payload}</Blah></Something>"
153
+ end
154
+ end
155
+
156
+ describe "nested associations" do
157
+ class Tuna < ZohoInvoice::Base
158
+ define_object_attrs :blah
159
+ end
160
+ class TestIt < ZohoInvoice::Base
161
+ has_many :tunas
162
+ end
163
+
164
+ before do
165
+ @client = ZohoInvoice::Client.new(default_credentials)
166
+ end
167
+
168
+ it "can be created at initialization" do
169
+ test = TestIt.new(@client, :tunas => [{:blah => 1234}, Tuna.new(@client, :blah => 5678)])
170
+ expect(test.tunas.length).to eq(2)
171
+ expect(test.tunas.first.blah).to eq(1234)
172
+ expect(test.tunas.last.blah).to eq(5678)
173
+ end
174
+
175
+ it "outputted when coverted to xml", :focus => true do
176
+ test = TestIt.new(@client, :tunas => [{:blah => 1234}])
177
+ doc = Nokogiri::XML(test.to_xml)
178
+ expect(doc.xpath('//Tunas').length).to be >= 1
179
+ expect(doc.xpath('//Tuna').length).to eq(1)
180
+ end
181
+ end
182
+ end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+
3
+ describe ZohoInvoice::Client do
4
+
5
+ subject do
6
+ ZohoInvoice::Client.new(default_credentials)
7
+ end
8
+
9
+ describe "#get" do
10
+ it "can create custom requests" do
11
+ query_hash = default_credentials.merge({:rad => 'object'})
12
+ stub_get('/something/sweet').with(:query => query_hash)
13
+ subject.get('/something/sweet', {:rad => 'object'})
14
+ expect(a_get('/something/sweet').with(:query => query_hash)).to have_been_made
15
+ end
16
+ end
17
+
18
+ describe "#post" do
19
+ it "can create custom requests" do
20
+ query_hash = default_credentials.merge({:rad => 'object'})
21
+ stub_post('/something/awesome').with(:body => query_hash)
22
+ subject.post('/something/awesome', {:rad => 'object'})
23
+ expect(a_post('/something/awesome').with(:body => query_hash)).to have_been_made
24
+ end
25
+ end
26
+
27
+ describe "#request" do
28
+ it "can create custom requests" do
29
+ stub_post('/awesome').with(:body => {:rad => 'object'})
30
+ subject.request(:post, '/awesome', {:rad => 'object'})
31
+ expect(a_post('/awesome').with({:rad => 'object'})).to have_been_made
32
+ end
33
+
34
+ it "does not include default credentials in params" do
35
+ stub_get('/awesome')
36
+ subject.request(:get, '/awesome')
37
+ expect(a_get('/awesome')).to have_been_made
38
+ end
39
+ end
40
+
41
+ describe "initialization" do
42
+ it "can pass options to faraday" do
43
+ client = ZohoInvoice::Client.new(
44
+ default_credentials.merge({
45
+ :client_options => {
46
+ :url => 'http://something.com'
47
+ }
48
+ })
49
+ )
50
+ stub_request(:get, 'http://something.com/awesome').with(:query => default_credentials)
51
+ client.get('/awesome')
52
+ expect(a_request(:get, 'http://something.com/awesome').with(:query => default_credentials)).to have_been_made
53
+ end
54
+
55
+ it "will inherit config from defaults" do
56
+ client = ZohoInvoice::Client.new
57
+ expect(client.scope).to eq(ZohoInvoice::Defaults::SCOPE)
58
+ expect(client.client_options).to eq({ :url => ZohoInvoice::Defaults::URL })
59
+ end
60
+ end
61
+
62
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe ZohoInvoice::Contact do
4
+
5
+ before do
6
+ @client = ZohoInvoice::Client.new(default_credentials)
7
+ end
8
+
9
+ it ".create should raise an error" do
10
+ expect{ ZohoInvoice::Contact.create(@client) }.to raise_error(ZohoInvoice::ActionNotSupportedError)
11
+ end
12
+
13
+ it "#save should raise an error" do
14
+ contact = ZohoInvoice::Contact.new(@client)
15
+ expect{ contact.save }.to raise_error(ZohoInvoice::ActionNotSupportedError)
16
+ end
17
+
18
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe ZohoInvoice do
4
+
5
+ it "should be able to define global options on the module" do
6
+ ZohoInvoice.configure do |config|
7
+ config.authtoken = '11111'
8
+ config.scope = 'scope'
9
+ config.apikey = 'apikey'
10
+ config.client_options = { :url => 'http://example.com' }
11
+ end
12
+ expect(ZohoInvoice.authtoken).to eq('11111')
13
+ expect(ZohoInvoice.scope).to eq('scope')
14
+ expect(ZohoInvoice.apikey).to eq('apikey')
15
+ expect(ZohoInvoice.client_options).to eq({ :url => 'http://example.com' })
16
+ end
17
+
18
+ it "should be able to return its current options" do
19
+ ZohoInvoice.configure do |config|
20
+ config.authtoken = '22222'
21
+ end
22
+ expect(ZohoInvoice.config_hash[:authtoken]).to eq('22222')
23
+ end
24
+ end
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'zoho_invoice/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "zoho_invoice"
8
+ gem.version = ZohoInvoice::VERSION
9
+ gem.authors = ["Rimas Silkaitis"]
10
+ gem.email = ["neovintage@gmail.com"]
11
+ gem.summary = %q{Zoho Invoice API}
12
+ gem.description = %q{Ruby wrapper for the Zoho Invoice API. Documentation for the Zoho Invoice API can be found at http://zoho.com}
13
+ gem.homepage = "https://github.com/neovintage/zoho_invoice"
14
+
15
+ gem.required_ruby_version = ">= 1.9.3"
16
+ gem.required_rubygems_version = ">= 1.3.6"
17
+
18
+ gem.add_development_dependency 'bundler', '~> 1.0'
19
+
20
+ gem.add_runtime_dependency "faraday", ">= 0.8.0"
21
+ gem.add_runtime_dependency "faraday_middleware", ">= 0.8.7"
22
+ gem.add_runtime_dependency "nokogiri"
23
+ gem.add_runtime_dependency "multi_xml"
24
+
25
+ gem.files = `git ls-files`.split($/)
26
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
27
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
28
+ gem.require_paths = ["lib"]
29
+ end
metadata ADDED
@@ -0,0 +1,175 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zoho_invoice
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Rimas Silkaitis
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-04-03 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: faraday
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 0.8.0
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 0.8.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: faraday_middleware
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: 0.8.7
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: 0.8.7
62
+ - !ruby/object:Gem::Dependency
63
+ name: nokogiri
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: multi_xml
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ description: Ruby wrapper for the Zoho Invoice API. Documentation for the Zoho Invoice
95
+ API can be found at http://zoho.com
96
+ email:
97
+ - neovintage@gmail.com
98
+ executables: []
99
+ extensions: []
100
+ extra_rdoc_files: []
101
+ files:
102
+ - .gitignore
103
+ - .travis.yml
104
+ - Gemfile
105
+ - LICENSE.txt
106
+ - README.md
107
+ - Rakefile
108
+ - lib/zoho_invoice.rb
109
+ - lib/zoho_invoice/api/customers.rb
110
+ - lib/zoho_invoice/auth_token.rb
111
+ - lib/zoho_invoice/base.rb
112
+ - lib/zoho_invoice/client.rb
113
+ - lib/zoho_invoice/configurable.rb
114
+ - lib/zoho_invoice/contact.rb
115
+ - lib/zoho_invoice/customer.rb
116
+ - lib/zoho_invoice/defaults.rb
117
+ - lib/zoho_invoice/error.rb
118
+ - lib/zoho_invoice/invoice.rb
119
+ - lib/zoho_invoice/invoice_item.rb
120
+ - lib/zoho_invoice/item.rb
121
+ - lib/zoho_invoice/version.rb
122
+ - spec/fixtures/500_internal_server_error
123
+ - spec/fixtures/incorrect_verb_used
124
+ - spec/fixtures/invalid_customer_id
125
+ - spec/fixtures/invalid_password
126
+ - spec/fixtures/invalid_url_passed
127
+ - spec/fixtures/invalid_user
128
+ - spec/fixtures/please_select_an_item
129
+ - spec/fixtures/successful_authtoken
130
+ - spec/spec_helper.rb
131
+ - spec/zoho_invoice/auth_token_spec.rb
132
+ - spec/zoho_invoice/base_spec.rb
133
+ - spec/zoho_invoice/client_spec.rb
134
+ - spec/zoho_invoice/contact_spec.rb
135
+ - spec/zoho_invoice_spec.rb
136
+ - zoho_invoice.gemspec
137
+ homepage: https://github.com/neovintage/zoho_invoice
138
+ licenses: []
139
+ post_install_message:
140
+ rdoc_options: []
141
+ require_paths:
142
+ - lib
143
+ required_ruby_version: !ruby/object:Gem::Requirement
144
+ none: false
145
+ requirements:
146
+ - - ! '>='
147
+ - !ruby/object:Gem::Version
148
+ version: 1.9.3
149
+ required_rubygems_version: !ruby/object:Gem::Requirement
150
+ none: false
151
+ requirements:
152
+ - - ! '>='
153
+ - !ruby/object:Gem::Version
154
+ version: 1.3.6
155
+ requirements: []
156
+ rubyforge_project:
157
+ rubygems_version: 1.8.21
158
+ signing_key:
159
+ specification_version: 3
160
+ summary: Zoho Invoice API
161
+ test_files:
162
+ - spec/fixtures/500_internal_server_error
163
+ - spec/fixtures/incorrect_verb_used
164
+ - spec/fixtures/invalid_customer_id
165
+ - spec/fixtures/invalid_password
166
+ - spec/fixtures/invalid_url_passed
167
+ - spec/fixtures/invalid_user
168
+ - spec/fixtures/please_select_an_item
169
+ - spec/fixtures/successful_authtoken
170
+ - spec/spec_helper.rb
171
+ - spec/zoho_invoice/auth_token_spec.rb
172
+ - spec/zoho_invoice/base_spec.rb
173
+ - spec/zoho_invoice/client_spec.rb
174
+ - spec/zoho_invoice/contact_spec.rb
175
+ - spec/zoho_invoice_spec.rb