helium-ruby 0.20.0 → 0.21.0

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 (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