alman 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.travis.yml +16 -0
- data/Gemfile +8 -0
- data/README.md +103 -0
- data/Rakefile +8 -0
- data/VERSION +1 -0
- data/alman.gemspec +29 -0
- data/bin/alman-console +7 -0
- data/gemfiles/default-with-activesupport.gemfile +10 -0
- data/gemfiles/json.gemfile +12 -0
- data/gemfiles/yajl.gemfile +12 -0
- data/lib/alman.rb +68 -0
- data/lib/alman/apibits/api_client.rb +28 -0
- data/lib/alman/apibits/api_endpoint.rb +11 -0
- data/lib/alman/apibits/api_list.rb +88 -0
- data/lib/alman/apibits/api_method.rb +95 -0
- data/lib/alman/apibits/api_object.rb +52 -0
- data/lib/alman/apibits/api_resource.rb +139 -0
- data/lib/alman/apibits/headers_builder.rb +47 -0
- data/lib/alman/apibits/params_builder.rb +27 -0
- data/lib/alman/apibits/path_builder.rb +38 -0
- data/lib/alman/apibits/requester.rb +104 -0
- data/lib/alman/apibits/util.rb +51 -0
- data/lib/alman/clients/default_client.rb +31 -0
- data/lib/alman/endpoints/bookings_endpoint.rb +36 -0
- data/lib/alman/endpoints/calendar_vacancies_endpoint.rb +35 -0
- data/lib/alman/endpoints/calendars_endpoint.rb +48 -0
- data/lib/alman/endpoints/vacancies_endpoint.rb +27 -0
- data/lib/alman/endpoints/vacancy_bookings_endpoint.rb +11 -0
- data/lib/alman/errors/alman_error.rb +13 -0
- data/lib/alman/errors/api_connection_error.rb +4 -0
- data/lib/alman/errors/api_error.rb +35 -0
- data/lib/alman/errors/authentication_error.rb +4 -0
- data/lib/alman/resources/booking.rb +62 -0
- data/lib/alman/resources/calendar.rb +65 -0
- data/lib/alman/resources/vacancy.rb +48 -0
- data/lib/alman/version.rb +3 -0
- data/test/alman/api_client_test.rb +51 -0
- data/test/alman/api_endpoint_test.rb +13 -0
- data/test/alman/api_list_test.rb +49 -0
- data/test/alman/api_method_test.rb +78 -0
- data/test/alman/headers_builder_test.rb +28 -0
- data/test/alman/params_builder_test.rb +57 -0
- data/test/alman/path_builder_test.rb +50 -0
- data/test/alman/requester_test.rb +86 -0
- data/test/alman/util_test.rb +51 -0
- data/test/test_data.rb +72 -0
- data/test/test_helper.rb +41 -0
- metadata +208 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 01e914cb37b2e9dac560acb9ed5dc39b55eb5ace
|
4
|
+
data.tar.gz: 5913e8b2d5ede9e2d6a5dc2b386a92b8a98dfb00
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6223e1fbf869c47d18c7ce6f0115d043be7ca2ff220a8cf4423e8e6f3fb9aade4545302135135cc6d19f1ce86a154911303ba2f4abdda6a9f02492fab2ddffab
|
7
|
+
data.tar.gz: 2e18722a1195ce2f6ca9ab5ead648da353abca7defd619050508c85c6059f035803f5916b4f452fba159cfaa61a3b6d3debf6fc00459827a89abdcd03220bea3
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
# Alman Ruby Bindings
|
2
|
+
|
3
|
+
[![API Library via Apibits.com](http://apibits.com/assets/images/apibits-badge.png)](http://apibits.com)
|
4
|
+
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
You don't need this source code unless you want to modify the gem. If
|
9
|
+
you just want to use the Alman Ruby bindings, you should run:
|
10
|
+
|
11
|
+
```bash
|
12
|
+
gem install alman
|
13
|
+
```
|
14
|
+
|
15
|
+
If you want to build & install the gem from source:
|
16
|
+
|
17
|
+
```bash
|
18
|
+
gem build alman.gemspec
|
19
|
+
gem install alman-0.0.1.gem
|
20
|
+
```
|
21
|
+
|
22
|
+
## Documentation
|
23
|
+
|
24
|
+
Full documentation is available at [http://almanapi.com/docs](http://almanapi.com/docs).
|
25
|
+
|
26
|
+
|
27
|
+
## Requirements
|
28
|
+
|
29
|
+
* Ruby 1.8.7 or above. (Ruby 1.8.6 may work if you load
|
30
|
+
ActiveSupport.) For Ruby versions before 1.9.2, you'll need to add this to your Gemfile:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('1.9.2')
|
34
|
+
gem 'rest-client', '~> 1.6.8'
|
35
|
+
end
|
36
|
+
```
|
37
|
+
|
38
|
+
* rest-client, json
|
39
|
+
|
40
|
+
|
41
|
+
## Bundler
|
42
|
+
|
43
|
+
If you are installing via bundler, you should be sure to use the https
|
44
|
+
rubygems source in your Gemfile, as any gems fetched over http could potentially be compromised.
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
source 'https://rubygems.org'
|
48
|
+
|
49
|
+
gem 'rails'
|
50
|
+
gem 'alman'
|
51
|
+
```
|
52
|
+
|
53
|
+
## Using the library
|
54
|
+
|
55
|
+
The following section covers some general info about this API library. If you are looking for documentation covering the entire API, check here: [http://almanapi.com/docs](http://almanapi.com/docs).
|
56
|
+
|
57
|
+
### Params and Headers
|
58
|
+
|
59
|
+
All API methods accept 2 optional arguments - the params and headers. For example, if you had `SomeResource` and wanted to call `#some_method(some_arg)`, all of the following would be valid:
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
# With no custom params or headers
|
63
|
+
Alman::SomeResource.some_method(some_arg)
|
64
|
+
|
65
|
+
# With only custom params
|
66
|
+
Alman::SomeResource.some_method(some_arg, params)
|
67
|
+
|
68
|
+
# With only custom headers
|
69
|
+
Alman::SomeResource.some_method(some_arg, nil, headers)
|
70
|
+
|
71
|
+
# With both custom params and headers
|
72
|
+
Alman::SomeResource.some_method(some_arg, params, headers)
|
73
|
+
```
|
74
|
+
|
75
|
+
This is mostly useful when creating new resources, or when trying to use an API endpoint that supports pagination.
|
76
|
+
|
77
|
+
|
78
|
+
### Using Multiple API Keys
|
79
|
+
|
80
|
+
The API library was designed to support both developers who plan to only use one API key, as well as developers who may need to use multiple API keys in the same server.
|
81
|
+
|
82
|
+
By default most resource lookups use code like this:
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
Alman.api_key = "your-api-key"
|
86
|
+
test = Alman::Calendar.retrieve(id)
|
87
|
+
```
|
88
|
+
|
89
|
+
but since the API key is set on the module, this could cause issues if you need to use multiple API keys. To get around this, simply use one of the API clients located in the `clients` folder. For example:
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
client = Alman::DefaultClient.new("your-api-key")
|
93
|
+
test = client.calendars.retreive(id)
|
94
|
+
```
|
95
|
+
|
96
|
+
The results from each will be the same, so if you prefer the client version you are welcome to use it even if you aren't using multiple API keys in production.
|
97
|
+
|
98
|
+
|
99
|
+
## Development
|
100
|
+
|
101
|
+
Test cases can be run with: `bundle exec rake test`
|
102
|
+
|
103
|
+
We welcome suggestions and feedback from our users :D
|
data/Rakefile
ADDED
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/alman.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
$:.unshift(File.join(File.dirname(__FILE__), 'lib'))
|
2
|
+
|
3
|
+
require 'alman/version'
|
4
|
+
|
5
|
+
spec = Gem::Specification.new do |s|
|
6
|
+
s.name = 'alman'
|
7
|
+
s.summary = 'Ruby bindings for Alman API'
|
8
|
+
s.description = 'Alman is a calendar scheduling API.'
|
9
|
+
s.homepage = 'http://almanapi.com/docs'
|
10
|
+
s.authors = ['Apibits.com']
|
11
|
+
s.email = ['libraries@apibits.com']
|
12
|
+
s.version = Alman::VERSION
|
13
|
+
s.license = 'MIT'
|
14
|
+
|
15
|
+
s.add_dependency('rest-client', '~> 1.4')
|
16
|
+
s.add_dependency('mime-types', '>= 1.25', '< 3.0')
|
17
|
+
s.add_dependency('json', '~> 1.8.1')
|
18
|
+
|
19
|
+
s.add_development_dependency('mocha', '~> 0.13.2')
|
20
|
+
s.add_development_dependency('shoulda', '~> 3.4.0')
|
21
|
+
s.add_development_dependency('test-unit')
|
22
|
+
s.add_development_dependency('rake')
|
23
|
+
|
24
|
+
s.files = `git ls-files`.split("\n")
|
25
|
+
s.test_files = `git ls-files -- test/*`.split("\n")
|
26
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
27
|
+
s.require_paths = ['lib']
|
28
|
+
end
|
29
|
+
|
data/bin/alman-console
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
gemspec :path => File.join(File.dirname(__FILE__), "..")
|
3
|
+
|
4
|
+
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('1.9.3')
|
5
|
+
gem 'i18n', '< 0.7'
|
6
|
+
gem 'rest-client', '~> 1.6.8'
|
7
|
+
gem 'activesupport', '~> 3.2'
|
8
|
+
else
|
9
|
+
gem 'activesupport'
|
10
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
gemspec :path => File.join(File.dirname(__FILE__), "..")
|
3
|
+
|
4
|
+
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('1.9.3')
|
5
|
+
gem 'i18n', '< 0.7'
|
6
|
+
gem 'rest-client', '~> 1.6.8'
|
7
|
+
gem 'activesupport', '~> 3.2'
|
8
|
+
else
|
9
|
+
gem 'activesupport'
|
10
|
+
end
|
11
|
+
|
12
|
+
gem 'json'
|
@@ -0,0 +1,12 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
gemspec :path => File.join(File.dirname(__FILE__), "..")
|
3
|
+
|
4
|
+
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('1.9.3')
|
5
|
+
gem 'i18n', '< 0.7'
|
6
|
+
gem 'rest-client', '~> 1.6.8'
|
7
|
+
gem 'activesupport', '~> 3.2'
|
8
|
+
else
|
9
|
+
gem 'activesupport'
|
10
|
+
end
|
11
|
+
|
12
|
+
gem 'yajl-ruby'
|
data/lib/alman.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
# Alman Ruby bindings
|
2
|
+
# API Docs are located at http://almanapi.com/docs
|
3
|
+
require 'cgi'
|
4
|
+
require 'set'
|
5
|
+
require 'openssl'
|
6
|
+
require 'rest_client'
|
7
|
+
require 'json'
|
8
|
+
require 'base64'
|
9
|
+
|
10
|
+
# Version
|
11
|
+
require 'alman/version'
|
12
|
+
|
13
|
+
# Errors
|
14
|
+
require 'alman/errors/alman_error'
|
15
|
+
require 'alman/errors/api_error'
|
16
|
+
require 'alman/errors/api_connection_error'
|
17
|
+
require 'alman/errors/authentication_error'
|
18
|
+
|
19
|
+
# Wrapper around RestClient
|
20
|
+
require 'alman/apibits/requester'
|
21
|
+
|
22
|
+
# Builders for creating API methods.
|
23
|
+
require 'alman/apibits/path_builder'
|
24
|
+
require 'alman/apibits/headers_builder'
|
25
|
+
require 'alman/apibits/params_builder'
|
26
|
+
require 'alman/apibits/api_method'
|
27
|
+
|
28
|
+
# Generic resources
|
29
|
+
require 'alman/apibits/api_endpoint'
|
30
|
+
require 'alman/apibits/api_client'
|
31
|
+
require 'alman/apibits/api_object'
|
32
|
+
require 'alman/apibits/api_resource'
|
33
|
+
require 'alman/apibits/api_list'
|
34
|
+
require 'alman/apibits/util'
|
35
|
+
|
36
|
+
# API specific resources
|
37
|
+
require 'alman/resources/calendar'
|
38
|
+
require 'alman/resources/vacancy'
|
39
|
+
require 'alman/resources/booking'
|
40
|
+
|
41
|
+
# API specific endpoints
|
42
|
+
require 'alman/endpoints/calendars_endpoint'
|
43
|
+
require 'alman/endpoints/calendar_vacancies_endpoint'
|
44
|
+
require 'alman/endpoints/vacancies_endpoint'
|
45
|
+
require 'alman/endpoints/vacancy_bookings_endpoint'
|
46
|
+
require 'alman/endpoints/bookings_endpoint'
|
47
|
+
|
48
|
+
# API specific clients
|
49
|
+
require 'alman/clients/default_client'
|
50
|
+
|
51
|
+
|
52
|
+
module Alman
|
53
|
+
@api_base = "http://almanapi.com/api/v0"
|
54
|
+
@api_staging = ""
|
55
|
+
@api_version = "v0"
|
56
|
+
@support_email = "support@almanapi.com"
|
57
|
+
@docs_url = "http://almanapi.com/docs"
|
58
|
+
|
59
|
+
class << self
|
60
|
+
attr_accessor :api_base, :api_version
|
61
|
+
attr_reader :api_staging, :support_email, :docs_url
|
62
|
+
attr_accessor :api_key
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.default_client
|
66
|
+
DefaultClient.new(Alman.api_key)
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Alman
|
2
|
+
class ApiClient
|
3
|
+
attr_accessor :headers, :params
|
4
|
+
|
5
|
+
def initialize(headers, params)
|
6
|
+
self.refresh_from(headers, params)
|
7
|
+
end
|
8
|
+
|
9
|
+
def refresh_from(headers, params)
|
10
|
+
@headers = headers
|
11
|
+
@params = params
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
def execute(api_method)
|
16
|
+
api_method.headers = ParamsBuilder.merge(api_method.headers, @headers)
|
17
|
+
api_method.params = ParamsBuilder.merge(api_method.params, @params)
|
18
|
+
api_method.execute
|
19
|
+
end
|
20
|
+
|
21
|
+
def inspect
|
22
|
+
"#<#{self.class}:0x#{self.object_id.to_s(16)}> Headers: " +
|
23
|
+
JSON.pretty_generate(@headers) + ", Params: " +
|
24
|
+
JSON.pretty_generate(@params)
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Alman
|
2
|
+
class ApiList < ApiResource
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
attr_reader :api_method
|
6
|
+
attr_reader :client
|
7
|
+
attr_reader :data
|
8
|
+
attr_reader :klass
|
9
|
+
|
10
|
+
def initialize(klass, json={}, api_method=nil, client=nil)
|
11
|
+
if klass.is_a?(Class)
|
12
|
+
@klass = klass
|
13
|
+
else
|
14
|
+
@klass = Util.constantize(klass)
|
15
|
+
end
|
16
|
+
|
17
|
+
refresh_from(json, api_method, client)
|
18
|
+
end
|
19
|
+
|
20
|
+
def refresh_from(json, api_method=nil, client=nil)
|
21
|
+
unless json.is_a?(Hash)
|
22
|
+
json = {
|
23
|
+
:data => json
|
24
|
+
}
|
25
|
+
end
|
26
|
+
json = Util.symbolize_keys(json)
|
27
|
+
|
28
|
+
clear_api_attributes
|
29
|
+
@api_method = api_method
|
30
|
+
@client = client
|
31
|
+
@data = []
|
32
|
+
@json = Util.sorta_deep_clone(json)
|
33
|
+
|
34
|
+
json.each do |k, v|
|
35
|
+
if k.to_sym == :data
|
36
|
+
if v.respond_to?(:map)
|
37
|
+
instance_variable_set("@#{k}", v.map{ |i| @klass.new(i, @api_method, @client) })
|
38
|
+
else
|
39
|
+
instance_variable_set("@#{k}", v || [])
|
40
|
+
end
|
41
|
+
elsif self.class.api_attribute_names.include?(k)
|
42
|
+
instance_variable_set("@#{k}", determine_api_attribute_value(k, v))
|
43
|
+
end
|
44
|
+
end
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
def [](k)
|
49
|
+
data[k]
|
50
|
+
end
|
51
|
+
|
52
|
+
def []=(k, v)
|
53
|
+
data[k]=v
|
54
|
+
end
|
55
|
+
|
56
|
+
def last
|
57
|
+
data.last
|
58
|
+
end
|
59
|
+
|
60
|
+
def length
|
61
|
+
data.length
|
62
|
+
end
|
63
|
+
|
64
|
+
def each(&blk)
|
65
|
+
data.each(&blk)
|
66
|
+
end
|
67
|
+
|
68
|
+
def inspect
|
69
|
+
"#<#{self.class}[#{@klass}]:0x#{self.object_id.to_s(16)}> Data: " + JSON.pretty_generate(inspect_data)
|
70
|
+
end
|
71
|
+
|
72
|
+
def inspect_data
|
73
|
+
ret = []
|
74
|
+
data.each do |d|
|
75
|
+
if d.respond_to?(:inspect_nested)
|
76
|
+
ret << d.inspect_nested
|
77
|
+
else
|
78
|
+
ret << d
|
79
|
+
end
|
80
|
+
end
|
81
|
+
ret
|
82
|
+
end
|
83
|
+
|
84
|
+
@api_attributes = {
|
85
|
+
:data => { :readonly => true }
|
86
|
+
}
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Alman
|
2
|
+
class ApiMethod
|
3
|
+
|
4
|
+
attr_accessor :path
|
5
|
+
attr_accessor :method
|
6
|
+
attr_accessor :params
|
7
|
+
attr_accessor :headers
|
8
|
+
|
9
|
+
attr_accessor :response_body
|
10
|
+
attr_accessor :response_code
|
11
|
+
attr_accessor :error
|
12
|
+
|
13
|
+
attr_accessor :api_base
|
14
|
+
|
15
|
+
def initialize(method, path, params, headers, object)
|
16
|
+
@api_base = api_base || Alman.api_base
|
17
|
+
|
18
|
+
@method = method.to_sym
|
19
|
+
@path = PathBuilder.build(path, object, params)
|
20
|
+
@params = ParamsBuilder.build(params)
|
21
|
+
@headers = HeadersBuilder.build(headers)
|
22
|
+
end
|
23
|
+
|
24
|
+
def execute
|
25
|
+
begin
|
26
|
+
response = Requester.request(method, url, params, headers)
|
27
|
+
@response_body = response.body
|
28
|
+
@response_code = response.code
|
29
|
+
rescue StandardError => e
|
30
|
+
@response_body = e.http_body if e.respond_to?(:http_body)
|
31
|
+
@response_code = e.http_code if e.respond_to?(:http_code)
|
32
|
+
@error = compose_error(e)
|
33
|
+
raise @error
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
response_json
|
38
|
+
end
|
39
|
+
|
40
|
+
def url
|
41
|
+
"#{api_base}#{@path}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def response_json
|
45
|
+
return @json if @json
|
46
|
+
begin
|
47
|
+
@json = Util.symbolize_keys(JSON.parse(@response_body))
|
48
|
+
rescue JSON::ParserError
|
49
|
+
@error = ApiError.new("Unable to parse the server response as JSON.", self)
|
50
|
+
raise @error
|
51
|
+
end
|
52
|
+
@json
|
53
|
+
end
|
54
|
+
|
55
|
+
def compose_error(error)
|
56
|
+
msg = "An error occured while making the API call."
|
57
|
+
|
58
|
+
case error
|
59
|
+
when RestClient::ExceptionWithResponse
|
60
|
+
return error_with_response(error)
|
61
|
+
|
62
|
+
when RestClient::RequestTimeout
|
63
|
+
msg = "The request timed out while making the API call."
|
64
|
+
|
65
|
+
when RestClient::ServerBrokeConnection
|
66
|
+
msg = "The connection to the server broke before the request completed."
|
67
|
+
|
68
|
+
when SocketError
|
69
|
+
msg = "An unexpected error occured while trying to connect to " \
|
70
|
+
"the API. You may be seeing this message because your DNS is " \
|
71
|
+
"not working. To check, try running 'host #{Alman.api_base}' "\
|
72
|
+
"from the command line."
|
73
|
+
|
74
|
+
else
|
75
|
+
msg = "An unexpected error occured. If this problem persists let us " \
|
76
|
+
"know at #{Alman.support_email}."
|
77
|
+
end
|
78
|
+
|
79
|
+
return ApiConnectionError.new(msg, self)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Handle a few common cases.
|
83
|
+
def error_with_response(error)
|
84
|
+
case @response_code
|
85
|
+
when 400, 404
|
86
|
+
return ApiError.new(@response_body || "Invalid request. Please check the URL and parameters.", self)
|
87
|
+
when 401
|
88
|
+
return AuthenticationError.new(@response_body || "Authentication failed.", self)
|
89
|
+
else
|
90
|
+
return ApiError.new(@response_body || "An error occured while making the API call.", self)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|