particlerb 0.0.1 → 0.0.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c21a444cf3a1c9100b0c8c8d9b70105ee65e28dc
4
- data.tar.gz: 9d7b3d4150a9c4d4f69306f47c411761c7a6d06b
3
+ metadata.gz: 3df82a4b9e831fa635c52d24e9d0f59bd3b6209d
4
+ data.tar.gz: 61c6aa2eba790faeee99e4cde8ea6f04e214946b
5
5
  SHA512:
6
- metadata.gz: 6daacb4b3fb434bfa5d5f250e2c7b90784b8e8319643998bce95732e7c6b47520e4fe9aa96767b4d2fd6e1b576b5f759bffe37de43281205d65c7ecc3a8e6bcd
7
- data.tar.gz: f6cd397e20992d386419f36e889fcdcdaeda9ae007777900752c847d1e5c99ca94cc79980b41989d7c6cf1b04140d8bd6766c5905d63cc71301bc2999a207643
6
+ metadata.gz: 4a4f275a4d1b7b1dde368e620a76205e4ee34fd23aaca4682d3df0bee6c3407786504d05b648cfe56ddb8ea0e88404a69d5a3479705e58ee6e9eb2276cb5f1d5
7
+ data.tar.gz: 95e421509a8109498915efcb25096969204afb0fb673a38edca51957ea20874d784a9dca23383e5d6cce600bb65e429f0ad7b8048b8de06985eb09b7c8d6d32f
data/README.md CHANGED
@@ -1,12 +1,16 @@
1
- # particlerb [![Build Status](https://travis-ci.org/monkbroc/particlerb.svg)](https://travis-ci.org/monkbroc/particlerb)
1
+ ## particlerb ##
2
2
 
