geotrigger 0.0.4 → 0.0.6
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 +4 -4
- data/.gitignore +1 -0
- data/README.md +169 -0
- data/lib/ext/string.rb +6 -5
- data/lib/geotrigger.rb +31 -2
- data/lib/geotrigger/ago/session.rb +70 -2
- data/lib/geotrigger/application.rb +59 -0
- data/lib/geotrigger/device.rb +26 -0
- data/lib/geotrigger/model.rb +60 -0
- data/lib/geotrigger/session.rb +88 -2
- data/lib/geotrigger/tag.rb +37 -0
- data/lib/geotrigger/trigger.rb +36 -0
- data/lib/geotrigger/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b80b540534f9cbb0793eca389c041ac27eea153c
|
4
|
+
data.tar.gz: 1100a93307a68ab21ff168fad392c21a64191820
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b80200b1e85cdd48327cecfc7fc684523a9190466f299f76f0a471290417696eca695e24e41b9b4564a7c9d4255551c45ddd6472f6771bcabd8a6865dfd7306a
|
7
|
+
data.tar.gz: 8939b512abcb68f664115143aef14d35b2b9a35dabce8605d5fea0f77fe5a1f813fd667b2b03c43de1f687635829837fbd910f2606a7d79b25c6c1425b9f3f19
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -2,3 +2,172 @@ geotrigger-ruby
|
|
2
2
|
===============
|
3
3
|
|
4
4
|
a small ruby client for https://developers.arcgis.com/en/geotrigger-service/.
|
5
|
+
|
6
|
+
# Install
|
7
|
+
|
8
|
+
`gem install geotrigger`
|
9
|
+
|
10
|
+
# Usage
|
11
|
+
|
12
|
+
## Session
|
13
|
+
|
14
|
+
`Geotrigger::Session` is the main interface to the Geotrigger API. Once
|
15
|
+
created, it serves as the underlying support to all the Model subclasses.
|
16
|
+
Its main features are:
|
17
|
+
|
18
|
+
* wrapping communication with the Geotrigger API
|
19
|
+
* handling all `access_token` negotiation with ArcGIS Online
|
20
|
+
|
21
|
+
See also [API Doc](http://www.rubydoc.info/gems/geotrigger/Geotrigger/Session)
|
22
|
+
|
23
|
+
To create a `Session`, call `#new` with a optional Hash:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
# specify client_id and client_secret
|
27
|
+
#
|
28
|
+
session = Geotrigger::Session.new client_id: 'abcde', client_secret: '12345'
|
29
|
+
#=> <Geotrigger::Session ... >
|
30
|
+
|
31
|
+
# specify client_id and :device type
|
32
|
+
# (registers as a new device)
|
33
|
+
#
|
34
|
+
session = Geotrigger::Session.new client_id: 'abcde', type: :device
|
35
|
+
#=> <Geotrigger::Session ... >
|
36
|
+
|
37
|
+
# specify client_id, refresh_token, and :device type
|
38
|
+
# (gets access_tokens for an existing device)
|
39
|
+
#
|
40
|
+
session = Geotrigger::Session.new client_id: 'abcde', refresh_token: 'qwert', type: :device
|
41
|
+
#=> <Geotrigger::Session ... >
|
42
|
+
|
43
|
+
# reads config from ~/.geotrigger YAML
|
44
|
+
#
|
45
|
+
session = Geotrigger::Session.new
|
46
|
+
#=> <Geotrigger::Session ... >
|
47
|
+
|
48
|
+
# reads config from ~/.geotrigger YAML :dev key
|
49
|
+
#
|
50
|
+
session = Geotrigger::Session.new config: :dev
|
51
|
+
#=> <Geotrigger::Session ... >
|
52
|
+
```
|
53
|
+
|
54
|
+
You can then POST to any route in the API, without worring about managing
|
55
|
+
`access_token` lifecycles, with a normal Ruby `Hash` for both request
|
56
|
+
parameters and additional headers. The return value will be a normal Ruby
|
57
|
+
`Hash` parsed from the JSON response of the API. A `GeotriggerError` may be
|
58
|
+
raised if there was a problem with your request parameters or soemthing else.
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
session.post 'trigger/list'
|
62
|
+
#=> { "triggers" => [] }
|
63
|
+
|
64
|
+
session.post 'device/update', deviceIds: ['abcd1234'], addTags: ['foo']
|
65
|
+
#=> { "devices" => [ { "deviceId" => "abcd1234", tags: ["foo", "device:abcd1234", ...] } ] }
|
66
|
+
|
67
|
+
begin
|
68
|
+
session.post 'device/update', bad_param: false
|
69
|
+
rescue Geotrigger::GeotriggerError => ge
|
70
|
+
|
71
|
+
ge.parameters
|
72
|
+
#=> {"bad_param"=>[{"type"=>"invalid", "message"=>"Not a valid parameter for this request."}]}
|
73
|
+
|
74
|
+
end
|
75
|
+
```
|
76
|
+
|
77
|
+
You can do all of what you need to through this interface, but there are some
|
78
|
+
more lightweight utility classes for those that prefer a more OO approach.
|
79
|
+
|
80
|
+
## Models
|
81
|
+
|
82
|
+
These classes provide a ORM-ish interface to the Geotrigger API objects
|
83
|
+
Application, Trigger, Tag, and Device.
|
84
|
+
|
85
|
+
### Application
|
86
|
+
|
87
|
+
Application objects offer top-level access to various other model objects,
|
88
|
+
as well as updating of [Application](https://developers.arcgis.com/geotrigger-service/api-reference/application/) specific settings.
|
89
|
+
|
90
|
+
See also [API Doc](http://www.rubydoc.info/gems/geotrigger/Geotrigger/Application)
|
91
|
+
|
92
|
+
```
|
93
|
+
a = Geotrigger::Application.new client_id: 'abcde', client_secret: '12345'
|
94
|
+
#=> <Geotrigger::Application ... >
|
95
|
+
|
96
|
+
d = a.devices(tags: ['foo']).first
|
97
|
+
#=> <Geotrigger::Device ... >
|
98
|
+
|
99
|
+
geojson = JSON.parse File.load 'something.geojson'
|
100
|
+
t = a.triggers(geo: {geojson: geosjon}).first
|
101
|
+
#=> <Geotrigger::Trigger ... >
|
102
|
+
|
103
|
+
tag = a.tags.first
|
104
|
+
#=> <Geotrigger::Tag ... >
|
105
|
+
```
|
106
|
+
|
107
|
+
### Trigger
|
108
|
+
|
109
|
+
Trigger objects offer access to all attributes of a [Trigger](https://developers.arcgis.com/geotrigger-service/api-reference/trigger/).
|
110
|
+
|
111
|
+
See also [API Doc](http://www.rubydoc.info/gems/geotrigger/Geotrigger/Trigger)
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
trigger.add_tags 'foo'
|
115
|
+
trigger.save
|
116
|
+
|
117
|
+
trigger.remove_tags 'bar'
|
118
|
+
trigger.properties = { foo: 'bar', baz: true, bat: 123 }
|
119
|
+
trigger.save
|
120
|
+
```
|
121
|
+
|
122
|
+
### Device
|
123
|
+
|
124
|
+
Device objects offer access to all attributes of a [Device](https://developers.arcgis.com/geotrigger-service/api-reference/device/).
|
125
|
+
|
126
|
+
See also [API Doc](http://www.rubydoc.info/gems/geotrigger/Geotrigger/Device)
|
127
|
+
|
128
|
+
```ruby
|
129
|
+
device.add_tags 'foo'
|
130
|
+
device.save
|
131
|
+
|
132
|
+
device.remove_tags 'bar'
|
133
|
+
device.properties = { foo: 'bar', baz: true, bat: 123 }
|
134
|
+
device.save
|
135
|
+
|
136
|
+
device.session.post 'location/update', locations: [{
|
137
|
+
longitude: -122,
|
138
|
+
latitude: 45,
|
139
|
+
accuracy: 10,
|
140
|
+
timestamp: DateTime.now.iso8601,
|
141
|
+
trackingProfile: 'adaptive'
|
142
|
+
}]
|
143
|
+
```
|
144
|
+
|
145
|
+
### Tag
|
146
|
+
|
147
|
+
Tag objects offer access to all attributes of a [Tag](https://developers.arcgis.com/geotrigger-service/api-reference/tag/).
|
148
|
+
|
149
|
+
See also [API Doc](http://www.rubydoc.info/gems/geotrigger/Geotrigger/Tag)
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
# create a new tag without applying it to any other objects
|
153
|
+
#
|
154
|
+
s = Geotrigger::Session.new
|
155
|
+
tag = Geotrigger::Tag.create s, name: 'foo', deviceTagging: false
|
156
|
+
#=> <Geotrigger::Tag ... >
|
157
|
+
```
|
158
|
+
|
159
|
+
Note that Tags are automatically created by the API, if needed, when added to a
|
160
|
+
Trigger or Device. This offers a way to create the Tag before applying it to
|
161
|
+
anything.
|
162
|
+
|
163
|
+
```ruby
|
164
|
+
a = Geotrigger::Application.new client_id: 'abcde', client_secret: '12345'
|
165
|
+
#=> <Geotrigger::Application ... >
|
166
|
+
|
167
|
+
tag = a.tags(tags: 'foo').first
|
168
|
+
#=> <Geotrigger::Tag ... >
|
169
|
+
|
170
|
+
tag.device_tagging = false
|
171
|
+
tag.trigger_list = false
|
172
|
+
tag.save
|
173
|
+
```
|
data/lib/ext/string.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
-
|
1
|
+
# basic string extensions
|
2
|
+
#
|
3
|
+
# these are pretty much ripped from:
|
4
|
+
#
|
5
|
+
# https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb
|
2
6
|
|
3
|
-
|
7
|
+
class String
|
4
8
|
|
5
|
-
# these are pretty much ripped from:
|
6
|
-
# https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb
|
7
|
-
#
|
8
9
|
def camelcase
|
9
10
|
string = self.sub(/^(?:(?=\b|[A-Z_])|\w)/) { $&.downcase }
|
10
11
|
string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" }
|
data/lib/geotrigger.rb
CHANGED
@@ -1,12 +1,39 @@
|
|
1
|
+
# Geotrigger - A small Ruby client for the Esri Geotrigger Service.
|
2
|
+
# Copyright 2014 Esri; Nakamura, Kenichi (knakamura@esri.com)
|
3
|
+
#
|
4
|
+
# https://developers.arcgis.com/geotrigger-service/
|
5
|
+
# http://www.esri.com/
|
6
|
+
#
|
7
|
+
# [author] Kenichi Nakamura (knakamura@esri.com)
|
8
|
+
# [license] http://www.apache.org/licenses/LICENSE-2.0.txt
|
9
|
+
|
1
10
|
require 'forwardable'
|
2
|
-
require 'httpclient'
|
11
|
+
require 'httpclient'
|
3
12
|
require 'json'
|
4
13
|
|
14
|
+
# HTTPClient's normal #inspect is quite large, ungainly in irb sessions
|
15
|
+
#
|
16
|
+
if $0 == 'irb'
|
17
|
+
class HTTPClient
|
18
|
+
alias_method :real_inspect, :inspect
|
19
|
+
def inspect; self.to_s; end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# The Geotrigger module is the main namespace for all things in this library.
|
24
|
+
#
|
5
25
|
module Geotrigger
|
26
|
+
|
27
|
+
# raised by AGOSession on error from ArcGIS Online API
|
28
|
+
#
|
6
29
|
class AGOError < StandardError; end
|
30
|
+
|
31
|
+
# raised by Session on error from Geotrigger API
|
32
|
+
#
|
7
33
|
class GeotriggerError < StandardError
|
8
|
-
attr_accessor :code, :headers, :message, :
|
34
|
+
attr_accessor :code, :headers, :message, :parameters
|
9
35
|
end
|
36
|
+
|
10
37
|
end
|
11
38
|
|
12
39
|
lib = File.expand_path '../..', __FILE__
|
@@ -21,3 +48,5 @@ require 'geotrigger/application'
|
|
21
48
|
require 'geotrigger/device'
|
22
49
|
require 'geotrigger/tag'
|
23
50
|
require 'geotrigger/trigger'
|
51
|
+
|
52
|
+
require 'geotrigger/version'
|
@@ -1,13 +1,46 @@
|
|
1
1
|
module Geotrigger
|
2
|
+
|
3
|
+
# namespace for interacting with the ArcGIS Online API.
|
4
|
+
#
|
2
5
|
module AGO
|
6
|
+
|
7
|
+
# AGO::Session is responsible for talking to the ArcGIS Online API.
|
8
|
+
#
|
9
|
+
# * retrieves a valid OAuth +access_token+, handling expiration
|
10
|
+
# * Application, via +client_credentials+
|
11
|
+
# * Device, via registration or +refresh_token+
|
12
|
+
# * registers a new Device given a +client_id+
|
13
|
+
#
|
14
|
+
# Generally, one interacts with only the +Session+, which retains an
|
15
|
+
# instance of this to deal with tokens.
|
16
|
+
#
|
3
17
|
class Session
|
18
|
+
|
4
19
|
extend ::Forwardable
|
5
20
|
def_delegators :@impl, :access_token, :access_token=, :ago_data, :device_data, :refresh_token
|
6
21
|
|
22
|
+
# Read the base URL for ArcGIS Online API from the environment,
|
23
|
+
# or use the default.
|
24
|
+
#
|
25
|
+
# If you have a different OAuth portal, you can:
|
26
|
+
#
|
27
|
+
# $ AGO_BASE_URL=http://example.com/path/ irb -rgeotrigger
|
28
|
+
#
|
29
|
+
# to have the client use that base URL.
|
30
|
+
#
|
7
31
|
AGO_BASE_URL = (ENV.key?('AGO_BASE_URL') ?
|
8
32
|
(ENV['AGO_BASE_URL'] + '%s') :
|
9
33
|
'https://www.arcgis.com/sharing/%s').freeze
|
10
34
|
|
35
|
+
# Determines underlying implementation type, and creates an HTTPClient
|
36
|
+
# instance.
|
37
|
+
#
|
38
|
+
# [opts] +Hash+ options for construction:
|
39
|
+
# [:client_id] +String+ OAuth client id
|
40
|
+
# [:client_secret] +String+ OAuth client secret
|
41
|
+
# [:refresh_token] +String+ OAuth refresh token
|
42
|
+
# [:type] +Symbol+ +:application+ (default) or +:device+
|
43
|
+
#
|
11
44
|
def initialize opts = {}
|
12
45
|
@hc = HTTPClient.new
|
13
46
|
@impl = case opts[:type] || :application
|
@@ -20,6 +53,8 @@ module Geotrigger
|
|
20
53
|
end
|
21
54
|
end
|
22
55
|
|
56
|
+
# Type of implementation as a symbol.
|
57
|
+
#
|
23
58
|
def type
|
24
59
|
case @impl
|
25
60
|
when Application
|
@@ -29,8 +64,12 @@ module Geotrigger
|
|
29
64
|
end
|
30
65
|
end
|
31
66
|
|
32
|
-
#
|
33
|
-
#
|
67
|
+
# HTTP the specified method to the specified path with the given params.
|
68
|
+
# JSON parse the response body or raise errors.
|
69
|
+
#
|
70
|
+
# [meth] +Symbol+ of the method to HTTP (:get,:post...)
|
71
|
+
# [path] +String+ path of the request ('/example/index.html')
|
72
|
+
# [params] +Hash+ parameters for the request
|
34
73
|
#
|
35
74
|
def hc meth, path, params
|
36
75
|
r = @hc.__send__ meth, AGO_BASE_URL % path, params.merge(f: 'json')
|
@@ -40,10 +79,17 @@ module Geotrigger
|
|
40
79
|
h
|
41
80
|
end
|
42
81
|
|
82
|
+
# Mixin for the AGO::Session underlying implementation.
|
83
|
+
#
|
43
84
|
module ExpirySet
|
44
85
|
|
86
|
+
# Number of seconds before expiration to refresh tokens.
|
87
|
+
#
|
45
88
|
TOKEN_EXPIRY_BUFFER = 10
|
46
89
|
|
90
|
+
# Sets a buffered +:expires_at+ for refreshing tokens. Generates this
|
91
|
+
# value from +Time.now+ and the supplied +expires_in+ value (seconds).
|
92
|
+
#
|
47
93
|
def wrap_token_retrieval &block
|
48
94
|
yield
|
49
95
|
expires_at = Time.now.to_i + @ago_data['expires_in']
|
@@ -53,6 +99,8 @@ module Geotrigger
|
|
53
99
|
|
54
100
|
end
|
55
101
|
|
102
|
+
# AGO::Session implementation for Applications
|
103
|
+
#
|
56
104
|
class Application
|
57
105
|
include ExpirySet
|
58
106
|
extend ::Forwardable
|
@@ -60,11 +108,15 @@ module Geotrigger
|
|
60
108
|
|
61
109
|
attr_reader :ago_data
|
62
110
|
|
111
|
+
# Accepts the abstract +AGO::Session+ and a +client_credentials+ Hash.
|
112
|
+
#
|
63
113
|
def initialize session, opts = {}
|
64
114
|
@session, @client_id, @client_secret =
|
65
115
|
session, opts[:client_id], opts[:client_secret]
|
66
116
|
end
|
67
117
|
|
118
|
+
# Returns a valid +access_token+. Gets a new one if +nil+ or expired.
|
119
|
+
#
|
68
120
|
def access_token
|
69
121
|
fetch_access_token if @ago_data.nil? or
|
70
122
|
(not @ago_data[:expires_at].nil? and
|
@@ -74,6 +126,8 @@ module Geotrigger
|
|
74
126
|
|
75
127
|
private
|
76
128
|
|
129
|
+
# Gets a new +access_token+.
|
130
|
+
#
|
77
131
|
def fetch_access_token
|
78
132
|
wrap_token_retrieval do
|
79
133
|
@ago_data = hc :post, 'oauth2/token',
|
@@ -85,6 +139,8 @@ module Geotrigger
|
|
85
139
|
|
86
140
|
end
|
87
141
|
|
142
|
+
# AGO::Session implementation for Devices
|
143
|
+
#
|
88
144
|
class Device
|
89
145
|
include ExpirySet
|
90
146
|
extend Forwardable
|
@@ -93,11 +149,17 @@ module Geotrigger
|
|
93
149
|
attr_accessor :refresh_token
|
94
150
|
attr_reader :ago_data
|
95
151
|
|
152
|
+
# Accepts the abstract +AGO::Session+ and a Hash with +:client_id+
|
153
|
+
# and +:refresh_token+ keys.
|
154
|
+
#
|
96
155
|
def initialize session, opts = {}
|
97
156
|
@session, @client_id, @refresh_token =
|
98
157
|
session, opts[:client_id], opts[:refresh_token]
|
99
158
|
end
|
100
159
|
|
160
|
+
# Returns a valid +access_token+. Registers a new Device with AGO if
|
161
|
+
# needed.
|
162
|
+
#
|
101
163
|
def access_token
|
102
164
|
if @ago_data.nil?
|
103
165
|
if @refresh_token.nil?
|
@@ -111,12 +173,16 @@ module Geotrigger
|
|
111
173
|
@ago_data['access_token']
|
112
174
|
end
|
113
175
|
|
176
|
+
# Fetches data from AGO about the device specified by this :access_token+.
|
177
|
+
#
|
114
178
|
def device_data
|
115
179
|
@device_data ||= hc(:get, 'portals/self', token: access_token)['deviceInfo']
|
116
180
|
end
|
117
181
|
|
118
182
|
private
|
119
183
|
|
184
|
+
# Registers as a new Device with AGO.
|
185
|
+
#
|
120
186
|
def register
|
121
187
|
wrap_token_retrieval do
|
122
188
|
data = hc :post, 'oauth2/registerDevice', client_id: @client_id, expiration: -1
|
@@ -129,6 +195,8 @@ module Geotrigger
|
|
129
195
|
end
|
130
196
|
end
|
131
197
|
|
198
|
+
# Gets a new +access_token+.
|
199
|
+
#
|
132
200
|
def refresh_access_token
|
133
201
|
wrap_token_retrieval do
|
134
202
|
@ago_data = hc :post, 'oauth2/token',
|
@@ -1,23 +1,82 @@
|
|
1
1
|
module Geotrigger
|
2
2
|
|
3
|
+
# Application objects offer top-level, ORM-ish access to various other model
|
4
|
+
# objects, as well as updating of application specific settings.
|
5
|
+
#
|
6
|
+
# a = Geotrigger::Application.new client_id: 'abcde', client_secret: '12345'
|
7
|
+
# #=> <Geotrigger::Application ...>
|
8
|
+
#
|
9
|
+
# # or
|
10
|
+
#
|
11
|
+
# s = Geotrigger::Session.new client_id: 'abcde', client_secret: '12345'
|
12
|
+
# a = Geotrigger::Application.new session: s
|
13
|
+
# #=> <Geotrigger::Application ...>
|
14
|
+
#
|
3
15
|
class Application < Model
|
4
16
|
|
17
|
+
# Return this application's default tag permissions as a +Hash+.
|
18
|
+
#
|
19
|
+
# a.permissions
|
20
|
+
# #=> {"deviceTagging"=>true, "deviceLocation"=>false, ... }
|
21
|
+
#
|
5
22
|
def permissions
|
6
23
|
post 'application/permissions'
|
7
24
|
end
|
8
25
|
|
26
|
+
# Update this application's default tag permissions with a +Hash+.
|
27
|
+
#
|
28
|
+
# a.permissions = { deviceTagging: false }
|
29
|
+
# #=> {:deviceTagging => false }
|
30
|
+
#
|
31
|
+
# a.permissions
|
32
|
+
# #=> {"deviceTagging"=>false, "deviceLocation"=>false, ... }
|
33
|
+
#
|
9
34
|
def permissions= perms
|
10
35
|
post 'application/permissions/update', perms
|
11
36
|
end
|
12
37
|
|
38
|
+
# Return an +Array+ of +Device+ model objects that belong to this
|
39
|
+
# application.
|
40
|
+
#
|
41
|
+
# [params] +Hash+ optional parameters to send with the request
|
42
|
+
#
|
43
|
+
# a.devices tags: 'foo'
|
44
|
+
# #=> [<Geotrigger::Device ...>, ...] # (devices that have the tag 'foo' on them)
|
45
|
+
#
|
46
|
+
# a.devices geo: { geojson: { type: "Feature", properties: nil, geometry: {
|
47
|
+
# type:"Polygon",coordinates:[[[-122.669085113593,45.4999973537201], ... ,[-122.669085113593,45.4999973537201]]]
|
48
|
+
# }}}
|
49
|
+
# #=> [<Geotrigger::Device ...>, ...] # (devices whose last location was inside the given polygon)
|
50
|
+
#
|
13
51
|
def devices params = {}
|
14
52
|
post_list 'devices', params
|
15
53
|
end
|
16
54
|
|
55
|
+
# Return an +Array+ of +Tag+ model objects that belong to this
|
56
|
+
# application.
|
57
|
+
#
|
58
|
+
# [params] +Hash+ optional parameters to send with the request
|
59
|
+
#
|
60
|
+
# a.tags
|
61
|
+
# #=> [<Geotrigger::Tag...>, ...]
|
62
|
+
#
|
17
63
|
def tags params = {}
|
18
64
|
post_list 'tags', params
|
19
65
|
end
|
20
66
|
|
67
|
+
# Return an +Array+ of +Trigger+ model objects that belong to this
|
68
|
+
# application.
|
69
|
+
#
|
70
|
+
# [params] +Hash+ optional parameters to send with the request
|
71
|
+
#
|
72
|
+
# a.triggers tags: 'foo'
|
73
|
+
# #=> [<Geotrigger::Trigger ...>, ...] # (triggers that have the tag 'foo' on them)
|
74
|
+
#
|
75
|
+
# a.triggers geo: { geojson: { type: "Feature", properties: nil, geometry: {
|
76
|
+
# type:"Polygon",coordinates:[[[-122.669085113593,45.4999973537201], ... ,[-122.669085113593,45.4999973537201]]]
|
77
|
+
# }}}
|
78
|
+
# #=> [<Geotrigger::Trigger ...>, ...] # (triggers whose condition polygon is inside the given polygon)
|
79
|
+
#
|
21
80
|
def triggers params = {}
|
22
81
|
post_list 'triggers', params
|
23
82
|
end
|
data/lib/geotrigger/device.rb
CHANGED
@@ -1,8 +1,23 @@
|
|
1
1
|
module Geotrigger
|
2
2
|
|
3
|
+
# +Device+ objects offer ORM-ish access to all attributes of a Device.
|
4
|
+
#
|
5
|
+
# device.add_tags 'foo'
|
6
|
+
# device.save
|
7
|
+
#
|
8
|
+
# device.remove_tags 'bar'
|
9
|
+
# device.properties = { foo: 'bar', baz: true, bat: 123 }
|
10
|
+
# device.save
|
11
|
+
#
|
3
12
|
class Device < Model
|
4
13
|
include Taggable
|
5
14
|
|
15
|
+
# Create a new +Device+ instance and load +@data+ from the API given a
|
16
|
+
# +Hash+ with options:
|
17
|
+
#
|
18
|
+
# [device_id] +String+ id of the device
|
19
|
+
# [tags] +Array+ name(s) of tag(s) to filter devices by
|
20
|
+
#
|
6
21
|
def initialize opts = {}
|
7
22
|
super opts
|
8
23
|
case session.type
|
@@ -17,10 +32,15 @@ module Geotrigger
|
|
17
32
|
end
|
18
33
|
end
|
19
34
|
|
35
|
+
# Return the +String+ of this device's default tag.
|
36
|
+
#
|
20
37
|
def default_tag
|
21
38
|
'device:%s' % deviceId
|
22
39
|
end
|
23
40
|
|
41
|
+
# POST the device's +@data+ to the API via 'device/update', and return
|
42
|
+
# the same object with the new +@data+ returned from API call.
|
43
|
+
#
|
24
44
|
def post_update
|
25
45
|
post_data = @data.dup
|
26
46
|
case @session.type
|
@@ -37,6 +57,12 @@ module Geotrigger
|
|
37
57
|
end
|
38
58
|
alias_method :save, :post_update
|
39
59
|
|
60
|
+
# Reads the data specific to this +Device+ from the API response and sets
|
61
|
+
# it in +@data+.
|
62
|
+
#
|
63
|
+
# [data] +Hash+ the API response
|
64
|
+
# [id] +String+ the id of the Device to pull out (first if nil)
|
65
|
+
#
|
40
66
|
def grok_self_from data, id = nil
|
41
67
|
if id == :first
|
42
68
|
@data = data['devices'].first
|
data/lib/geotrigger/model.rb
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
module Geotrigger
|
2
2
|
|
3
|
+
# Superclass for Geotrigger "objects" - +Application+, +Trigger+, +Device+,
|
4
|
+
# +Tag+.
|
5
|
+
#
|
6
|
+
# Contains the base logic for interacting with the Geotrigger API in an
|
7
|
+
# ORM-like fashion. Never instantiated directly by the user.
|
8
|
+
#
|
3
9
|
class Model
|
4
10
|
|
5
11
|
class StateError < StandardError; end
|
@@ -7,19 +13,39 @@ module Geotrigger
|
|
7
13
|
extend Forwardable
|
8
14
|
def_delegator :@session, :post
|
9
15
|
|
16
|
+
# The data behind the model object.
|
17
|
+
#
|
10
18
|
attr_accessor :data
|
19
|
+
|
20
|
+
# The +Session+ the model object uses to talk to the API.
|
21
|
+
#
|
11
22
|
attr_reader :session
|
12
23
|
|
24
|
+
# Create an instance of the subclassed model object from data retrieved
|
25
|
+
# from the API.
|
26
|
+
#
|
13
27
|
def self.from_api data, session
|
14
28
|
i = self.new session: session
|
15
29
|
i.data = data
|
16
30
|
return i
|
17
31
|
end
|
18
32
|
|
33
|
+
# Create an instance and from given options +Hash+.
|
34
|
+
#
|
35
|
+
# [:session] +Session+ underlying session to use when talking to the API
|
36
|
+
#
|
19
37
|
def initialize opts = {}
|
20
38
|
@session = opts[:session] || Session.new(opts)
|
21
39
|
end
|
22
40
|
|
41
|
+
# POST a request to this model's /list route, passing parameters. Returns
|
42
|
+
# a new instance of the model object with populated data via
|
43
|
+
# +Model.from_api+.
|
44
|
+
#
|
45
|
+
# [models] +String+ name of the model to request listed data for
|
46
|
+
# [params] +Hash+ parameters to send with the request
|
47
|
+
# [default_params] +Hash+ default parameters to merge +params+ into
|
48
|
+
#
|
23
49
|
def post_list models, params = {}, default_params = {}
|
24
50
|
model = models.sub /s$/, ''
|
25
51
|
params = default_params.merge params
|
@@ -28,6 +54,15 @@ module Geotrigger
|
|
28
54
|
end
|
29
55
|
end
|
30
56
|
|
57
|
+
# Allows snake_case accessor to top-level data values keyed by their
|
58
|
+
# camelCase counterparts. An attempt to be moar Rubyish.
|
59
|
+
#
|
60
|
+
# device.tracking_profile
|
61
|
+
# #=> 'adaptive'
|
62
|
+
#
|
63
|
+
# device.trackingProfile
|
64
|
+
# #=> 'adaptive'
|
65
|
+
#
|
31
66
|
def method_missing meth, *args
|
32
67
|
meth_s = meth.to_s
|
33
68
|
if meth_s =~ /=$/ and args.length == 1
|
@@ -47,6 +82,8 @@ module Geotrigger
|
|
47
82
|
end
|
48
83
|
end
|
49
84
|
|
85
|
+
# Compares underlying data for equality.
|
86
|
+
#
|
50
87
|
def == obj
|
51
88
|
if Model === obj
|
52
89
|
self.data == obj.data
|
@@ -55,22 +92,45 @@ module Geotrigger
|
|
55
92
|
end
|
56
93
|
end
|
57
94
|
|
95
|
+
# Mixin for +Trigger+ and +Device+ to add tag functionality.
|
96
|
+
#
|
58
97
|
module Taggable
|
59
98
|
|
99
|
+
# Returns this model's tags as an +Array+ of +Tag+ objects.
|
100
|
+
#
|
60
101
|
def tags params = {}
|
61
102
|
post_list 'tags', params, tags: @data['tags']
|
62
103
|
end
|
63
104
|
|
105
|
+
# Sets 'addTags' in this model's +@data+ for POSTing to the API via
|
106
|
+
# +Model#save+.
|
107
|
+
#
|
108
|
+
# device.add_tags 'foo', 'bar'
|
109
|
+
# device.save
|
110
|
+
#
|
64
111
|
def add_tags *names
|
65
112
|
@data['addTags'] = names.flatten
|
66
113
|
end
|
67
114
|
|
115
|
+
# Sets 'removeTags' in this model's +@data+ for POSTing to the API via
|
116
|
+
# +Model#save+.
|
117
|
+
#
|
118
|
+
# trigger.remove_tags 'foo', 'bar'
|
119
|
+
# trigger.save
|
120
|
+
#
|
68
121
|
def remove_tags *names
|
69
122
|
names = names.flatten
|
70
123
|
raise ArgumentError.new "default tag prohibited" if names.include? default_tag
|
71
124
|
@data['removeTags'] = names
|
72
125
|
end
|
73
126
|
|
127
|
+
|
128
|
+
# Sets 'setTags' in this model's +@data+ for POSTing to the API via
|
129
|
+
# +Model#save+.
|
130
|
+
#
|
131
|
+
# trigger.tags = ['foo', 'bar']
|
132
|
+
# trigger.save
|
133
|
+
#
|
74
134
|
def tags= *names
|
75
135
|
names = names.flatten
|
76
136
|
raise ArgumentError.new "default tag required" unless names.include? default_tag
|
data/lib/geotrigger/session.rb
CHANGED
@@ -1,9 +1,67 @@
|
|
1
|
+
# Geotrigger - A small Ruby client for the Esri Geotrigger Service.
|
2
|
+
# Copyright 2014 Esri; Nakamura, Kenichi (knakamura@esri.com)
|
3
|
+
#
|
4
|
+
# https://developers.arcgis.com/geotrigger-service/
|
5
|
+
# http://www.esri.com/
|
6
|
+
#
|
7
|
+
# [author] Kenichi Nakamura (knakamura@esri.com)
|
8
|
+
# [license] http://www.apache.org/licenses/LICENSE-2.0.txt
|
9
|
+
|
1
10
|
module Geotrigger
|
2
11
|
|
12
|
+
# +Session+ is the main interface to the Geotrigger API.
|
13
|
+
#
|
14
|
+
# Instances of it POST to the API and return the values using normal Ruby
|
15
|
+
# Hashes. Used by objects subclassed from +Model+.
|
16
|
+
#
|
17
|
+
# Example:
|
18
|
+
#
|
19
|
+
# session = Geotrigger::Session.new client_id: 'abcde', client_secret: '12345'
|
20
|
+
# session.post 'trigger/list'
|
21
|
+
# #=> { "triggers" => [ ... ] }
|
22
|
+
#
|
23
|
+
# device_session = Geotrigger::Session.new client_id: 'abcde', type: :device
|
24
|
+
# device_session.post 'device/update', addTags: ['foo']
|
25
|
+
# #=> { "devices" => [{ "deviceId" => '0987qwer', tags: ["device:0987qwer", "foo"], ... }] }
|
26
|
+
#
|
27
|
+
# device_session = Geotrigger::Session.new client_id: 'abcde', refresh_token: 'zxcvb', type: :device
|
28
|
+
# device_session.post 'device/list'
|
29
|
+
# #=> { "devices" => [{ "deviceId" => '1234zxcv', tags: ["device:1234zxcv"], ... }] }
|
30
|
+
#
|
31
|
+
# It can also read default values for the constructor options from
|
32
|
+
# +ENV['HOME']/.geotrigger+, which is YAML formatted like:
|
33
|
+
#
|
34
|
+
# :production:
|
35
|
+
# :client_id: abcde
|
36
|
+
# :client_secret: 12345
|
37
|
+
# :test:
|
38
|
+
# :client_id: qwert
|
39
|
+
# :client_secret: 67890
|
40
|
+
# :test_device:
|
41
|
+
# :client_id: qwert
|
42
|
+
# :refresh_token: 45678lmnop
|
43
|
+
# :type: :device
|
44
|
+
#
|
45
|
+
# It will load the first Hash it finds with value for key +:client_id+, or
|
46
|
+
# one specified with the +:config+ option like:
|
47
|
+
#
|
48
|
+
# # default to :production in the example
|
49
|
+
# s = Geotrigger::Session.new
|
50
|
+
#
|
51
|
+
# # specify the :test_device config
|
52
|
+
# s = Geotrigger::Session.new config: :test_device
|
53
|
+
#
|
3
54
|
class Session
|
4
55
|
extend Forwardable
|
5
56
|
def_delegator :@ago, :type
|
6
57
|
|
58
|
+
# Read the base URL for Geotrigger API from the environment, or use the
|
59
|
+
# default.
|
60
|
+
#
|
61
|
+
# $ GT_BASE_URL=http://example.com/path/ irb -rgeotrigger
|
62
|
+
#
|
63
|
+
# to have the client use that base URL.
|
64
|
+
#
|
7
65
|
BASE_URL = (ENV.key?('GT_BASE_URL') ?
|
8
66
|
(ENV['GT_BASE_URL'] + '%s') :
|
9
67
|
'https://geotrigger.arcgis.com/%s').freeze
|
@@ -12,6 +70,14 @@ module Geotrigger
|
|
12
70
|
|
13
71
|
attr_writer :access_token
|
14
72
|
|
73
|
+
# Creates a Geotrigger::Session instance. Valid key/values for +opts+ are:
|
74
|
+
#
|
75
|
+
# [:client_id] +String+ OAuth client id
|
76
|
+
# [:client_secret] +String+ OAuth client secret
|
77
|
+
# [:refresh_token] +String+ OAuth refresh token (used for +:device+ type)
|
78
|
+
# [:type] +Symbol+ +:application+ (default) or +:device+
|
79
|
+
# [:config] +Symbol+ key of options defaults in ~/.geotrigger
|
80
|
+
#
|
15
81
|
def initialize opts = {}
|
16
82
|
if opts[:config] or opts.empty?
|
17
83
|
if File.exist? USER_CONFIG
|
@@ -31,17 +97,31 @@ module Geotrigger
|
|
31
97
|
@hc = HTTPClient.new
|
32
98
|
end
|
33
99
|
|
100
|
+
# Returns a valid +access_token+. Gets a new one if +nil+ or expired.
|
101
|
+
#
|
34
102
|
def access_token
|
35
103
|
@access_token || @ago.access_token
|
36
104
|
end
|
37
105
|
|
106
|
+
# Returns default request headers optionally merged with specified others.
|
107
|
+
#
|
108
|
+
# [others] +Hash+ other headers to include
|
109
|
+
#
|
38
110
|
def headers others = {}
|
39
111
|
{
|
40
112
|
'Content-Type' => 'application/json',
|
41
|
-
'Authorization' => "Bearer #{access_token}"
|
113
|
+
'Authorization' => "Bearer #{access_token}",
|
114
|
+
'X-GT-Client-Name' => 'geotrigger-ruby',
|
115
|
+
'X-GT-Client-Version' => Geotrigger::VERSION
|
42
116
|
}.merge others
|
43
117
|
end
|
44
118
|
|
119
|
+
# POST an API request to the given path, with optional params and
|
120
|
+
# headers. Returns a normal Ruby +Hash+ of the response data.
|
121
|
+
#
|
122
|
+
# [params] +Hash+ parameters to include in the request (will be converted to JSON)
|
123
|
+
# [other_headers] +Hash+ headers to include in the request in addition to the defaults.
|
124
|
+
#
|
45
125
|
def post path, params = {}, other_headers = {}
|
46
126
|
r = @hc.post BASE_URL % path, params.to_json, headers(other_headers)
|
47
127
|
raise GeotriggerError.new r.body unless r.status == 200
|
@@ -50,20 +130,26 @@ module Geotrigger
|
|
50
130
|
h
|
51
131
|
end
|
52
132
|
|
133
|
+
# Creates and raises a +GeotriggerError+ from an API error response.
|
134
|
+
#
|
53
135
|
def raise_error error
|
54
136
|
ge = GeotriggerError.new error['message']
|
55
137
|
ge.code = error['code']
|
56
138
|
ge.headers = error['headers']
|
57
139
|
ge.message = error['message']
|
58
|
-
ge.
|
140
|
+
ge.parameters = error['parameters']
|
59
141
|
jj error
|
60
142
|
raise ge
|
61
143
|
end
|
62
144
|
|
145
|
+
# True if Session is for a Device.
|
146
|
+
#
|
63
147
|
def device?
|
64
148
|
type == :device
|
65
149
|
end
|
66
150
|
|
151
|
+
# True if Session is for an Application.
|
152
|
+
#
|
67
153
|
def application?
|
68
154
|
type == :application
|
69
155
|
end
|
data/lib/geotrigger/tag.rb
CHANGED
@@ -1,7 +1,18 @@
|
|
1
1
|
module Geotrigger
|
2
2
|
|
3
|
+
# +Tag+ objects offer ORM-ish access to all attributes of a Tag.
|
4
|
+
#
|
3
5
|
class Tag < Model
|
4
6
|
|
7
|
+
# Create a Tag with the given +Session+ and options. Note that Tags are
|
8
|
+
# automatically created by the API, if needed, when added to a Trigger
|
9
|
+
# or Device. This offers a way to create the Tag before applying it to
|
10
|
+
# anything.
|
11
|
+
#
|
12
|
+
# s = Geotrigger::Session.new
|
13
|
+
# tag = Geotrigger::Tag.create s, name: 'foo', deviceTagging: false
|
14
|
+
# #=> <Geotrigger::Tag ... >
|
15
|
+
#
|
5
16
|
def self.create session, opts
|
6
17
|
t = ::Geotrigger::Tag.new session: session
|
7
18
|
t.data = opts
|
@@ -9,6 +20,11 @@ module Geotrigger
|
|
9
20
|
t.post_create
|
10
21
|
end
|
11
22
|
|
23
|
+
# Create a new +Tag+ instance and load +@data+ from the API given a +Hash+
|
24
|
+
# with options:
|
25
|
+
#
|
26
|
+
# [name] +String+ name of the tag
|
27
|
+
#
|
12
28
|
def initialize opts = {}
|
13
29
|
super opts
|
14
30
|
if opts[:name] and @data.nil?
|
@@ -16,20 +32,35 @@ module Geotrigger
|
|
16
32
|
end
|
17
33
|
end
|
18
34
|
|
35
|
+
# Return an +Array+ of +Trigger+ objects in this Application that have this
|
36
|
+
# tag applied to them.
|
37
|
+
#
|
38
|
+
# [params] +Hash+ any additional parameters to include in the request (trigger/list)
|
39
|
+
#
|
19
40
|
def triggers params = {}
|
20
41
|
post_list 'triggers', params, tags: name
|
21
42
|
end
|
22
43
|
|
44
|
+
# Return an +Array+ of +Device+ objects in this Application that have this
|
45
|
+
# tag applied to them.
|
46
|
+
#
|
47
|
+
# [params] +Hash+ any additional parameters to include in the request (device/list)
|
48
|
+
#
|
23
49
|
def devices params = {}
|
24
50
|
post_list 'devices', params, tags: name
|
25
51
|
end
|
26
52
|
|
53
|
+
# Creates a tag by POSTing to tag/permissions/update with +@data+.
|
54
|
+
#
|
27
55
|
def post_create
|
28
56
|
post_data = @data.dup
|
29
57
|
grok_self_from post('tag/permissions/update', post_data), @data[:tags]
|
30
58
|
self
|
31
59
|
end
|
32
60
|
|
61
|
+
# POST the tag's +@data+ to the API via 'tag/permissions/update', and return
|
62
|
+
# the same object with the new +@data+ returned from API call.
|
63
|
+
#
|
33
64
|
def post_update
|
34
65
|
raise StateError.new 'device access_token prohibited' if @session.device?
|
35
66
|
post_data = @data.dup
|
@@ -39,6 +70,12 @@ module Geotrigger
|
|
39
70
|
end
|
40
71
|
alias_method :save, :post_update
|
41
72
|
|
73
|
+
# Reads the data specific to this +Tag+ from the API response and sets
|
74
|
+
# it in +@data+.
|
75
|
+
#
|
76
|
+
# [data] +Hash+ the API response
|
77
|
+
# [name] +String+ the name of the Tag to pull out
|
78
|
+
#
|
42
79
|
def grok_self_from data, name = nil
|
43
80
|
@data = data['tags'].select {|t| t['name'] == (name || @data['name'])}.first
|
44
81
|
end
|
data/lib/geotrigger/trigger.rb
CHANGED
@@ -1,16 +1,36 @@
|
|
1
1
|
module Geotrigger
|
2
2
|
|
3
|
+
# +Trigger+ objects offer ORM-ish access to all attributes of a Trigger.
|
4
|
+
#
|
5
|
+
# trigger.add_tags 'foo'
|
6
|
+
# trigger.save
|
7
|
+
#
|
8
|
+
# trigger.remove_tags 'bar'
|
9
|
+
# trigger.properties = { foo: 'bar', baz: true, bat: 123 }
|
10
|
+
# trigger.save
|
11
|
+
#
|
3
12
|
class Trigger < Model
|
4
13
|
include Taggable
|
5
14
|
|
6
15
|
CIRCLE_KEYS = %w[latitude longitude distance]
|
7
16
|
|
17
|
+
# Create a Trigger with the given +Session+ and options.
|
18
|
+
#
|
19
|
+
# s = Geotrigger::Session.new
|
20
|
+
# t = Geotrigger::Trigger.create s, condition: { ... }, action: { ... }, tags: ['foo']
|
21
|
+
# #=> <Geotrigger::Trigger ... >
|
22
|
+
#
|
8
23
|
def self.create session, opts
|
9
24
|
t = Trigger.new session: session
|
10
25
|
t.data = opts
|
11
26
|
t.post_create
|
12
27
|
end
|
13
28
|
|
29
|
+
# Create a new +Trigger+ instance and load +@data+ from the API given a
|
30
|
+
# +Hash+ with options:
|
31
|
+
#
|
32
|
+
# [tags] +Array+ name(s) of tag(s)
|
33
|
+
#
|
14
34
|
def initialize opts = {}
|
15
35
|
super opts
|
16
36
|
if opts[:trigger_id] and @data.nil?
|
@@ -18,16 +38,23 @@ module Geotrigger
|
|
18
38
|
end
|
19
39
|
end
|
20
40
|
|
41
|
+
# Return the +String+ of this trigger's default tag.
|
42
|
+
#
|
21
43
|
def default_tag
|
22
44
|
'trigger:%s' % triggerId
|
23
45
|
end
|
24
46
|
|
47
|
+
# Creates a trigger by POSTing to trigger/create with +@data+.
|
48
|
+
#
|
25
49
|
def post_create
|
26
50
|
post_data = @data.dup
|
27
51
|
@data = post 'trigger/create', post_data
|
28
52
|
self
|
29
53
|
end
|
30
54
|
|
55
|
+
# POST the trigger's +@data+ to the API via 'trigger/update', and return
|
56
|
+
# the same object with the new +@data+ returned from API call.
|
57
|
+
#
|
31
58
|
def post_update opts = {}
|
32
59
|
post_data = @data.dup
|
33
60
|
post_data['triggerIds'] = post_data.delete 'triggerId'
|
@@ -43,10 +70,19 @@ module Geotrigger
|
|
43
70
|
end
|
44
71
|
alias_method :save, :post_update
|
45
72
|
|
73
|
+
# Reads the data specific to this +Trigger+ from the API response and sets
|
74
|
+
# it in +@data+.
|
75
|
+
#
|
76
|
+
# [data] +Hash+ the API response
|
77
|
+
# [triggerId] +String+ the id of the trigger to pull out (first if nil)
|
78
|
+
#
|
46
79
|
def grok_self_from data, id = nil
|
47
80
|
@data = data['triggers'].select {|t| t['triggerId'] == (id || @data['triggerId'])}.first
|
48
81
|
end
|
49
82
|
|
83
|
+
# True if trigger is a "circle" type, meaning it has a point(longitude,latitude) and
|
84
|
+
# radius(distance) in its condition, rather than only a geojson or esrijson geometry.
|
85
|
+
#
|
50
86
|
def circle?
|
51
87
|
not CIRCLE_KEYS.map {|k| @data['condition']['geo'].keys.include? k}.select {|e| e}.empty?
|
52
88
|
end
|
data/lib/geotrigger/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: geotrigger
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kenichi Nakamura
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-02-
|
11
|
+
date: 2014-02-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: httpclient
|