gemagent 0.0.1.pre
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/CONTRIBUTORS +1 -0
- data/HACKING +8 -0
- data/LICENCE +19 -0
- data/README.md +43 -0
- data/example/example.rb +13 -0
- data/gemagent.gemspec +25 -0
- data/gemagent.rb +8 -0
- data/lib/api_classes.rb +36 -0
- data/lib/api_handler.rb +28 -0
- data/lib/authentication.rb +23 -0
- data/lib/connection.rb +116 -0
- metadata +92 -0
data/CONTRIBUTORS
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Ryan Stenhouse <ryan@ryanstenhouse.eu>
|
data/HACKING
ADDED
data/LICENCE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2011 Ryan Stenhouse and CONTRIBUTORS
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
Gemagent
|
2
|
+
========
|
3
|
+
|
4
|
+
By Ryan Stenhouse http://ryanstenhouse.eu
|
5
|
+
|
6
|
+
What is it?
|
7
|
+
-----------
|
8
|
+
|
9
|
+
Gemagent is a purely Standard Ruby implementation of a consumer of
|
10
|
+
the FreeAgent Central API.
|
11
|
+
|
12
|
+
The goal of this project was not to build a 'fast' API consumer, but
|
13
|
+
instead to build a 'correct' one. All published API features of the
|
14
|
+
FreeAgent application are implemented.
|
15
|
+
|
16
|
+
If it's fast, it's a bonus!
|
17
|
+
|
18
|
+
Installation
|
19
|
+
------------
|
20
|
+
|
21
|
+
Install via rubygems:
|
22
|
+
|
23
|
+
$ gem install gemagent
|
24
|
+
|
25
|
+
It requires OpenSSL in order to create the HTTPS connection to the
|
26
|
+
FreeAgent API.
|
27
|
+
|
28
|
+
Extension
|
29
|
+
---------
|
30
|
+
|
31
|
+
The main purpose of this gem was to allow me to become more familiar
|
32
|
+
with the FreeAgent API and to shake out some cobwebs when it came to
|
33
|
+
my knowledge of Ruby's Standard Library.
|
34
|
+
|
35
|
+
Because of this, some parts (like local data storage) are sub-optimal.
|
36
|
+
|
37
|
+
I have, however, build some abstraction on top of the data storage,
|
38
|
+
HTTP requests and XML parsing to allow for some or all of these
|
39
|
+
components to be replaced at a later date.
|
40
|
+
|
41
|
+
See the documentation online for more information.
|
42
|
+
|
43
|
+
|
data/example/example.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# GemAgent API Interface
|
2
|
+
# Ryan Stenhouse, 20 January 2011
|
3
|
+
|
4
|
+
# Longhand representation of connecting to the FreeAgent API
|
5
|
+
#
|
6
|
+
ga = GemAgent::NetHttpConnection.new 'you.freeagentcentral.com', GemAgent::HttpBasicAuthentication.new('username','password')
|
7
|
+
ga.verify # => { :user_id => 1, :permission_level => 8, :company_type => UK_LIMITED }
|
8
|
+
|
9
|
+
# Sexy sugary version of the above.
|
10
|
+
#
|
11
|
+
GemAgent::Api.connect 'you.freeagentcentral.com', 'username', 'password' do |api|
|
12
|
+
api.verify # => { :user_id => 1, :permission_level => 8, :company_type => UK_LIMITED }
|
13
|
+
end
|
data/gemagent.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
summary = "GemAgent is a clean and light way to interact with the FreeAgent API. "
|
2
|
+
description = <<-TXT
|
3
|
+
GemAgent lets users of FreeAgent Central's online accounting software
|
4
|
+
to interact with the API simply and cleanly by only using functionality
|
5
|
+
provided by Ruby's Standard Library.
|
6
|
+
TXT
|
7
|
+
|
8
|
+
Gem::Specification.new do |spec|
|
9
|
+
spec.name = "gemagent"
|
10
|
+
spec.version = '0.0.1.pre'
|
11
|
+
spec.platform = Gem::Platform::RUBY
|
12
|
+
spec.files = Dir.glob("{bin,lib,.}/**/**/*") +
|
13
|
+
["gemagent.rb","README.md","HACKING",'LICENCE','CONTRIBUTORS', "gemagent.gemspec"]
|
14
|
+
spec.require_path = "lib"
|
15
|
+
spec.required_ruby_version = '>= 1.8.4'
|
16
|
+
spec.required_rubygems_version = ">= 1.3.0"
|
17
|
+
spec.test_files = Dir[ "test/*_test.rb" ]
|
18
|
+
spec.has_rdoc = false
|
19
|
+
spec.author = "Ryan Stenhouse"
|
20
|
+
spec.email = "ryan@ryanstenhouse.eu"
|
21
|
+
spec.rubyforge_project = "gemagemt"
|
22
|
+
spec.homepage = "http://ryanstenhouse.eu"
|
23
|
+
spec.summary = summary
|
24
|
+
spec.description = description
|
25
|
+
end
|
data/gemagent.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
GEMAGENT_ROOT = File.expand_path(File.dirname(__FILE__))
|
2
|
+
require GEMAGENT_ROOT + '/lib/connection.rb'
|
3
|
+
require GEMAGENT_ROOT + '/lib/authentication.rb'
|
4
|
+
require GEMAGENT_ROOT + '/lib/api_classes.rb'
|
5
|
+
require GEMAGENT_ROOT + '/lib/api_handler.rb'
|
6
|
+
|
7
|
+
module GemAgent
|
8
|
+
end
|
data/lib/api_classes.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require "rexml/document"
|
2
|
+
|
3
|
+
module GemAgent
|
4
|
+
module Api
|
5
|
+
|
6
|
+
def self.connect(freeagent_url, username, password, connection = GemAgent::NetHttpConnection,
|
7
|
+
authenticator = GemAgent::HttpBasicAuthentication,
|
8
|
+
api_version = GemAgent::ApiHandler)
|
9
|
+
if !block_given?
|
10
|
+
raise "I need to have a block" and return
|
11
|
+
end
|
12
|
+
api = connection.new(freeagent_url, authenticator.new(username, password), api_version)
|
13
|
+
yield api
|
14
|
+
end
|
15
|
+
|
16
|
+
class ApiBase
|
17
|
+
def method
|
18
|
+
'GET'
|
19
|
+
end
|
20
|
+
|
21
|
+
def path
|
22
|
+
end
|
23
|
+
|
24
|
+
def expected_status
|
25
|
+
'200'
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
class Verify < ApiBase
|
31
|
+
def path
|
32
|
+
'/verify'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/api_handler.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/api_classes.rb'
|
2
|
+
module GemAgent
|
3
|
+
class ApiHandler
|
4
|
+
|
5
|
+
def initialize(connection)
|
6
|
+
@connection = connection
|
7
|
+
end
|
8
|
+
|
9
|
+
def verify
|
10
|
+
verify = GemAgent::Api::Verify.new
|
11
|
+
response = @connection.send_request(verify)
|
12
|
+
unless response.has_error?
|
13
|
+
{
|
14
|
+
:company_type => response.headers['company-type'],
|
15
|
+
:user_id => response.headers['user-id'].to_i,
|
16
|
+
:permission_level => response.headers['user-permission-level'].to_i
|
17
|
+
}
|
18
|
+
else
|
19
|
+
{
|
20
|
+
:error => response.headers[:error],
|
21
|
+
:body => response.body,
|
22
|
+
:company_type => 'ERROR'
|
23
|
+
}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module GemAgent
|
2
|
+
class ApiAuthentication
|
3
|
+
def authenticate_for(obj)
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
class HttpBasicAuthentication < ApiAuthentication
|
8
|
+
|
9
|
+
def initialize(username, password)
|
10
|
+
@username = username
|
11
|
+
@password = password
|
12
|
+
end
|
13
|
+
|
14
|
+
def authenticate_for(obj)
|
15
|
+
if obj.respond_to?(:basic_auth)
|
16
|
+
obj.basic_auth @username, @password
|
17
|
+
else
|
18
|
+
raise "Can't do basic authentication with this, I expect a NetHTTP request"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
data/lib/connection.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'net/https'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module GemAgent
|
5
|
+
|
6
|
+
# ConnectionMethod implements the basic interface all child
|
7
|
+
# classes must implement to allow connection to the FreeAgent
|
8
|
+
# API.
|
9
|
+
#
|
10
|
+
class ConnectionMethod
|
11
|
+
|
12
|
+
attr_reader :freeagent_url
|
13
|
+
attr_reader :api
|
14
|
+
attr_reader :authenticator
|
15
|
+
|
16
|
+
@@method_map = {}
|
17
|
+
|
18
|
+
def initialize(freeagent_url, authenticator, api_handler = GemAgent::ApiHandler)
|
19
|
+
@freeagent_url = freeagent_url
|
20
|
+
@authenticator = authenticator
|
21
|
+
@api = api_handler.new(self)
|
22
|
+
end
|
23
|
+
|
24
|
+
def send_request(request)
|
25
|
+
raise "Subclasses of ConnectionMethod must implement their own send_request method"
|
26
|
+
end
|
27
|
+
|
28
|
+
def wrap_response(response)
|
29
|
+
raise "Subclasses of ConnectionMethod must implement their own wrap_response method"
|
30
|
+
end
|
31
|
+
|
32
|
+
# Proxies all undefined method to the API handler
|
33
|
+
# associated with the connection.
|
34
|
+
#
|
35
|
+
def method_missing(method, *args)
|
36
|
+
unless args.empty?
|
37
|
+
@api.send(method, args)
|
38
|
+
else
|
39
|
+
@api.send(method)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# The ResponseWrapper is used to present a consistant way
|
45
|
+
# for the various API classes to process messages returned
|
46
|
+
# by the API server.
|
47
|
+
#
|
48
|
+
# If you write your own ConnectionMethod subclass, ensure that
|
49
|
+
# your send_request method returns a ResponseWrapper correctly
|
50
|
+
# set with the correct values for headers and body.
|
51
|
+
#
|
52
|
+
class ResponseWrapper
|
53
|
+
attr_reader :headers, :body
|
54
|
+
def initialize(headers, body)
|
55
|
+
@headers = headers
|
56
|
+
@body = body
|
57
|
+
end
|
58
|
+
|
59
|
+
def has_error?
|
60
|
+
!headers[:error].nil?
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# NetHttpConnection is the default method to connect to the
|
65
|
+
# FreeAgent API. It uses the Standard Ruby library net/http
|
66
|
+
# to create an SSL connection to the FreeAgent server specified
|
67
|
+
# by +@freeagent_url+ and authenticates your request with the
|
68
|
+
# ApiAuthentication subclass you pass thorugh as +authenticator+.
|
69
|
+
#
|
70
|
+
# The last last optional value takes a API Handler class, which
|
71
|
+
# defaults to the standard one which ships with this gem.
|
72
|
+
#
|
73
|
+
class NetHttpConnection < ConnectionMethod
|
74
|
+
|
75
|
+
@@method_map = {
|
76
|
+
'GET' => Net::HTTP::Get,
|
77
|
+
'POST' => Net::HTTP::Post,
|
78
|
+
'PUT' => Net::HTTP::Put,
|
79
|
+
'DELETE' => Net::HTTP::Delete
|
80
|
+
}
|
81
|
+
|
82
|
+
# Sends the +request+ to +@freeagent_url+ over HTTPS. The class
|
83
|
+
# instance variable +@@method_map+ maps each HTTP verb required
|
84
|
+
# to the correct class for instantiation.
|
85
|
+
#
|
86
|
+
def send_request(request)
|
87
|
+
https = Net::HTTP.new(@freeagent_url, Net::HTTP.https_default_port)
|
88
|
+
https.use_ssl = true
|
89
|
+
https.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
90
|
+
req = @@method_map[request.method].new(request.path)
|
91
|
+
req['content-type'] = 'application/xml'
|
92
|
+
req['accept'] = 'application/xml'
|
93
|
+
authenticator.authenticate_for req
|
94
|
+
resp = nil
|
95
|
+
https.start do |http|
|
96
|
+
resp = http.request(req)
|
97
|
+
end
|
98
|
+
if resp.code == request.expected_status
|
99
|
+
wrap_response(resp)
|
100
|
+
else
|
101
|
+
ResponseWrapper.new({ :error => resp.code, :status => resp.code }, resp.body)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Wraps the response from net/http in a ResponseWrapper object
|
106
|
+
# so the API classes are able to work with it to do their work.
|
107
|
+
#
|
108
|
+
def wrap_response(response)
|
109
|
+
headers = {}
|
110
|
+
response.each_header { |k,v| headers[k] = v }
|
111
|
+
ResponseWrapper.new(headers, response.body)
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gemagent
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 961915968
|
5
|
+
prerelease: 6
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
- pre
|
11
|
+
version: 0.0.1.pre
|
12
|
+
platform: ruby
|
13
|
+
authors:
|
14
|
+
- Ryan Stenhouse
|
15
|
+
autorequire:
|
16
|
+
bindir: bin
|
17
|
+
cert_chain: []
|
18
|
+
|
19
|
+
date: 2011-04-29 00:00:00 +01:00
|
20
|
+
default_executable:
|
21
|
+
dependencies: []
|
22
|
+
|
23
|
+
description: " GemAgent lets users of FreeAgent Central's online accounting software\n to interact with the API simply and cleanly by only using functionality\n provided by Ruby's Standard Library.\n"
|
24
|
+
email: ryan@ryanstenhouse.eu
|
25
|
+
executables: []
|
26
|
+
|
27
|
+
extensions: []
|
28
|
+
|
29
|
+
extra_rdoc_files: []
|
30
|
+
|
31
|
+
files:
|
32
|
+
- lib/api_classes.rb
|
33
|
+
- lib/api_handler.rb
|
34
|
+
- lib/authentication.rb
|
35
|
+
- lib/connection.rb
|
36
|
+
- ./CONTRIBUTORS
|
37
|
+
- ./example/example.rb
|
38
|
+
- ./gemagent.gemspec
|
39
|
+
- ./gemagent.rb
|
40
|
+
- ./HACKING
|
41
|
+
- ./lib/api_classes.rb
|
42
|
+
- ./lib/api_handler.rb
|
43
|
+
- ./lib/authentication.rb
|
44
|
+
- ./lib/connection.rb
|
45
|
+
- ./LICENCE
|
46
|
+
- ./README.md
|
47
|
+
- gemagent.rb
|
48
|
+
- README.md
|
49
|
+
- HACKING
|
50
|
+
- LICENCE
|
51
|
+
- CONTRIBUTORS
|
52
|
+
- gemagent.gemspec
|
53
|
+
has_rdoc: true
|
54
|
+
homepage: http://ryanstenhouse.eu
|
55
|
+
licenses: []
|
56
|
+
|
57
|
+
post_install_message:
|
58
|
+
rdoc_options: []
|
59
|
+
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
hash: 63
|
68
|
+
segments:
|
69
|
+
- 1
|
70
|
+
- 8
|
71
|
+
- 4
|
72
|
+
version: 1.8.4
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
hash: 27
|
79
|
+
segments:
|
80
|
+
- 1
|
81
|
+
- 3
|
82
|
+
- 0
|
83
|
+
version: 1.3.0
|
84
|
+
requirements: []
|
85
|
+
|
86
|
+
rubyforge_project: gemagemt
|
87
|
+
rubygems_version: 1.6.1
|
88
|
+
signing_key:
|
89
|
+
specification_version: 3
|
90
|
+
summary: GemAgent is a clean and light way to interact with the FreeAgent API.
|
91
|
+
test_files: []
|
92
|
+
|