lelylan-rb 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/.gitignore +26 -0
  2. data/CHANGELOG.md +5 -0
  3. data/Gemfile +7 -0
  4. data/Guardfile +5 -0
  5. data/LICENSE.md +1 -0
  6. data/README.md +218 -0
  7. data/Rakefile +21 -0
  8. data/lelylan_rb.gemspec +36 -0
  9. data/lib/faraday/response/raise_http_error.rb +35 -0
  10. data/lib/lelylan.rb +26 -0
  11. data/lib/lelylan/authentication.rb +11 -0
  12. data/lib/lelylan/client.rb +47 -0
  13. data/lib/lelylan/client/categories.rb +112 -0
  14. data/lib/lelylan/client/consumptions.rb +93 -0
  15. data/lib/lelylan/client/devices.rb +211 -0
  16. data/lib/lelylan/client/functions.rb +118 -0
  17. data/lib/lelylan/client/histories.rb +42 -0
  18. data/lib/lelylan/client/locations.rb +92 -0
  19. data/lib/lelylan/client/properties.rb +115 -0
  20. data/lib/lelylan/client/statuses.rb +110 -0
  21. data/lib/lelylan/client/types.rb +109 -0
  22. data/lib/lelylan/configuration.rb +65 -0
  23. data/lib/lelylan/connection.rb +33 -0
  24. data/lib/lelylan/error.rb +34 -0
  25. data/lib/lelylan/request.rb +70 -0
  26. data/lib/lelylan/version.rb +15 -0
  27. data/spec/faraday/response_spec.rb +37 -0
  28. data/spec/fixtures/categories.json +7 -0
  29. data/spec/fixtures/category.json +7 -0
  30. data/spec/fixtures/consumption.json +11 -0
  31. data/spec/fixtures/consumptions.json +11 -0
  32. data/spec/fixtures/device.json +25 -0
  33. data/spec/fixtures/devices.json +25 -0
  34. data/spec/fixtures/function.json +15 -0
  35. data/spec/fixtures/functions.json +15 -0
  36. data/spec/fixtures/histories.json +16 -0
  37. data/spec/fixtures/history.json +16 -0
  38. data/spec/fixtures/location.json +55 -0
  39. data/spec/fixtures/locations.json +18 -0
  40. data/spec/fixtures/oauth2/refresh.json +6 -0
  41. data/spec/fixtures/oauth2/token.json +6 -0
  42. data/spec/fixtures/pending.json +12 -0
  43. data/spec/fixtures/properties.json +9 -0
  44. data/spec/fixtures/property.json +9 -0
  45. data/spec/fixtures/status.json +24 -0
  46. data/spec/fixtures/statuses.json +24 -0
  47. data/spec/fixtures/type.json +94 -0
  48. data/spec/fixtures/types.json +88 -0
  49. data/spec/helper.rb +71 -0
  50. data/spec/lelylan/client/categories_spec.rb +178 -0
  51. data/spec/lelylan/client/consumptions_spec.rb +150 -0
  52. data/spec/lelylan/client/devices_spec.rb +342 -0
  53. data/spec/lelylan/client/functions_spec.rb +184 -0
  54. data/spec/lelylan/client/histories_spec.rb +64 -0
  55. data/spec/lelylan/client/locations_spec.rb +155 -0
  56. data/spec/lelylan/client/properties_spec.rb +184 -0
  57. data/spec/lelylan/client/statuses_spec.rb +184 -0
  58. data/spec/lelylan/client/types_spec.rb +184 -0
  59. data/spec/lelylan/client_spec.rb +32 -0
  60. data/spec/lelylan/oauth2_spec.rb +54 -0
  61. data/spec/lelylan_spec.rb +32 -0
  62. metadata +351 -0
