helium-ruby 0.20.0 → 0.21.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +47 -0
  3. data/docs/Helium.html +6 -6
  4. data/docs/Helium/Client.html +62 -34
  5. data/docs/Helium/Client/Configurations.html +296 -0
  6. data/docs/Helium/Client/DeviceConfigurations.html +465 -0
  7. data/docs/Helium/Client/Elements.html +92 -18
  8. data/docs/Helium/Client/Http.html +86 -28
  9. data/docs/Helium/Client/Labels.html +29 -86
  10. data/docs/Helium/Client/Organizations.html +5 -75
  11. data/docs/Helium/Client/Sensors.html +147 -17
  12. data/docs/Helium/Client/Users.html +5 -9
  13. data/docs/Helium/ClientError.html +3 -3
  14. data/docs/Helium/Collection.html +1053 -0
  15. data/docs/Helium/Configuration.html +380 -0
  16. data/docs/Helium/Cursor.html +7 -3
  17. data/docs/Helium/DataPoint.html +16 -5
  18. data/docs/Helium/DeviceConfiguration.html +476 -0
  19. data/docs/Helium/Element.html +203 -43
  20. data/docs/Helium/Error.html +15 -5
  21. data/docs/Helium/InvalidApiKey.html +3 -3
  22. data/docs/Helium/Label.html +35 -36
  23. data/docs/Helium/Metadata.html +589 -0
  24. data/docs/Helium/Organization.html +236 -17
  25. data/docs/Helium/Resource.html +655 -161
  26. data/docs/Helium/Sensor.html +345 -111
  27. data/docs/Helium/Timeseries.html +354 -0
  28. data/docs/Helium/User.html +161 -18
  29. data/docs/Helium/Utils.html +56 -4
  30. data/docs/_index.html +72 -7
  31. data/docs/class_list.html +1 -1
  32. data/docs/css/style.css +8 -1
  33. data/docs/file.README.html +66 -6
  34. data/docs/frames.html +1 -1
  35. data/docs/index.html +66 -6
  36. data/docs/method_list.html +619 -139
  37. data/docs/top-level-namespace.html +3 -3
  38. data/lib/helium.rb +2 -0
  39. data/lib/helium/client/configurations.rb +0 -12
  40. data/lib/helium/client/elements.rb +0 -12
  41. data/lib/helium/client/labels.rb +1 -12
  42. data/lib/helium/client/organizations.rb +1 -47
  43. data/lib/helium/client/sensors.rb +0 -12
  44. data/lib/helium/client/users.rb +1 -3
  45. data/lib/helium/collection.rb +129 -0
  46. data/lib/helium/configuration.rb +5 -1
  47. data/lib/helium/element.rb +1 -1
  48. data/lib/helium/label.rb +1 -3
  49. data/lib/helium/metadata.rb +58 -0
  50. data/lib/helium/organization.rb +4 -4
  51. data/lib/helium/resource.rb +28 -29
  52. data/lib/helium/sensor.rb +1 -1
  53. data/lib/helium/version.rb +1 -1
  54. metadata +11 -2
@@ -6,7 +6,7 @@
6
6
  <title>
7
7
  Top Level Namespace
8
8
 
9
- &mdash; Documentation by YARD 0.9.3
9
+ &mdash; Documentation by YARD 0.9.5
10
10
 
11
11
  </title>
12
12
 
@@ -102,9 +102,9 @@
102
102
  </div>
103
103
 
104
104
  <div id="footer">
105
- Generated on Thu Sep 1 16:28:30 2016 by
105
+ Generated on Thu Jan 12 15:58:34 2017 by
106
106
  <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
107
- 0.9.3 (ruby-2.3.1).
107
+ 0.9.5 (ruby-2.3.1).
108
108
  </div>
109
109
 
110
110
  </div>
@@ -7,7 +7,9 @@ require "helium/error"
7
7
  require "helium/utils"
8
8
  require "helium/client"