3
- Ruby client for the [Particle.io] Cloud API
3
+ [![Gem Version](https://badge.fury.io/rb/particlerb.svg)](http://badge.fury.io/rb/particlerb)
4
+ [![Build Status](https://travis-ci.org/monkbroc/particlerb.svg)](https://travis-ci.org/monkbroc/particlerb)
5
+ [![Code Climate](https://codeclimate.com/github/monkbroc/particlerb/badges/gpa.svg)](https://codeclimate.com/github/monkbroc/particlerb)
6
+
7
+ Ruby client for the [Particle.io] Cloud API with an object-oriented interface
4
8
 
5
9
  [Particle.io]: https://www.particle.io
6
10
 
7
11
  *Note: this is not an official gem by Particle. It is maintained by Julien Vanier.*
8
12
 
9
- ## Quick start
13
+ ## Installation
10
14
 
11
15
  Install via Rubygems
12
16
 
@@ -14,82 +18,111 @@ Install via Rubygems
14
18
 
15
19
  ... or add to your Gemfile
16
20
 
17
- gem "particlerb", "~> 0.0.1"
21
+ gem "particlerb", "~> 0.0.2"
18
22
 
19
23
 
20
- ### Making requests
24
+ ### Providing credentials
21
25
 
22
- API methods are available as module methods (consuming module-level
23
- configuration) or as client instance methods.
26
+ **A Particle cloud API access token is necessary for most requests.** You can use the one from the [Web IDE][] for testing, but it's recommended to generate a new token with this gem using `Particle.login` or with the [Particle CLI][] using `particle token new`
24
27
 
25
28
  ```ruby
26
- # Provide authentication credentials
29
+ # Provide acess token as an environment variable
30
+ ENV['PARTICLE_ACCESS_TOKEN']
31
+
32
+ # Or configure global authentication credentials
33
+ # If you use Rails, you can put this in config/initializers/particle.rb
27
34
  Particle.configure do |c|
28
35
  c.access_token = "38bb7b318cc6898c80317decb34525844bc9db55"
29
36
  end
30
37
 
31
- # Fetch the list of devices
32
- Particle.devices
38
+ # Or pass access token when creating a client
39
+ # If no token is passed to Particle::Client.new, the global or environment one is used
40
+ client = Particle::Client.new(access_token: "38bb7b318cc6898c80317decb34525844bc9db55")
33
41
  ```
34
- or
42
+
43
+ ### Making requests
44
+
45
+ API methods are available as module methods (consuming module-level
46
+ configuration) or as client instance methods.
35
47
 
36
48
  ```ruby
37
- # Provide authentication credentials
38
- client = Particle::Client.new(access_token: "38bb7b318cc6898c80317decb34525844bc9db55")
49
+ # Fetch the list of devices using the global client
50
+ Particle.devices
51
+
52
+ # Or used a newly created client
53
+ client = Particle::Client.new
39
54
  # Fetch the list of devices
40
55
  client.devices
41
56
  ```
42
57
 
43
- ## Device commands
58
+ [Web IDE]: http://docs.particle.io/core/build/#flash-apps-with-particle-build-account-information
59
+ [Particle CLI]: http://docs.particle.io/core/cli
60
+
61
+ ## Interacting with devices
44
62
 
45
- See the [Particle Cloud API documentation][API docs] for more details.
63
+ List all devices. Rreturns an `Array` of `Particle::Device`.
46
64
 
47
- List all devices (returns an `Array` of `Device`)
48
65
  ```ruby
49
66
  devices = Particle.devices
50
67
  ```
51
68
 
52
- Get a `Device` by id or name
69
+ Get a `Particle::Device` by id or name.
70
+
53
71
  ```ruby
54
72
  device = Particle.device('blue_fire')
55
73
  device = Particle.device('f8bbe1e6e69e05c9c405ba1ca504d438061f1b0d')
56
74
  ```
57
75
 
58
- Get information about a device
76
+ Get information about a device.
77
+
59
78
  ```ruby
60
79
  device = Particle.device('blue_fire')
61
- device.id
62
- device.name
63
- device.connected?
64
- device.variables
65
- device.functions
66
80
  device.attributes # Hash of all attributes
81
+ device.id # ==> 'f8bbe1e6e69e05c9c405ba1ca504d438061f1b0d'
82
+ device.name # ==> 'blue_fire'
83
+ device.connected? # true
84
+ device.variables # {:myvar => "double" } or nil if not connected
85
+ device.functions # ["myfunction"] or nil if not connected
67
86
  device.get_attributes # forces refresh of all attributes from the cloud
87
+
88
+ # If you get a Device from the Particle.devices call, you will need to call
89
+ # get_attributes to get the list of functions and variables since that's not
90
+ # returned by the cloud when calling Particle.devices
91
+ device = Particle.devices.first
92
+ device.connected? # ==> true
93
+ device.functions # ==> nil
94
+ device.get_attributes
95
+ device.functions # ==> ["myfunction"]
68
96
  ```
69
97
 
70
- Claim a device and add it to your account (returns the `Device`)
98
+ Claim a device by id and add it to your account. Returns a `Particle::Device`.
99
+
71
100
  ```ruby
72
- Particle.device('blue_fire').claim
101
+ Particle.device('f8bbe1e6e69e05c9c405ba1ca504d438061f1b0d').claim
73
102
  ```
74
103
 
75
- Remove a device from your account
104
+ Remove a device from your account. Returns true on success.
105
+
76
106
  ```ruby
77
107
  Particle.device('blue_fire').remove
78
108
  Particle.devices.first.remove
79
109
  ```
80
110
 
81
- Rename a device
111
+ Rename a device. Returns true on success.
112
+
82
113
  ```ruby
83
114
  Particle.device('red').rename('green')
84
115
  ```
85
116
 
86
- Call a function on the firmware (returns the result of running the function)
117
+ Call a function on the firmware with an optional `String` argument. Returns the result of running the function as as `Number`.
118
+
87
119
  ```ruby
88
- Particle.device('coffeemaker').function('brew') # String argument optional
120
+ Particle.device('coffeemaker').function('brew')
89
121
  Particle.devices.first.function('digitalWrite', '1')
90
122
  ```
91
123
 
92
- Get the value of a firmware variable (returns the result as a String or Number)
124
+ Get the value of a firmware variable. Returns the result as a `String` or `Number`.
125
+
93
126
  ```ruby
94
127
  Particle.device('mycar').variable('battery') # ==> 12.33
95
128
  device = Particle.device('f8bbe1e6e69e05c9c405ba1ca504d438061f1b0d')
@@ -97,13 +130,145 @@ device.variable('version') # ==> "1.0.1"
97
130
  ```
98
131
 
99
132
 
100
- Signal a device to start blinking the RGB LED in rainbow patterns.
133
+ Signal a device to start blinking the RGB LED in rainbow patterns. Returns whether the device is signaling.
134
+
101
135
  ```ruby
102
136
  Particle.device('nyan_cat').signal(true)
103
137
  ```
138
+ See the [Particle Cloud API documentation about devices][device docs] for more details.
139
+
140
+ [device docs]: http://docs.particle.io/core/api/#introduction-device-information
141
+
142
+ ## Interacting with events
143
+
144
+ Publish an event to your devices. Returns true on success.
145
+
146
+ ```ruby
147
+ Particle.publish(name: "wakeup")
148
+ Particle.publish(name: "server_ip", data: "8.8.8.8", ttl: 3600, private: true)
149
+ ```
150
+
151
+ Data, ttl and private are optional.
152
+
153
+ Data is converted to JSON if it is a Hash or an Array, otherwise it is converted to a String.
154
+
155
+ See the [Particle Cloud API documentation about publishing events][publish docs] for more details.
156
+
157
+ [publish docs]: http://docs.particle.io/core/api/#publishing-events
158
+
159
+ ### Limitation: Subscribe not supported
160
+
161
+ This gem does not support subscribing (listening) to events from devices.
162
+
163
+ This would require an HTTP client that supports streaming responses which is not common in Ruby. Some clients like EM-HTTP-Request do support streaming responses, but are tied to specific architectures like EventMachine.
164
+
165
+ For web server applications, webhooks are better suited to process incoming events.
166
+
167
+ ## Interacting with webhooks
168
+
169
+ List existing webhooks. Returns an `Array` of `Particle::Webhook`
170
+
171
+ ```ruby
172
+ Particle.webhooks
173
+ ```
174
+
175
+ Get info about an existing webhook by id. Returns a `Particle::Webhook`
176
+
177
+ ```ruby
178
+ webhook = Particle.webhook('ffcddbd30b860ea3cadd22db')
179
+ webhook.attributes
180
+ webhook.event
181
+ webhook.url
182
+ ```
183
+
184
+ Calling `attributes` will also send a test message to your webhook url and report the `response` or `error`.
185
+
186
+ ```ruby
187
+ webhook.response
188
+ webhook.error
189
+
190
+ webhook = Particle.webhooks.first
191
+ webhook.get_attributes # force reloading attributes from the cloud
192
+ # get_attributes necessary to get the response when Webhook was returned from the
193
+ # Particle.webhooks() method as it doesn't do a test message on each webhook
194
+ webhook.response
195
+ ```
196
+
197
+ Create a new webhook. Pass a hash of [any options accepted by the Particle Cloud API][webhook options]. Returns a `Particle::Webhook`
198
+
199
+ ```ruby
200
+ Particle.webhook(event: "weather", url: "http://myserver.com/report").create
201
+ ```
202
+ See the [Particle Cloud API documentation about webhooks][webhook docs] for more details.
203
+
204
+ [webhook docs]: http://docs.particle.io/core/webhooks/
205
+ [webhook options]: http://docs.particle.io/core/webhooks/#webhook-options
206
+
207
+ ## Authentication
208
+
209
+ Replace the access token on a client
210
+
211
+ ```ruby
212
+ Particle.access_token = 'f1d52ea0de921fad300027763d8c5ebd03b1934d'
213
+
214
+ # On client instance
215
+ client = Particle::Client.new
216
+ client.access_token = 'f1d52ea0de921fad300027763d8c5ebd03b1934d'
217
+ ```
218
+
219
+ **All these following methods requires the account username (email) and password.**
220
+
221
+ List all tokens that can be used to access an account. Returns an `Array` of `Particle::Token`
222
+
223
+ ```ruby
224
+ Particle.tokens("me@example.com", "pa$$w0rd")
225
+ ```
226
+
227
+ Log in and create a new token. Returns a `Particle::Token`. This will also set the token on the client for future calls.
228
+
229
+ ```ruby
230
+ Particle.login("me@example.com", "pa$$w0rd")
231
+ ```
232
+
233
+ Create a token but don't set it on the client. Returns a `Particle::Token`
234
+
235
+ ```ruby
236
+ Particle.token.create("me@example.com", "pa$$w0rd")
237
+ ```
238
+
239
+ Invalidate and delete a token. Returns true on success.
240
+
241
+ ```ruby
242
+ Particle.token('f1d52ea0de921fad300027763d8c5ebd03b1934d').remove("me@example.com", "pa$$w0rd")
243
+ Particle.tokens.first.remove("me@example.com", "pa$$w0rd")
244
+ ```
245
+
246
+ See the [Particle Cloud API documentation about authentication and token][authentication docs] for more details.
247
+
248
+ [authentication docs]: http://docs.particle.io/core/api/#introduction-authentication
249
+
104
250
 
251
+ ## Errors
105
252
 
106
- [API docs]: http://docs.particle.io/core/api
253
+ When any API error occurs, a subclass of `Particle::Error` will be raised.
254
+
255
+ The actual error classes are
256
+
257
+ - `MissingTokenError`
258
+ - `BadRequest`
259
+ - `Unauthorized`
260
+ - `Forbidden`
261
+ - `NotFound`
262
+ - `TimedOut`
263
+ - `ServerError`
264
+
265
+ See [a description of each error on the Particle API docs][error docs].
266
+
267
+ [error docs]: http://docs.particle.io/core/api/#introduction-errors
268
+
269
+ ## Advanced
270
+
271
+ All API endpoints are availble directly on the client object as method calls like `Particle.claim_device(id)` but the preferred usage is to call methods on domain objects like `Particle.device(id).claim`. See the various `Particle::Client` subclasses for more details.
107
272
 
108
273
  ### Accessing HTTP responses
109
274
 
@@ -117,6 +282,19 @@ response = Particle.last_response
117
282
  headers = response.headers
118
283
  ```
119
284
 
285
+ ## Support
286
+
287
+ Open a [GitHub issue][] if you find a bug.
288
+
289
+ [Join the conversion on the awesome Particle community forums][forum] to discuss any other topic!
290
+
291
+ [GitHub issue]: https://github.com/monkbroc/particlerb/issues
292
+ [forum]: http://community.particle.io/
293
+
294
+ ## Versioning
295
+
296
+ particlerb follows the [Semantic Versioning](http://semver.org/) standard.
297
+
120
298
  ## Thanks
121
299
 
122
300
  This gem is heavily inspired by [Octokit][] by GitHub. I stand on the shoulder of giants. Thanks!
@@ -1,13 +1,16 @@
1
+ require 'particle/device'
2
+
1
3
  module Particle
2
4
  class Client
3
5
 
4
- # Methods for the Particle device API
6
+ # Client methods for the Particle device API
7
+ #
5
8
  # @see http://docs.particle.io/core/api/#introduction-list-devices
6
9
  module Devices
7
10
 
8
11
  # Create a domain model for a Particle device
9
12
  #
10
- # @param target [String, Sawyer::Resource, Device] A device id, name or {Device} object
13
+ # @param target [String, Sawyer::Resource, Device] A device id, name, Sawyer::Resource or {Device} object
11
14
  # @return [Device] A device object to interact with
12
15
  def device(target)
13
16
  if target.is_a? Device
@@ -15,18 +18,16 @@ module Particle
15
18
  elsif target.respond_to?(:to_attrs)
16
19
  Device.new(self, target.to_attrs)
17
20
  else
18
- Device.new(self, target.to_s)
21
+ Device.new(self, target)
19
22
  end
20
23
  end
21
24
 
22
25
  # List all Particle devices on the account
23
26
  #
24
- # @see http://docs.particle.io/core/api/#introduction-list-devices
25
- #
26
27
  # @return [Array<Device>] List of Particle devices to interact with
27
28
  def devices
28
29
  get(Device.list_path).map do |resource|
29
- Device.new(self, resource)
30
+ device(resource)
30
31
  end
31
32
  end
32
33
 
@@ -0,0 +1,38 @@
1
+ require 'particle/event'
2
+
3
+ module Particle
4
+ class Client
5
+
6
+ # Client methods for the Particle publish API
7
+ #
8
+ # @see http://docs.particle.io/core/api/#publishing-events
9
+ module Publish
10
+
11
+ # Publish an event to your devices
12
+ #
13
+ # @param options [Hash]
14
+ # * :name [String]: name of the event to publish
15
+ # * :data [String,Hash]: optional data to include with the event
16
+ # * :ttl [Number]: optional Time-To-Live in seconds for the
17
+ # event (currently ignored by the cloud)
18
+ # * :private [boolean]: optional key to indicate event should
19
+ # be published only to your own devices
20
+ # @return [boolean] true for success
21
+ def publish(options)
22
+ params = {
23
+ name: options.fetch(:name)
24
+ }
25
+ case options[:data]
26
+ when Hash, Array then params[:data] = options[:data].to_json
27
+ else params[:data] = options[:data].to_s
28
+ end
29
+
30
+ params[:ttl] = options[:ttl] if options[:ttl]
31
+ params[:private] = true if options[:private]
32
+
33
+ result = post(Event.publish_path, params)
34
+ result.ok
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,101 @@
1
+ require 'particle/token'
2
+ require 'uri'
3
+
4
+ module Particle
5
+ class Client
6
+
7
+ # Client methods for the Particle authentication/token API
8
+ #
9
+ # @see http://docs.particle.io/core/api/#introduction-authentication
10
+ module Tokens
11
+
12
+ # Create a domain model for a Particle token
13
+ #
14
+ # @param target [String, Sawyer::Resource, Token] A token id, Sawyer::Resource or {Token} object
15
+ # @return [Token] A token object to interact with
16
+ def token(target = {})
17
+ if target.is_a? Token
18
+ target
19
+ elsif target.respond_to?(:to_attrs)
20
+ Token.new(self, target.to_attrs)
21
+ else
22
+ Token.new(self, target)
23
+ end
24
+ end
25
+
26
+ # List all Particle tokens for the account
27
+ #
28
+ # @param username [String] The username (email) used to log in to
29
+ # the Particle Cloud API
30
+ # @param password [String] The password used to log in to
31
+ # the Particle Cloud API
32
+ # @return [Array<Token>] List of Particle tokens
33
+ def tokens(username, password)
34
+ http_options = {
35
+ username: username,
36
+ password: password
37
+ }
38
+ request(:get, Token.list_path, "", http_options).map do |resource|
39
+ token(resource)
40
+ end
41
+ end
42
+
43
+ # Authenticate with Particle and create a new token
44
+ #
45
+ # @param username [String] The username (email) used to log in to
46
+ # the Particle Cloud API
47
+ # @param password [String] The password used to log in to
48
+ # the Particle Cloud API
49
+ # @param options [Hash] Additional Particle Cloud API options to
50
+ # create the token.
51
+ # @return [Token] The token object
52
+ def create_token(username, password, options = {})
53
+ data = URI.encode_www_form({
54
+ grant_type: 'password', # specified by docs
55
+ username: username,
56
+ password: password
57
+ }.merge(options))
58
+ http_options = {
59
+ headers: { content_type: "application/x-www-form-urlencoded" },
60
+ username: 'particle', # specified by docs
61
+ password: 'particle' # specified by docs
62
+ }
63
+ result = request(:post, Token.create_path, data, http_options)
64
+ token(result.access_token)
65
+ end
66
+
67
+ # Authenticate with Particle and start using the token on the
68
+ # client right away
69
+ #
70
+ # @param username [String] The username (email) used to log in to
71
+ # the Particle Cloud API
72
+ # @param password [String] The password used to log in to
73
+ # the Particle Cloud API
74
+ # @param options [Hash] Additional Particle Cloud API options to
75
+ # create the token.
76
+ # @return [Token] The token object
77
+ def login(username, password, options = {})
78
+ token = create_token(username, password, options)
79
+ self.access_token = token
80
+ token
81
+ end
82
+
83
+ # Remove a Particle token
84
+ #
85
+ # @param username [String] The username (email) used to log in to
86
+ # the Particle Cloud API
87
+ # @param password [String] The password used to log in to
88
+ # the Particle Cloud API
89
+ # @param target [String, Token] An token id or {Token} object
90
+ # @return [boolean] true for success
91
+ def remove_token(username, password, target)
92
+ http_options = {
93
+ username: username,
94
+ password: password
95
+ }
96
+ result = request(:delete, token(target).path, "", http_options)
97
+ result.ok
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,66 @@
1
+ require 'particle/webhook'
2
+
3
+ module Particle
4
+ class Client
5
+
6
+ # Client methods for the Particle webhook API
7
+ #
8
+ # @see http://docs.particle.io/core/webhooks/
9
+ module Webhooks
10
+
11
+ # Create a domain model for a Particle webhook
12
+ #
13
+ # @param target [String, Sawyer::Resource, Webhook] A webhook id, Sawyer::Resource or {Device} object
14
+ # @return [Webhook] A webhook object to interact with
15
+ def webhook(target)
16
+ if target.is_a? Webhook
17
+ target
18
+ elsif target.respond_to?(:to_attrs)
19
+ Webhook.new(self, target.to_attrs)
20
+ else
21
+ Webhook.new(self, target)
22
+ end
23
+ end
24
+
25
+ # List all Particle webhooks on the account
26
+ #
27
+ # @return [Array<Webhook>] List of Particle webhooks to interact with
28
+ def webhooks
29
+ get(Webhook.list_path).map do |resource|
30
+ webhook(resource)
31
+ end
32
+ end
33
+
34
+ # Get information about a Particle webhook
35
+ #
36
+ # The Particle cloud will send a test message to the webhook URL
37
+ # when this is called
38
+ #
39
+ # @param target [String, Webhook] A webhook id or {Webhook} object
40
+ # @return [Hash] The webhook attributes and test message response
41
+ def webhook_attributes(target)
42
+ result = get(webhook(target).path)
43
+ result.to_attrs
44
+ end
45
+
46
+ # Creates a new Particle webhook
47
+ #
48
+ # @param options [Hash] Options to configure the webhook
49
+ # @return [Webhook] The webhook object
50
+ # @see http://docs.particle.io/core/webhooks/#Webhook-options
51
+ def create_webhook(options)
52
+ result = post(Webhook.create_path, options)
53
+ webhook(result)
54
+ end
55
+
56
+ # Remove a Particle webhook
57
+ #
58
+ # @param target [String, Webhook] A webhook id or {Webhook} object
59
+ # @return [boolean] true for success
60
+ def remove_webhook(target)
61
+ result = delete(webhook(target).path)
62
+ result.ok
63
+ end
64
+ end
65
+ end
66
+ end
@@ -1,6 +1,8 @@
1
1
  require 'particle/connection'
2
- require 'particle/device'
3
2
  require 'particle/client/devices'
3
+ require 'particle/client/publish'
4
+ require 'particle/client/webhooks'
5
+ require 'particle/client/tokens'
4
6
 
5
7
  module Particle
6
8
 
@@ -11,6 +13,9 @@ module Particle
11
13
  include Particle::Configurable
12
14
  include Particle::Connection
13
15
  include Particle::Client::Devices
16
+ include Particle::Client::Publish
17
+ include Particle::Client::Webhooks
18
+ include Particle::Client::Tokens
14
19
 
15
20
  def initialize(options = {})
16
21
  # Use options passed in, but fall back to module defaults
@@ -38,7 +43,12 @@ module Particle
38
43
  # @param value [String] 40 character Particle OAuth2 access token
39
44
  def access_token=(value)
40
45
  reset_agent
41
- @access_token = value
46
+ @access_token =
47
+ if value.respond_to? :access_token
48
+ value.access_token
49
+ else
50
+ value
51
+ end
42
52
  end
43
53
  end
44
54
  end
@@ -3,7 +3,7 @@ module Particle
3
3
  # Configuration options for {Client}, defaulting to values in
4
4
  # {Default}
5
5
  module Configurable
6
- # @!attribute [w] access_token
6
+ # @!attribute [String] access_token
7
7
  # @see http://docs.particle.io/core/api/#introduction-authentication
8
8
  # @return [String] Particle access token for authentication
9
9
  # @!attribute api_endpoint
@@ -57,15 +57,6 @@ module Particle
57
57
  request :delete, url, options
58
58
  end
59
59
 
60
- # Make a HTTP HEAD request
61
- #
62
- # @param url [String] The path, relative to {#api_endpoint}
63
- # @param options [Hash] Query and header params for request
64
- # @return [Sawyer::Resource]
65
- def head(url, options = {})
66
- request :head, url, parse_query_and_convenience_headers(options)
67
- end
68
-
69
60
  # Hypermedia agent for the Particle API
70
61
  #
71
62
  # @return [Sawyer::Agent]
@@ -98,13 +89,21 @@ module Particle
98
89
 
99
90
  def request(method, path, data, options = {})
100
91
  if data.is_a?(Hash)
101
- options[:query] = data.delete(:query) || {}
102
- options[:headers] = data.delete(:headers) || {}
92
+ options[:query] ||= data.delete(:query) || {}
93
+ options[:headers] ||= data.delete(:headers) || {}
103
94
  if accept = data.delete(:accept)
104
95
  options[:headers][:accept] = accept
105
96
  end
106
97
  end
107
98
 
99
+ username = options.delete(:username)
100
+ password = options.delete(:password)
101
+ if username && password
102
+ options[:headers] ||= {}
103
+ options[:headers][:authorization] =
104
+ basic_auth_header(username, password)
105
+ end
106
+
108
107
  @last_response = response = agent.call(method, URI::Parser.new.escape(path.to_s), data, options)
109
108
  response.data
110
109
  end
@@ -120,6 +119,12 @@ module Particle
120
119
 
121
120
  opts
122
121
  end
122
+
123
+ # Temporarily set the Authorization to use basic auth
124
+ def basic_auth_header(username, password)
125
+ Faraday::Request.lookup_middleware(:basic_auth).
126
+ header(username, password)
127
+ end
123
128
  end
124
129
  end
125
130
 
@@ -1,20 +1,20 @@
1
+ require 'particle/model'
2
+
1
3
  module Particle
2
4
 
3
5
  # Domain model for one Particle device
4
- class Device
6
+ class Device < Model
5
7
  ID_REGEX = /\h{24}/
6
8
 
7
9
  def initialize(client, attributes)
8
- @client = client
9
- @attributes =
10
+ super(client, attributes)
11
+
10
12
  if attributes.is_a? String
11
13
  if attributes =~ ID_REGEX
12
- { id: attributes }
14
+ @attributes = { id: attributes }
13
15
  else
14
- { name: attributes }
16
+ @attributes = { name: attributes }
15
17
  end
16
- else
17
- attributes
18
18
  end
19
19
  end
20
20
 
@@ -32,17 +32,10 @@ module Particle
32
32
  @attributes[:id] || @attributes[:name]
33
33
  end
34
34
 
35
- %w(connected functions variables product_id last_heard).each do |key|
36
- define_method key do
37
- attributes[key.to_sym]
38
- end
39
- end
40
- alias_method :connected?, :connected
35
+ attribute_reader :connected, :functions, :variables, :product_id,
36
+ :last_heard, :last_app, :last_ip_address
41
37
 
42
- def attributes
43
- get_attributes unless @loaded
44
- @attributes
45
- end
38
+ alias_method :connected?, :connected
46
39
 
47
40
  def get_attributes
48
41
  @loaded = true
@@ -1,5 +1,7 @@
1
1
  module Particle
2
2
  # Custom error class for rescuing from all Particle errors
3
+ #
4
+ # @see http://docs.particle.io/core/api/#introduction-errors
3
5
  class Error < StandardError
4
6
 
5
7
  # Returns the appropriate Particle::Error subclass based
@@ -0,0 +1,10 @@
1
+ module Particle
2
+
3
+ # Domain model for Particle event
4
+ class Event
5
+ def self.publish_path
6
+ "v1/devices/events"
7
+ end
8
+ end
9
+ end
10
+
@@ -0,0 +1,49 @@
1
+ module Particle
2
+
3
+ # Base class for domain models
4
+ class Model
5
+ def initialize(client, attributes)
6
+ @client = client
7
+ @attributes =
8
+ if attributes.is_a? String
9
+ { id: attributes }
10
+ else
11
+ # Consider attributes loaded when passed in through constructor
12
+ @loaded = true
13
+ attributes
14
+ end
15
+ end
16
+
17
+ # Display the attributes when inspecting the object in the console
18
+ def inspect
19
+ "#<#{self.class} #{@attributes}>"
20
+ end
21
+
22
+ # Accessor for the id
23
+ def id
24
+ @attributes[:id]
25
+ end
26
+
27
+ # Define accessor methods for attributes.
28
+ #
29
+ # Will load the attributes from the cloud if not already done
30
+ def self.attribute_reader(*keys)
31
+ keys.each do |key|
32
+ define_method key do
33
+ attributes[key]
34
+ end
35
+ end
36
+ end
37
+
38
+ # Hash of all attributes returned by the cloud
39
+ def attributes
40
+ get_attributes unless @loaded
41
+ @attributes
42
+ end
43
+
44
+ # Load the model attributes with the correct API call in subclasses
45
+ def get_attributes
46
+ raise "Implement in subclasses and set @loaded = true"
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,76 @@
1
+ module Particle
2
+
3
+ # Domain model for one Particle token
4
+ class Token < Model
5
+ def initialize(client, attributes)
6
+ @client = client
7
+ @attributes =
8
+ if attributes.is_a? String
9
+ { token: attributes }
10
+ else
11
+ # Consider attributes loaded when passed in through constructor
12
+ @loaded = true
13
+ attributes
14
+ end
15
+ end
16
+
17
+ # The id of the token
18
+ def token
19
+ @attributes[:token]
20
+ end
21
+ alias_method :id, :token
22
+ alias_method :access_token, :token
23
+
24
+ attribute_reader :expires_at, :client
25
+
26
+ # Tokens can't be loaded. What you see is what you get...
27
+ def get_attributes
28
+ @loaded = true
29
+ @attributes
30
+ end
31
+
32
+ # Text representation of the token, masking the secret part
33
+ #
34
+ # @return [String]
35
+ def inspect
36
+ inspected = super
37
+
38
+ # Only show last 4 of token, secret
39
+ if id
40
+ inspected = inspected.gsub! id, "#{'*'*36}#{id[36..-1]}"
41
+ end
42
+
43
+ inspected
44
+ end
45
+
46
+ # Create a Particle token
47
+ # @param username [String] The username (email) used to log in to
48
+ # the Particle Cloud API
49
+ # @param password [String] The password used to log in to
50
+ # the Particle Cloud API
51
+ def create(username, password)
52
+ @client.create_token(username, password)
53
+ end
54
+
55
+ # Remove a Particle token
56
+ # @param username [String] The username (email) used to log in to
57
+ # the Particle Cloud API
58
+ # @param password [String] The password used to log in to
59
+ # the Particle Cloud API
60
+ def remove(username, password)
61
+ @client.remove_token(username, password, self)
62
+ end
63
+
64
+ def self.list_path
65
+ "v1/access_tokens"
66
+ end
67
+
68
+ def self.create_path
69
+ "/oauth/token"
70
+ end
71
+
72
+ def path
73
+ "/v1/access_tokens/#{access_token}"
74
+ end
75
+ end
76
+ end
@@ -1,3 +1,3 @@
1
1
  module Particle
2
- VERSION = "0.0.1".freeze
2
+ VERSION = "0.0.2".freeze
3
3
  end
@@ -0,0 +1,62 @@
1
+ require 'particle/model'
2
+
3
+ module Particle
4
+
5
+ # Domain model for one Particle device
6
+ class Webhook < Model
7
+ def initialize(client, attributes)
8
+ super(client, attributes)
9
+ end
10
+
11
+ attribute_reader :url, :deviceID, :event, :created_at, :mydevices,
12
+ :requestType, :headers, :json, :query, :auth
13
+
14
+ # The response of the web server to a test message
15
+ # If nil, check error
16
+ def response
17
+ get_attributes unless @loaded
18
+ @response
19
+ end
20
+
21
+ # The error from the web server to a test message
22
+ # If nil, check response
23
+ def error
24
+ get_attributes unless @loaded
25
+ @error
26
+ end
27
+
28
+ # Force reloading the attributes for the webhook
29
+ def get_attributes
30
+ @loaded = true
31
+ result = @client.webhook_attributes(self)
32
+ @response = result[:response]
33
+ @error = result[:error]
34
+ @attributes = result[:webhook]
35
+ end
36
+
37
+ # Add a Particle webhook
38
+ def create
39
+ new_webhook = @client.create_webhook(@attributes)
40
+ @attributes = new_webhook.attributes
41
+ self
42
+ end
43
+
44
+ # Remove a Particle webhook
45
+ def remove
46
+ @client.remove_webhook(self)
47
+ end
48
+
49
+ def self.list_path
50
+ "v1/webhooks"
51
+ end
52
+
53
+ def self.create_path
54
+ "v1/webhooks"
55
+ end
56
+
57
+ def path
58
+ "/v1/webhooks/#{id}"
59
+ end
60
+ end
61
+ end
62
+
data/lib/particle.rb CHANGED
@@ -11,6 +11,25 @@ module Particle
11
11
  @client = Particle::Client.new(options)
12
12
  end
13
13
 
14
+ # Authenticate with Particle and start using the new token as the
15
+ # global token
16
+ #
17
+ # Delegates actual functionality to the client.
18
+ #
19
+ # @param username [String] The username (email) used to log in to
20
+ # the Particle Cloud API
21
+ # @param password [String] The password used to log in to
22
+ # the Particle Cloud API
23
+ # @param options [Hash] Additional Particle Cloud API options to
24
+ # create the token.
25
+ # @return [Token] The token object
26
+ # @see {Particle::Client::Tokens#login}
27
+ def login(username, password, options = {})
28
+ token = client.login(username, password, options)
29
+ self.access_token = token.access_token
30
+ token
31
+ end
32
+
14
33
  private
15
34
 
16
35
  def respond_to_missing?(method_name, include_private = false)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: particlerb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julien Vanier
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-25 00:00:00.000000000 Z
11
+ date: 2015-06-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sawyer
@@ -149,13 +149,20 @@ files:
149
149
  - lib/particle.rb
150
150
  - lib/particle/client.rb
151
151
  - lib/particle/client/devices.rb
152
+ - lib/particle/client/publish.rb
153
+ - lib/particle/client/tokens.rb
154
+ - lib/particle/client/webhooks.rb
152
155
  - lib/particle/configurable.rb
153
156
  - lib/particle/connection.rb
154
157
  - lib/particle/default.rb
155
158
  - lib/particle/device.rb
156
159
  - lib/particle/error.rb
160
+ - lib/particle/event.rb
161
+ - lib/particle/model.rb
157
162
  - lib/particle/response/raise_error.rb
163
+ - lib/particle/token.rb
158
164
  - lib/particle/version.rb
165
+ - lib/particle/webhook.rb
159
166
  - lib/particlerb.rb
160
167
  - particlerb.gemspec
161
168
  homepage: https://github.com/monkbroc/particlerb