kegbot_api 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.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +24 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +9 -0
  5. data/.yardopts +1 -0
  6. data/CHANGELOG.md +18 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +178 -0
  10. data/Rakefile +1 -0
  11. data/kegbot_api.gemspec +30 -0
  12. data/lib/kegbot_api.rb +7 -0
  13. data/lib/kegbot_api/client.rb +39 -0
  14. data/lib/kegbot_api/client_helpers.rb +33 -0
  15. data/lib/kegbot_api/errors.rb +17 -0
  16. data/lib/kegbot_api/nouns/beverage.rb +23 -0
  17. data/lib/kegbot_api/nouns/drink.rb +32 -0
  18. data/lib/kegbot_api/nouns/image.rb +22 -0
  19. data/lib/kegbot_api/nouns/keg.rb +45 -0
  20. data/lib/kegbot_api/nouns/keg_size.rb +21 -0
  21. data/lib/kegbot_api/nouns/remote_rest_noun.rb +49 -0
  22. data/lib/kegbot_api/nouns/rest_noun.rb +36 -0
  23. data/lib/kegbot_api/nouns/rest_noun_attributes.rb +119 -0
  24. data/lib/kegbot_api/nouns/session.rb +32 -0
  25. data/lib/kegbot_api/nouns/tap.rb +31 -0
  26. data/lib/kegbot_api/rest_response.rb +76 -0
  27. data/lib/kegbot_api/version.rb +3 -0
  28. data/spec/beverage_spec.rb +44 -0
  29. data/spec/drink_spec.rb +88 -0
  30. data/spec/drinks.http +5416 -0
  31. data/spec/drinks_614.http +68 -0
  32. data/spec/drinks_999999.http +18 -0
  33. data/spec/example_1.rb +40 -0
  34. data/spec/example_2.rb +28 -0
  35. data/spec/examples_spec.rb +37 -0
  36. data/spec/image_spec.rb +33 -0
  37. data/spec/keg_spec.rb +131 -0
  38. data/spec/kegs.http +144 -0
  39. data/spec/kegs_4.http +46 -0
  40. data/spec/kegs_999999.http +18 -0
  41. data/spec/session_spec.rb +73 -0
  42. data/spec/sessions.http +601 -0
  43. data/spec/sessions_65.http +23 -0
  44. data/spec/sessions_999999.http +18 -0
  45. data/spec/spec_helper.rb +34 -0
  46. data/spec/tap_spec.rb +134 -0
  47. data/spec/taps.http +96 -0
  48. metadata +209 -0