9
9
  require "helium/cursor"
10
+ require "helium/collection"
10
11
  require "helium/resource"
12
+ require "helium/metadata"
11
13
  require "helium/user"
12
14
  require "helium/organization"
13
15
  require "helium/sensor"
@@ -10,18 +10,6 @@ module Helium
10
10
  Configuration.find(id, client: self)
11
11
  end
12
12
 
13
- def config_device_configurations(id)
14
- path = "/configuration/#{id}/device-configuration"
15
- response = get(path)
16
- device_confs_data = JSON.parse(response.body)["data"]
17
-
18
- device_confs = device_confs_data.map do |dc|
19
- DeviceConfiguration.new(client: self, params: dc)
20
- end
21
-
22
- return device_confs
23
- end
24
-
25
13
  # Configurations are immutable, so no updates are available
26
14
  def create_configuration(attributes)
27
15
  Configuration.create(attributes, client: self)
@@ -9,18 +9,6 @@ module Helium
9
9
  Element.find(id, client: self)
10
10
  end
11
11
 
12
- def element_sensors(element)
13
- path = "/element/#{element.id}/sensor"
14
- response = get(path)
15
- sensors_data = JSON.parse(response.body)["data"]
16
-
17
- sensors = sensors_data.map do |sensor|
18
- Sensor.new(client: self, params: sensor)
19
- end
20
-
21
- return sensors
22
- end
23
-
24
12
  def element_device_configuration(element)
25
13
  path = "/element/#{element.id}/device-configuration"
26
14
  response = get(path)
@@ -13,18 +13,7 @@ module Helium
13
13
  Label.create(attributes, client: self)
14
14
  end
15
15
 
16
- def label_sensors(label)
17
- path = "/label/#{label.id}/sensor"
18
- response = get(path)
19
- sensors_data = JSON.parse(response.body)["data"]
20
-
21
- sensors = sensors_data.map do |sensor_data|
22
- Sensor.new(client: self, params: sensor_data)
23
- end
24
-
25
- return sensors
26
- end
27
-
16
+ # TODO incorporate this logic into Helium::Collection
28
17
  def update_label_sensors(label, opts = {})
29
18
  sensors = opts.fetch(:sensors, [])
30
19
 
@@ -2,53 +2,7 @@ module Helium
2
2
  class Client
3
3
  module Organizations
4
4
  def organization
5
- response = get('/organization')
6
- org_data = JSON.parse(response.body)["data"]
7
- return Organization.new(client: self, params: org_data)
8
- end
9
-
10
- def organization_users
11
- response = get('/organization/user')
12
- users_data = JSON.parse(response.body)["data"]
13
-
14
- users = users_data.map do |user_data|
15
- User.new(client: self, params: user_data)
16
- end
17
-
18
- return users
19
- end
20
-
21
- def organization_labels
22
- response = get('/organization/label')
23
- labels_data = JSON.parse(response.body)["data"]
24
-
25
- labels = labels_data.map do |label_data|
26
- Label.new(client: self, params: label_data)
27
- end
28
-
29
- return labels
30
- end
31
-
32
- def organization_elements
33
- response = get('/organization/element')
34
- elements_data = JSON.parse(response.body)["data"]
35
-
36
- elements = elements_data.map do |element_data|
37
- Element.new(client: self, params: element_data)
38
- end
39
-
40
- return elements
41
- end
42
-
43
- def organization_sensors
44
- response = get('/organization/sensor')
45
- sensors_data = JSON.parse(response.body)["data"]
46
-
47
- sensors = sensors_data.map do |sensor_data|
48
- Sensor.new(client: self, params: sensor_data)
49
- end
50
-
51
- return sensors
5
+ Organization.singleton(client: self)
52
6
  end
53
7
  end
54
8
  end
@@ -18,18 +18,6 @@ module Helium
18
18
  return element
19
19
  end
20
20
 
