zoho_invoice 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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