@@ -0,0 +1,65 @@
1
+ require 'faraday'
2
+ require 'lelylan/version'
3
+
4
+ module Lelylan
5
+ module Configuration
6
+ VALID_OPTIONS_KEYS = [
7
+ :adapter,
8
+ :api_version,
9
+ :api_endpoint,
10
+ :web_endpoint,
11
+ :endpoint,
12
+ :user,
13
+ :password,
14
+ :proxy,
15
+ :token,
16
+ :user_agent,
17
+ :auto_traversal,
18
+ :per_page].freeze
19
+
20
+ DEFAULT_ADAPTER = Faraday.default_adapter
21
+ DEFAULT_API_VERSION = 0
22
+ DEFAULT_API_ENDPOINT = 'http://api.lelylan.com/'
23
+ DEFAULT_WEB_ENDPOINT = 'http://lelylan.com/'
24
+ DEFAULT_USER_AGENT = "Lelylan Ruby Gem #{Lelylan::Version}".freeze
25
+ DEFAULT_AUTO_TRAVERSAL = false
26
+
27
+ attr_accessor(*VALID_OPTIONS_KEYS)
28
+
29
+ def self.extended(base)
30
+ base.reset
31
+ end
32
+
33
+ def configure
34
+ yield self
35
+ end
36
+
37
+ def options
38
+ VALID_OPTIONS_KEYS.inject({}){|o,k| o.merge!(k => send(k)) }
39
+ end
40
+
41
+ def api_endpoint=(value)
42
+ @api_endpoint = File.join(value, "")
43
+ end
44
+
45
+ def web_endpoint=(value)
46
+ @web_endpoint = File.join(value, "")
47
+ end
48
+
49
+ alias :endpoint= :api_endpoint=
50
+ alias :endpoint :api_endpoint
51
+
52
+ def reset
53
+ self.adapter = DEFAULT_ADAPTER
54
+ self.api_version = DEFAULT_API_VERSION
55
+ self.api_endpoint = DEFAULT_API_ENDPOINT
56
+ self.web_endpoint = DEFAULT_WEB_ENDPOINT
57
+ self.user = nil
58
+ self.password = nil
59
+ self.proxy = nil
60
+ self.token = nil
61
+ self.user_agent = DEFAULT_USER_AGENT
62
+ self.auto_traversal = DEFAULT_AUTO_TRAVERSAL
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,33 @@
1
+ require 'faraday_middleware'
2
+ require 'faraday/response/raise_http_error'
3
+
4
+ module Lelylan
5
+ module Connection
6
+ private
7
+
8
+ def connection(authenticate=true, raw=false, version=0, force_urlencoded=false)
9
+
10
+ options = {
11
+ :headers => {'Accept' => 'application/json', 'User-Agent' => user_agent, 'Content-Type' => 'application/json'},
12
+ :proxy => proxy,
13
+ :ssl => { :verify => false },
14
+ :url => Lelylan.api_endpoint
15
+ }
16
+
17
+ if authenticated?
18
+ self.token = token.refresh! if token.expired?
19
+ options[:headers].merge!('Authorization' => "Bearer #{token.token}")
20
+ end
21
+
22
+ connection = Faraday.new(options) do |builder|
23
+ builder.request :json
24
+ builder.use Faraday::Response::RaiseHttpError
25
+ builder.use FaradayMiddleware::Mashify
26
+ builder.use FaradayMiddleware::ParseJson
27
+ builder.adapter(adapter)
28
+ end
29
+
30
+ connection
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,34 @@
1
+ module Lelylan
2
+ # Internal: Custom error class for rescuing from all Lelylan errors
3
+ class Error < StandardError; end
4
+
5
+ # Internal: Raised when Lelylan returns a 400 HTTP status code
6
+ class BadRequest < Error; end
7
+
8
+ # Internal: Raised when Lelylan returns a 401 HTTP status code
9
+ class Unauthorized < Error; end
10
+
11
+ # Internal: Raised when Lelylan returns a 403 HTTP status code
12
+ class Forbidden < Error; end
13
+
14
+ # Internal: Raised when Lelylan returns a 404 HTTP status code
15
+ class NotFound < Error; end
16
+
17
+ # Internal: Raised when Lelylan returns a 406 HTTP status code
18
+ class NotAcceptable < Error; end
19
+
20
+ # Internal: Raised when Lelylan returns a 422 HTTP status code
21
+ class UnprocessableEntity < Error; end
22
+
23
+ # Internal: Raised when Lelylan returns a 500 HTTP status code
24
+ class InternalServerError < Error; end
25
+
26
+ # Internal: Raised when Lelylan returns a 501 HTTP status code
27
+ class NotImplemented < Error; end
28
+
29
+ # Internal: Raised when Lelylan returns a 502 HTTP status code
30
+ class BadGateway < Error; end
31
+
32
+ # Internal: Raised when Lelylan returns a 503 HTTP status code
33
+ class ServiceUnavailable < Error; end
34
+ end
@@ -0,0 +1,70 @@
1
+ require 'multi_json'
2
+
3
+ module Lelylan
4
+ module Request
5
+
6
+ # Internal: Perform an HTTP DELETE request.
7
+ def delete(path, options={}, authenticate=true, raw=false, version=api_version, force_urlencoded=false)
8
+ request(:delete, path, options, authenticate, raw, version, force_urlencoded)
9
+ end
10
+
11
+ # Internal: Perform an HTTP GET request.
12
+ def get(path, options={}, authenticate=true, raw=false, version=api_version, force_urlencoded=false)
13
+ request(:get, path, options, authenticate, raw, version, force_urlencoded)
14
+ end
15
+
16
+ # Internal: Perform an HTTP PATCH request.
17
+ def patch(path, options={}, authenticate=true, raw=false, version=api_version, force_urlencoded=false)
18
+ request(:patch, path, options, authenticate, raw, version, force_urlencoded)
19
+ end
20
+
21
+ # Internal: Perform an HTTP POST request.
22
+ def post(path, options={}, authenticate=true, raw=false, version=api_version, force_urlencoded=false)
23
+ request(:post, path, options, authenticate, raw, version, force_urlencoded)
24
+ end
25
+
26
+ # Internal: Perform an HTTP PUT request.
27
+ def put(path, options={}, authenticate=true, raw=false, version=api_version, force_urlencoded=false)
28
+ request(:put, path, options, authenticate, raw, version, force_urlencoded)
29
+ end
30
+
31
+ # Internal: Gets the user requests limit.
32
+ def ratelimit
33
+ headers = get("/ratelimit",{}, true, true).headers
34
+ return headers["X-RateLimit-Limit"].to_i
35
+ end
36
+ alias rate_limit ratelimit
37
+
38
+ # Internal: Gets the remaining user requests.
39
+ def ratelimit_remaining
40
+ headers = get("/ratelimit",{}, api_version, true, true).headers
41
+ return headers["X-RateLimit-Remaining"].to_i
42
+ end
43
+ alias rate_limit_remaining ratelimit_remaining
44
+
45
+ private
46
+
47
+ # Internal: Perform the HTTP request.
48
+ #
49
+ # method - The Symbol representing the HTTP method.
50
+ # path - The String URI to call.
51
+ # options - The Hash options containing the params to send to the
52
+ # service.
53
+ # authenticate - The Boolean value that authenticate the user.
54
+ # raw - The Boolean value let return the complete response.
55
+ # force_urlencoded - The Boolean value that force the url encoding.
56
+ def request(method, path, options, authenticate, raw, version, force_urlencoded)
57
+ response = connection(authenticate, raw, version, force_urlencoded).send(method) do |request|
58
+ case method
59
+ when :delete, :get
60
+ request.url(path, options)
61
+ when :patch, :post, :put
62
+ request.url(path)
63
+ request.body = options unless options.empty?
64
+ end
65
+ end
66
+
67
+ raw ? response : response.body
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,15 @@
1
+ module Lelylan
2
+ class Version
3
+ MAJOR = 0 unless defined? MAJOR
4
+ MINOR = 0 unless defined? MINOR
5
+ PATCH = 1 unless defined? PATCH
6
+ PRE = nil unless defined? PRE
7
+
8
+ class << self
9
+
10
+ def to_s
11
+ [MAJOR, MINOR, PATCH, PRE].compact.join('.')
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,37 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'helper'
3
+
4
+ describe Faraday::Response do
5
+
6
+ let(:client) do
7
+ Lelylan::Client.new
8
+ end
9
+
10
+ {
11
+ 400 => Lelylan::BadRequest,
12
+ 401 => Lelylan::Unauthorized,
13
+ 403 => Lelylan::Forbidden,
14
+ 404 => Lelylan::NotFound,
15
+ 406 => Lelylan::NotAcceptable,
16
+ 422 => Lelylan::UnprocessableEntity,
17
+ 500 => Lelylan::InternalServerError,
18
+ 501 => Lelylan::NotImplemented,
19
+ 502 => Lelylan::BadGateway,
20
+ 503 => Lelylan::ServiceUnavailable,
21
+ }.each do |status, exception|
22
+
23
+ context "when HTTP status is #{status}" do
24
+
25
+ before do
26
+ stub_get('http://api.lelylan.com/devices').
27
+ to_return(:status => status)
28
+ end
29
+
30
+ it "should raise #{exception.name} error" do
31
+ expect do
32
+ client.devices
33
+ end.to raise_error(exception)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,7 @@
1
+ [{
2
+ "uri": "http://www.example.com/categories/500425f5d033a9b4ac000003",
3
+ "id": "500425f5d033a9b4ac000003",
4
+ "name": "Lighting",
5
+ "created_at": "2012-07-16T14:32:21Z",
6
+ "updated_at": "2012-07-16T14:32:21Z"
7
+ }]
@@ -0,0 +1,7 @@
1
+ {
2
+ "uri": "http://www.example.com/categories/500425f8d033a9b4ac0000b1",
3
+ "id": "500425f8d033a9b4ac0000b1",
4
+ "name": "Lighting",
5
+ "created_at": "2012-07-16T14:32:24Z",
6
+ "updated_at": "2012-07-16T14:32:24Z"
7
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "uri": "http://www.example.com/consumptions/5003c54dd033a96a3c0000fc",
3
+ "id": "5003c54dd033a96a3c0000fc",
4
+ "device": {
5
+ "uri": "http://www.example.com/devices/5003c54dd033a96a3c0000f8"
6
+ },
7
+ "type": "instantaneous",
8
+ "value": 125.0,
9
+ "unit": "kwh",
10
+ "occur_at": "2012-07-09T09:39:57+02:00"
11
+ }
@@ -0,0 +1,11 @@
1
+ [{
2
+ "uri": "http://www.example.com/consumptions/5003c54ad033a96a3c00000b",
3
+ "id": "5003c54ad033a96a3c00000b",
4
+ "device": {
5
+ "uri": "http://www.example.com/devices/5003c54ad033a96a3c000007"
6
+ },
7
+ "type": "instantaneous",
8
+ "value": 125.0,
9
+ "unit": "kwh",
10
+ "occur_at": "2012-07-09T09:39:54+02:00"
11
+ }]
@@ -0,0 +1,25 @@
1
+ {
2
+ "uri": "http://www.example.com/devices/5003c611d033a96b96000271",
3
+ "id": "5003c611d033a96b96000271",
4
+ "name": "Closet dimmer",
5
+ "type": {
6
+ "uri": "https://type.lelylan.com/types/dimmer"
7
+ },
8
+ "properties": [{
9
+ "uri": "https://type.lelylan.com/properties/status",
10
+ "value": "off"
11
+ },
12
+ {
13
+ "uri": "https://type.lelylan.com/properties/intensity",
14
+ "value": "0.0"
15
+ }],
16
+ "physical": {
17
+ "uri": "https://node.lelylan.com/devices/physical-dimmer"
18
+ },
19
+ "pending": {
20
+ "uri": "http://www.example.com/devices/5003c611d033a96b96000271/pending",
21
+ "status": "false"
22
+ },
23
+ "created_at": "2012-07-16T09:43:13+02:00",
24
+ "updated_at": "2012-07-16T09:43:13+02:00"
25
+ }
@@ -0,0 +1,25 @@
1
+ [{
2
+ "uri": "http://www.example.com/devices/5003c60ed033a96b96000009",
3
+ "id": "5003c60ed033a96b96000009",
4
+ "name": "Closet dimmer",
5
+ "type": {
6
+ "uri": "https://type.lelylan.com/types/dimmer"
7
+ },
8
+ "properties": [{
9
+ "uri": "https://type.lelylan.com/properties/status",
10
+ "value": "off"
11
+ },
12
+ {
13
+ "uri": "https://type.lelylan.com/properties/intensity",
14
+ "value": "0.0"
15
+ }],
16
+ "physical": {
17
+ "uri": "https://node.lelylan.com/devices/physical-dimmer"
18
+ },
19
+ "pending": {
20
+ "uri": "http://www.example.com/devices/5003c60ed033a96b96000009/pending",
21
+ "status": false
22
+ },
23
+ "created_at": "2012-07-16T09:43:10+02:00",
24
+ "updated_at": "2012-07-16T09:43:10+02:00"
25
+ }]
@@ -0,0 +1,15 @@
1
+ {
2
+ "uri": "http://www.example.com/functions/500425ffd033a9b4ac0002e5",
3
+ "id": "500425ffd033a9b4ac0002e5",
4
+ "name": "Set intensity",
5
+ "created_at": "2012-07-16T14:32:31Z",
6
+ "updated_at": "2012-07-16T14:32:31Z",
7
+ "properties": [{
8
+ "uri": "http://www.example.com/properties/status",
9
+ "value": "on"
10
+ },
11
+ {
12
+ "uri": "http://www.example.com/properties/intensity",
13
+ "value": "0"
14
+ }]
15
+ }
@@ -0,0 +1,15 @@
1
+ [{
2
+ "uri": "http://www.example.com/functions/500425fbd033a9b4ac0000db",
3
+ "id": "500425fbd033a9b4ac0000db",
4
+ "name": "Set intensity",
5
+ "created_at": "2012-07-16T14:32:27Z",
6
+ "updated_at": "2012-07-16T14:32:27Z",
7
+ "properties": [{
8
+ "uri": "http://www.example.com/properties/status",
9
+ "value": "on"
10
+ },
11
+ {
12
+ "uri": "http://www.example.com/properties/intensity",
13
+ "value": "0"
14
+ }]
15
+ }]
@@ -0,0 +1,16 @@
1
+ [{
2
+ "uri": "http://www.example.com/histories/5003c54fd033a96a3c0001a7",
3
+ "id": "5003c54fd033a96a3c0001a7",
4
+ "device": {
5
+ "uri": "http://www.example.com/devices/5003c54fd033a96a3c0001a3"
6
+ },
7
+ "properties": [{
8
+ "uri": "https://type.lelylan.com/properties/intensity",
9
+ "value": "100.0"
10
+ },
11
+ {
12
+ "uri": "https://type.lelylan.com/properties/status",
13
+ "value": "on"
14
+ }],
15
+ "created_at": "2012-07-09T09:39:59+02:00"
16
+ }]
@@ -0,0 +1,16 @@
1
+ {
2
+ "uri": "http://www.example.com/histories/5003c552d033a96a3c000398",
3
+ "id": "5003c552d033a96a3c000398",
4
+ "device": {
5
+ "uri": "http://www.example.com/devices/5003c552d033a96a3c000394"
6
+ },
7
+ "properties": [{
8
+ "uri": "https://type.lelylan.com/properties/intensity",
9
+ "value": "100.0"
10
+ },
11
+ {
12
+ "uri": "https://type.lelylan.com/properties/status",
13
+ "value": "on"
14
+ }],
15
+ "created_at": "2012-07-09T09:40:02+02:00"
16
+ }
@@ -0,0 +1,55 @@
1
+ {
2
+ "uri": "http://www.example.com/locations/1",
3
+ "id": "1",
4
+ "name": "Floor",
5
+ "type": "floor",
6
+ "created_at": "2012-07-16T17:04:14Z",
7
+ "updated_at": "2012-07-16T17:04:14Z",
8
+ "locations": {
9
+ "parent": {
10
+ "uri": "http://www.example.com/locations/2"
11
+ },
12
+ "children": [{
13
+ "uri": "http://www.example.com/locations/4"
14
+ }],
15
+ "ancestors": [{
16
+ "uri": "http://www.example.com/locations/3"
17
+ },
18
+ {
19
+ "uri": "http://www.example.com/locations/2"
20
+ }],
21
+ "descendants": [{
22
+ "uri": "http://www.example.com/locations/4"
23
+ },
24
+ {
25
+ "uri": "http://www.example.com/locations/5"
26
+ }]
27
+ },
28
+ "devices": {
29
+ "children": [{
30
+ "uri": {
31
+ "uri": "https://device.lelylan.com/devices/closet-dimmer"
32
+ }
33
+ },
34
+ {
35
+ "uri": {
36
+ "uri": "https://device.lelylan.com/devices/another-closet-dimmer"
37
+ }
38
+ }],
39
+ "descendants": [{
40
+ "uri": {
41
+ "uri": "https://device.lelylan.com/devices/closet-dimmer"
42
+ }
43
+ },
44
+ {
45
+ "uri": {
46
+ "uri": "https://device.lelylan.com/devices/another-closet-dimmer"
47
+ }
48
+ },
49
+ {
50
+ "uri": {
51
+ "uri": "https://device.lelylan.com/devices/descendant-closet-dimmer"
52
+ }
53
+ }]
54
+ }
55
+ }