21
- def sensor_labels(sensor)
22
- path = "/sensor/#{sensor.id}/label"
23
- response = get(path)
24
- labels_data = JSON.parse(response.body)["data"]
25
-
26
- labels = labels_data.map do |label|
27
- Label.new(client: self, params: label)
28
- end
29
-
30
- return labels
31
- end
32
-
33
21
  def sensor_device_configuration(sensor)
34
22
  path = "/sensor/#{sensor.id}/device-configuration"
35
23
  response = get(path)
@@ -2,9 +2,7 @@ module Helium
2
2
  class Client
3
3
  module Users
4
4
  def user
5
- response = get('/user')
6
- user_data = JSON.parse(response.body)["data"]
7
- return User.new(client: self, params: user_data)
5
+ User.singleton(client: self)
8
6
  end
9
7
  end
10
8
  end
@@ -0,0 +1,129 @@
1
+ module Helium
2
+ class Collection
3
+ include Enumerable
4
+
5
+ attr_reader :filter_criteria
6
+
7
+ def initialize(opts)
8
+ @client = opts.fetch(:client)
9
+ @klass = opts.fetch(:klass)
10
+ @belongs_to = opts.fetch(:belongs_to, nil)
11
+
12
+ @filter_criteria = {}
13
+ end
14
+
15
+ # Returns all resources
16
+ # @option opts [Client] :client A Helium::Client
17
+ # @return [Helium::Collection] a Collection of all of the given Resource
18
+ def all
19
+ @filter_criteria = {}
20
+ self
21
+ end
22
+
23
+ # Uses metadata filtering
24
+ # (https://docs.helium.com/api/v1/metadata/index.html#filtering)
25
+ # to search for a collection of resources matching the search parameters
26
+ # @param [Hash] a set of search criteria
27
+ #
28
+ # @example Search for sensors by location
29
+ # client.sensors.where(location: 'Building B') #=> [Sensor, Sensor]
30
+ #
31
+ # @example Search for multiple matching search parameters
32
+ # client.sensors.where(departments: ['it', 'engineering']) #=> [Sensor, Sensor]
33
+ #
34
+ # @return [Collection] a Collection of resources matching the provided search criteria
35
+ def where(criteria)
36
+ add_filter_criteria(criteria)
37
+ self
38
+ end
39
+
40
+ # Returns an array of the Resources belonging to the Collection
41
+ # @return [Resource]
42
+ def collection
43
+ fetch_collection
44
+ end
45
+
46
+ def each
47
+ collection.each{ |element| yield element }
48
+ end
49
+
50
+ def inspect
51
+ collection
52
+ end
53
+
54
+ def to_json(*options)
55
+ collection.to_json(*options)
56
+ end
57
+
58
+ # TODO: could support something like label.sensors << new_sensor
59
+ # see Label#add_sensors for where it would be applied
60
+ def +(other)
61
+ collection + other
62
+ end
63
+
64
+ def -(other)
65
+ collection - other
66
+ end
67
+
68
+ def [](index)
69
+ collection[index]
70
+ end
71
+
72
+ # Collections are considered equal if they contain the same resources
73
+ # as determined by the resources' ids
74
+ def ==(other)
75
+ self.map(&:id).sort == other.map(&:id).sort
76
+ end
77
+
78
+ # NOTE: if we implement pagination, we'll need to rethink this
79
+ def last
80
+ collection.last
81
+ end
82
+
83
+ protected
84
+
85
+ def add_filter_criteria(criteria)
86
+ criteria.each do |key, value|
87
+ if existing_value = @filter_criteria[key]
88
+ @filter_criteria[key] = (Array(existing_value) + Array(value)).uniq
89
+ else
90
+ @filter_criteria[key] = value
91
+ end
92
+ end
93
+ end
94
+
95
+ def fetch_collection
96
+ response = @client.get(resource_path)
97
+ return collection_from_response(response)
98
+ end
99
+
100
+ def filter_param
101
+ "filter[metadata]=#{@filter_criteria.to_json}"
102
+ end
103
+
104
+ def resource_path
105
+ uri = if @belongs_to
106
+ URI.parse("#{@belongs_to.resource_path}/#{@klass.resource_name}")
107
+ else
108
+ URI.parse(@klass.all_path)
109
+ end
110
+
111
+ if @filter_criteria.any?
112
+ uri.query = [uri.query, filter_param].compact.join('&')
113
+ end
114
+
115
+ uri.to_s
116
+ end
117
+
118
+ def collection_from_response(response)
119
+ resources_data = JSON.parse(response.body)["data"]
120
+
121
+ resources = resources_data.map do |resource_data|
122
+ @klass.new(client: @client, params: resource_data)
123
+ end
124
+
125
+ return resources
126
+ end
127
+
128
+ end
129
+ end
@@ -8,7 +8,11 @@ module Helium
8
8
  end
