alman 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.travis.yml +16 -0
  4. data/Gemfile +8 -0
  5. data/README.md +103 -0
  6. data/Rakefile +8 -0
  7. data/VERSION +1 -0
  8. data/alman.gemspec +29 -0
  9. data/bin/alman-console +7 -0
  10. data/gemfiles/default-with-activesupport.gemfile +10 -0
  11. data/gemfiles/json.gemfile +12 -0
  12. data/gemfiles/yajl.gemfile +12 -0
  13. data/lib/alman.rb +68 -0
  14. data/lib/alman/apibits/api_client.rb +28 -0
  15. data/lib/alman/apibits/api_endpoint.rb +11 -0
  16. data/lib/alman/apibits/api_list.rb +88 -0
  17. data/lib/alman/apibits/api_method.rb +95 -0
  18. data/lib/alman/apibits/api_object.rb +52 -0
  19. data/lib/alman/apibits/api_resource.rb +139 -0
  20. data/lib/alman/apibits/headers_builder.rb +47 -0
  21. data/lib/alman/apibits/params_builder.rb +27 -0
  22. data/lib/alman/apibits/path_builder.rb +38 -0
  23. data/lib/alman/apibits/requester.rb +104 -0
  24. data/lib/alman/apibits/util.rb +51 -0
  25. data/lib/alman/clients/default_client.rb +31 -0
  26. data/lib/alman/endpoints/bookings_endpoint.rb +36 -0
  27. data/lib/alman/endpoints/calendar_vacancies_endpoint.rb +35 -0
  28. data/lib/alman/endpoints/calendars_endpoint.rb +48 -0
  29. data/lib/alman/endpoints/vacancies_endpoint.rb +27 -0
  30. data/lib/alman/endpoints/vacancy_bookings_endpoint.rb +11 -0
  31. data/lib/alman/errors/alman_error.rb +13 -0
  32. data/lib/alman/errors/api_connection_error.rb +4 -0
  33. data/lib/alman/errors/api_error.rb +35 -0
  34. data/lib/alman/errors/authentication_error.rb +4 -0
  35. data/lib/alman/resources/booking.rb +62 -0
  36. data/lib/alman/resources/calendar.rb +65 -0
  37. data/lib/alman/resources/vacancy.rb +48 -0
  38. data/lib/alman/version.rb +3 -0
  39. data/test/alman/api_client_test.rb +51 -0
  40. data/test/alman/api_endpoint_test.rb +13 -0
  41. data/test/alman/api_list_test.rb +49 -0
  42. data/test/alman/api_method_test.rb +78 -0
  43. data/test/alman/headers_builder_test.rb +28 -0
  44. data/test/alman/params_builder_test.rb +57 -0
  45. data/test/alman/path_builder_test.rb +50 -0
  46. data/test/alman/requester_test.rb +86 -0
  47. data/test/alman/util_test.rb +51 -0
  48. data/test/test_data.rb +72 -0
  49. data/test/test_helper.rb +41 -0
  50. 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
@@ -0,0 +1,4 @@
1
+ pkg
2
+ Gemfile.lock
3
+ *.gem
4
+ .ruby-version
data/.travis.yml ADDED
@@ -0,0 +1,16 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 1.8.7
5
+ - 1.9.2
6
+ - 1.9.3
7
+ - 2.0.0
8
+ - 2.1
9
+ - 2.2
10
+
11
+ gemfile:
12
+ - gemfiles/default-with-activesupport.gemfile
13
+ - gemfiles/json.gemfile
14
+ - gemfiles/yajl.gemfile
15
+
16
+ sudo: false
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source "https://rubygems.org"
2
+ gemspec
3
+
4
+ if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('1.9.3')
5
+ gem 'i18n', '< 0.7'
6
+ gem 'rest-client', '~> 1.6.8'
7
+ gem 'activesupport', '~> 3.2'
8
+ end
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
@@ -0,0 +1,8 @@
1
+ require 'rake/testtask'
2
+ require 'yaml'
3
+
4
+ task :default => [:test]
5
+
6
+ Rake::TestTask.new do |t|
7
+ t.pattern = './test/**/[^_]*_test.rb'
8
+ end
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,7 @@
1
+ #!/usr/bin/env ruby
2
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
3
+
4
+ libs = " -r irb/completion"
5
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/alman'}"
6
+ puts "Loading Alman gem"
7
+ exec "#{irb} #{libs} --simple-prompt"
@@ -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,11 @@
1
+ module Alman
2
+ class ApiEndpoint
3
+ attr_accessor :client
4
+ attr_accessor :parent
5
+
6
+ def initialize(client, parent=nil)
7
+ @client = client
8
+ @parent = parent
9
+ end
10
+ end
11
+ 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