@@ -0,0 +1,22 @@
1
+ require 'kegbot_api/nouns/rest_noun'
2
+
3
+ module KegbotApi
4
+ class Image < RestNoun
5
+ id :id
6
+ time :time
7
+ url :url
8
+ url :thumbnail_url
9
+
10
+ def to_s
11
+ "#<#{self.class.to_s}:#{"0x%x" % object_id} @id=#{self.id.inspect}>"
12
+ end
13
+
14
+ def self.to_s
15
+ self.client ? "KegbotApi::Image<#{self.client.base_url}>" : super
16
+ end
17
+
18
+ def self.inspect
19
+ to_s
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,45 @@
1
+ require 'kegbot_api/nouns/remote_rest_noun'
2
+
3
+ module KegbotApi
4
+ class Keg < RemoteRestNoun
5
+ extend Forwardable
6
+
7
+ id :id
8
+ float :percent_full
9
+ float :volume_ml_remain, :alias => :volume_ml_remaining
10
+ float :spilled_ml
11
+ time :start_time
12
+ time :end_time
13
+ boolean :online
14
+ has_one :beverage, :attribute_name => :type, :alias => :type
15
+ has_one :size, :class_name => 'KegSize'
16
+
17
+ def_delegators :beverage, :name
18
+ def_delegators :size, :volume_ml
19
+
20
+
21
+ def size_name
22
+ self.size.name
23
+ end
24
+
25
+ def self.all
26
+ list(get("#{self.client.base_url}/kegs"))
27
+ end
28
+
29
+ def self.find(id, *args)
30
+ one(get("#{self.client.base_url}/kegs/#{id}"))
31
+ end
32
+
33
+ def to_s
34
+ "#<#{self.class.to_s}:#{"0x%x" % object_id} @id=#{self.id.inspect}>"
35
+ end
36
+
37
+ def self.to_s
38
+ self.client ? "KegbotApi::Keg<#{self.client.base_url}>" : super
39
+ end
40
+
41
+ def self.inspect
42
+ to_s
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,21 @@
1
+ require 'kegbot_api/nouns/rest_noun'
2
+
3
+ module KegbotApi
4
+ class KegSize < RestNoun
5
+ id :id
6
+ string :name
7
+ float :volume_ml
8
+
9
+ def to_s
10
+ "#<#{self.class.to_s}:#{"0x%x" % object_id} @id=#{self.id.inspect}>"
11
+ end
12
+
13
+ def self.to_s
14
+ self.client ? "KegbotApi::KegSize<#{self.client.base_url}>" : super
15
+ end
16
+
17
+ def self.inspect
18
+ to_s
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,49 @@
1
+ require 'active_support/concern'
2
+ require 'kegbot_api/nouns/rest_noun'
3
+
4
+ module KegbotApi
5
+ # Rest noun that supports remote access via {#all} and {#find}
6
+ class RemoteRestNoun < RestNoun
7
+
8
+ #
9
+ # Class Methods
10
+ #
11
+
12
+ class << self
13
+ def list(response)
14
+ if response.success?
15
+ response.objects.map { |hash| self.new hash }
16
+ else
17
+ raise *response.raise_arguments
18
+ end
19
+ end
20
+
21
+ def one(response)
22
+ if response.success?
23
+ self.new response.object
24
+ else
25
+ raise *response.raise_arguments
26
+ end
27
+ end
28
+
29
+ def first
30
+ all.first
31
+ end
32
+
33
+ def get(url, options = {})
34
+ uri = URI(url)
35
+
36
+ Net::HTTP.start(uri.host, uri.port, :use_ssl => (uri.scheme == 'https')) do |http|
37
+ http_response = http.request_get uri.request_uri
38
+
39
+ case http_response
40
+ when Net::HTTPRedirection
41
+ raise NotImplementedError, "HTTP redirecting isn't supported"
42
+ else
43
+ RestResponse.new http_response
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,36 @@
1
+ require 'net/https'
2
+ require 'kegbot_api/rest_response'
3
+ require 'kegbot_api/nouns/rest_noun_attributes'
4
+
5
+ module KegbotApi
6
+ # Base class of all REST nouns (types) in the Kegbot REST API.
7
+ class RestNoun
8
+ include RestNounAttributes
9
+
10
+ def initialize(attributes = {})
11
+ self.attributes = attributes
12
+ end
13
+
14
+ # Returns a {Client} client configured for this class
15
+ # @return [Client]
16
+ def client
17
+ self.class.client
18
+ end
19
+
20
+ # Class Methods
21
+
22
+ class << self
23
+ # @return [Client]
24
+ attr_accessor :client
25
+ attr_accessor :noun
26
+
27
+ def client=(client)
28
+ @client = client
29
+ end
30
+
31
+ def client
32
+ @client
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,119 @@
1
+ require 'active_support/concern'
2
+
3
+ module KegbotApi
4
+ # Provides methods for specifying, storing, and retriving REST noun attributes and relationships.
5
+ # @see RestNoun
6
+ module RestNounAttributes
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ attr_accessor :attributes
11
+ end
12
+
13
+ module ClassMethods
14
+ # Defines an id field for this REST noun
15
+ def id(name, *args)
16
+ define_accessor name
17
+ end
18
+
19
+ # Defines a {String} field for this REST noun
20
+ def string(name, *args)
21
+ define_accessor name
22
+ end
23
+
24
+ # Defines a {Float} field for this REST noun
25
+ def float(name, *args)
26
+ define_accessor name, *args
27
+ end
28
+
29
+ # Defines a {Fixnum} field for this REST noun
30
+ def integer(name, *args)
31
+ define_accessor name
32
+ end
33
+
34
+ # Defines a 1-1 relationship between this Noun and another.
35
+ # @overload has_one(name, options = {})
36
+ def has_one(name, *args)
37
+ options = args.slice!(0) || {}
38
+ class_name = options[:class_name] || name.capitalize
39
+ attribute_name = options[:attribute_name] || name
40
+ key = attribute_name.to_s
41
+ aliases = options[:alias]
42
+
43
+ define_method(name) do
44
+ if self.attributes.include? key
45
+ self.client.noun_class(class_name).new self.attributes[key]
46
+ else
47
+ nil
48
+ end
49
+ end
50
+ alias_method aliases.to_sym, name.to_sym if aliases
51
+ end
52
+
53
+ # Defines a boolean field for this REST noun, available at name?
54
+ # @overload boolean(name, options = {})
55
+ def boolean(name, *args)
56
+ options = args.slice!(0) || {}
57
+ attribute_name = options[:attribute_name] || name
58
+ key = attribute_name.to_s
59
+
60
+ define_method("#{name}?") do
61
+ self.attributes[key]
62
+ end
63
+ end
64
+
65
+ # Defines a Time field for this REST noun.
66
+ # The time must be defined as a string in the REST JSON in ISO8601 format
67
+ def time(name, *args)
68
+ define_method(name) do
69
+ if self.attributes.include? name.to_s
70
+ DateTime.iso8601(self.attributes[name.to_s]).to_time
71
+ else
72
+ nil
73
+ end
74
+
75
+ end
76
+ end
77
+
78
+ # Defines a NOUN attribute of a URL
79
+ # In the REST JSON, the URL my be relative or absolute. If relative, it will get appended to the client's base_url host.
80
+ # @overload url(name, options = {})
81
+ def url(name, *args)
82
+ options = args.slice!(0) || {}
83
+ attribute_name = options[:attribute_name] || name
84
+ key = attribute_name.to_s
85
+
86
+ define_method("#{name}") do
87
+ path = self.attributes[key]
88
+
89
+ if path
90
+ uri = URI(path)
91
+
92
+ if uri.absolute?
93
+ uri.to_s
94
+ else
95
+ # merge this URI with the client path
96
+ base_uri = URI(self.client.base_url)
97
+
98
+ klass = base_uri.scheme == 'http' ? URI::HTTP : URI::HTTPS
99
+ klass.build(:host => base_uri.host, :path => path).to_s
100
+ end
101
+ else
102
+ nil
103
+ end
104
+ end
105
+ end
106
+
107
+ # Defines an attribute accessor with the specified name, returning the value of {#attribute} value with key +name+
108
+ def define_accessor(name, *args)
109
+ options = args.slice!(0) || {}
110
+ aliases = options[:alias]
111
+
112
+ define_method(name) do
113
+ self.attributes[name.to_s]
114
+ end
115
+ alias_method aliases.to_sym, name.to_sym if aliases
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,32 @@
1
+ require 'kegbot_api/nouns/remote_rest_noun'
2
+
3
+ module KegbotApi
4
+ class Session < RemoteRestNoun
5
+ id :id
6
+ string :name
7
+ time :start_time
8
+ time :end_time
9
+ boolean :active, :attribute_name => 'is_active'
10
+ float :volume_ml
11
+
12
+ def self.all
13
+ list(get("#{self.client.base_url}/sessions"))
14
+ end
15
+
16
+ def self.find(id, *args)
17
+ one(get("#{self.client.base_url}/sessions/#{id}"))
18
+ end
19
+
20
+ def to_s
21
+ "#<#{self.class.to_s}:#{"0x%x" % object_id} @id=#{self.id.inspect}>"
22
+ end
23
+
24
+ def self.to_s
25
+ self.client ? "KegbotApi::Session<#{self.client.base_url}>" : super
26
+ end
27
+
28
+ def self.inspect
29
+ to_s
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,31 @@
1
+ require 'kegbot_api/nouns/remote_rest_noun'
2
+ require 'kegbot_api/nouns/keg'
3
+
4
+ module KegbotApi
5
+ class Tap < RemoteRestNoun
6
+ extend Forwardable
7
+
8
+ id :id
9
+ string :name
10
+ string :meter_name
11
+ float :ml_per_tick
12
+ has_one :keg, :class_name => 'Keg', :attribute_name => 'current_keg', :alias => 'current_keg'
13
+ def_delegators :keg, :online?
14
+
15
+ def self.all
16
+ list(get("#{self.client.base_url}/taps"))
17
+ end
18
+
19
+ def to_s
20
+ "#<#{self.class.to_s}:#{"0x%x" % object_id} @id=#{self.id.inspect}>"
21
+ end
22
+
23
+ def self.to_s
24
+ self.client ? "KegbotApi::Tap<#{self.client.base_url}>" : super
25
+ end
26
+
27
+ def self.inspect
28
+ to_s
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,76 @@
1
+ require 'json'
2
+
3
+ module KegbotApi
4
+ # Encapsulates a REST response ({Net::HTTPResponse})
5
+ #
6
+ # @visibility private
7
+ class RestResponse
8
+ # HTTP status code returned
9
+ attr_accessor :status_code
10
+
11
+ # result (as a string)
12
+ attr_accessor :body
13
+
14
+ # result (parsed into a {Hash})
15
+ attr_accessor :body_hash
16
+
17
+ # @overload initialize(http_response)
18
+ # @param http_reponse [Net::HTTPResponse]
19
+ def initialize(*args)
20
+ http_response = args.slice!(0)
21
+
22
+ self.status_code = http_response.code.to_i
23
+ self.body = http_response.read_body
24
+ end
25
+
26
+ def success?
27
+ self.status_code == 200 && self.meta_result == 'ok'
28
+ end
29
+
30
+ # Raises an instance of {KegbotApi::Error} appropriate to the error code returned in the response
31
+ def raise_arguments
32
+ error_name = self.error_code
33
+ klass = KegbotApi.const_defined?(error_name) ? KegbotApi.const_get(error_name) : KegbotApi::Error
34
+
35
+ [klass, self.error_message]
36
+ end
37
+
38
+ # Parses the http result body into a {Hash}
39
+ def body_hash
40
+ @body_hash ||= JSON::load(self.body)
41
+ end
42
+
43
+ def metadata
44
+ hash_attribute_or_fail(self.body_hash, 'meta')
45
+ end
46
+
47
+ def meta_result
48
+ hash_attribute_or_fail(self.metadata, 'result')
49
+ end
50
+
51
+ def error
52
+ hash_attribute_or_fail(self.body_hash, 'error')
53
+ end
54
+
55
+ def error_code
56
+ hash_attribute_or_fail(self.error, 'code')
57
+ end
58
+
59
+ def error_message
60
+ hash_attribute_or_fail(self.error, 'message')
61
+ self.error['message']
62
+ end
63
+
64
+ def objects
65
+ hash_attribute_or_fail(self.body_hash, 'objects')
66
+ end
67
+
68
+ def object
69
+ hash_attribute_or_fail(self.body_hash, 'object')
70
+ end
71
+
72
+ def hash_attribute_or_fail(hash, attribute)
73
+ (found = hash[attribute]) ? found : raise(InvalidInputError, "Server JSON response didn't include the expected '#{attribute}' field. #{hash.inspect}")
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,3 @@
1
+ module KegbotApi
2
+ VERSION = "0.0.1"
3
+ end