9
9
 
10
10
  def device_configurations
11
- @client.config_device_configurations(self.id)
11
+ Collection.new(
12
+ klass: DeviceConfiguration,
13
+ client: @client,
14
+ belongs_to: self
15
+ )
12
16
  end
13
17
 
14
18
  end
@@ -11,7 +11,7 @@ module Helium
11
11
  end
12
12
 
13
13
  def sensors
14
- @client.element_sensors(self)
14
+ Collection.new(klass: Sensor, client: @client, belongs_to: self)
15
15
  end
16
16
 
17
17
  def device_configuration
@@ -8,10 +8,8 @@ module Helium
8
8
  @name = @params.dig("attributes", "name")
9
9
  end
10
10
 
11
- # TODO: would be nice to wrap this in a proxy collection, that way
12
- # we could do something like label.sensors << new_sensor
13
11
  def sensors
14
- @client.label_sensors(self)
12
+ Collection.new(klass: Sensor, client: @client, belongs_to: self)
15
13
  end
16
14
 
17
15
  def add_sensors(sensors_to_add = [])
@@ -0,0 +1,58 @@
1
+ module Helium
2
+ # TODO make Metadata inherit from Resource and implement method_missing
3
+ # for all resources to automatically generate methods for attributes
4
+ # rather than whitelisting them with hardcoding
5
+ class Metadata
6
+ def initialize(opts = {})
7
+ @client = opts.fetch(:client)
8
+ @klass = opts.fetch(:klass)
9
+
10
+ @params = fetch_params
11
+ end
12
+
13
+ def id
14
+ @klass.id
15
+ end
16
+
17
+ def properties
18
+ @params["attributes"]
19
+ end
20
+
21
+ def inspect
22
+ "<Helium::Metadata properties=#{properties}>"
23
+ end
24
+
25
+ def method_missing(method_name, *arguments, &block)
26
+ properties[method_name.to_s] || super
27
+ end
28
+
29
+ def respond_to_missing?(method_name, include_private = false)
30
+ properties[method_name.to_s] || super
31
+ end
32
+
33
+ def update(attributes = {})
34
+ body = {
35
+ data: {
36
+ attributes: attributes,
37
+ id: id,
38
+ type: "metadata"
39
+ }
40
+ }
41
+
42
+ response = @client.patch(path, body: body)
43
+ @params = JSON.parse(response.body)["data"]
44
+ return self
45
+ end
46
+
47
+ protected
48
+
49
+ def path
50
+ "#{@klass.resource_path}/metadata"
51
+ end
52
+
53
+ def fetch_params
54
+ response = @client.get(path)
55
+ JSON.parse(response.body)["data"]
56
+ end
57
+ end
58
+ end
@@ -15,19 +15,19 @@ module Helium
15
15
 
16
16
  # TODO refactor into relationships
17
17
  def users
18
- @client.organization_users
18
+ Collection.new(klass: User, client: @client, belongs_to: self)
19
19
  end
20
20
 
21
21
  def labels
22
- @client.organization_labels
22
+ Collection.new(klass: Label, client: @client, belongs_to: self)
23
23
  end
24
24
 
25
25
  def elements
26
- @client.organization_elements
26
+ Collection.new(klass: Element, client: @client, belongs_to: self)
27
27
  end
28
28
 
29
29
  def sensors
30
- @client.organization_sensors
30
+ Collection.new(klass: Sensor, client: @client, belongs_to: self)
31
31
  end
32
32
 
33
33
  def as_json
@@ -17,28 +17,15 @@ module Helium
17
17
  class << self
18
18
  include Helium::Utils
19
19
 
20
- # NOTE seems a bit out of place to be doing client work here, but it
21
- # makes sense for the Eigenclass to be responsible for constructing
22
- # instances of its inheriting class.
20
+ # The resource's index API route
21
+ # @return [String] path to resource's index
22
+ def all_path
23
+ "/#{resource_name}"
24
+ end
23
25
 
24
- # Returns all resources
25
- # @option opts [Client] :client A Helium::Client
26
- # @return [Array<Resource>] an Array of all of the inheriting Resource
27
26
  def all(opts = {})
28
27
  client = opts.fetch(:client)
29
-
30
- response = client.get(all_path)
31
- resources_data = JSON.parse(response.body)["data"]
32
-
33
- resources = resources_data.map do |resource_data|
34
- self.new(client: client, params: resource_data)
35
- end
36
-
37
- return resources
38
- end
39
-
40
- def all_path
41
- "/#{resource_name}"
28
+ Collection.new(klass: self, client: client).all
42
29
  end
43
30
 
44
31
  # Finds a single Resource by id
@@ -47,11 +34,15 @@ module Helium
47
34
  # @return [Resource]
48
35
  def find(id, opts = {})
49
36
  client = opts.fetch(:client)
37
+ initialize_from_path(path: "/#{resource_name}/#{id}", client: client)
38
+ end
50
39
 
51
- response = client.get("/#{resource_name}/#{id}")
52
- resource_data = JSON.parse(response.body)["data"]
53
-
54
- return self.new(client: client, params: resource_data)
40
+ # Fetches a singleton resource (e.g. organization, user)
41
+ # @option opts [Client] :client A Helium::Client
42
+ # @return [Resource] A singleton resource
43
+ def singleton(opts = {})
44
+ client = opts.fetch(:client)
45
+ initialize_from_path(path: all_path, client: client)
55
46
  end
56
47
 
57
48
  # Creates a new resource with given attributes
@@ -76,11 +67,18 @@ module Helium
76
67
  return self.new(client: client, params: resource_data)
77
68
  end
78
69
 
79
- private
80
-
81
70
  def resource_name
82
71
  kebab_case(self.name.split('::').last)
83
72
  end
73
+
74
+ def initialize_from_path(opts = {})
75
+ client = opts.fetch(:client)
76
+ path = opts.fetch(:path)
77
+
78
+ response = client.get(path)
79
+ resource_data = JSON.parse(response.body)["data"]
80
+ return self.new(client: client, params: resource_data)
81
+ end
84
82
  end # << self
85
83
 
86
84
  # Returns a path identifying the current resource. Can be overridden
@@ -111,8 +109,11 @@ module Helium
111
109
  # Deletes the Resource
112
110
  # @return [Boolean] Whether the operation was successful
113
111
  def destroy
114
- path = "/#{resource_name}/#{self.id}"
115
- @client.delete(path)
112
+ @client.delete(resource_path)
113
+ end
114
+
115
+ def metadata
116
+ Metadata.new(client: @client, klass: self)
116
117
  end
117
118
 
118
119
  # Override equality to use id for comparisons
@@ -161,8 +162,6 @@ module Helium
161
162
  as_json.to_json(*options)
162
163
  end
163
164
 
164
- private
165
-
166
165
  def resource_name
167
166
  kebab_case(self.class.name.split('::').last)
168
167
  end