cs 0.1.0beta3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +21 -0
- data/Gemfile +10 -0
- data/LICENSE +22 -0
- data/README.md +132 -0
- data/Rakefile +7 -0
- data/cs.gemspec +22 -0
- data/lib/commonsense-ruby-lib.rb +127 -0
- data/lib/commonsense-ruby-lib/auth/http.rb +117 -0
- data/lib/commonsense-ruby-lib/auth/oauth.rb +101 -0
- data/lib/commonsense-ruby-lib/end_point.rb +276 -0
- data/lib/commonsense-ruby-lib/end_point/group.rb +28 -0
- data/lib/commonsense-ruby-lib/end_point/sensor.rb +36 -0
- data/lib/commonsense-ruby-lib/end_point/sensor_data.rb +70 -0
- data/lib/commonsense-ruby-lib/end_point/user.rb +50 -0
- data/lib/commonsense-ruby-lib/error.rb +51 -0
- data/lib/commonsense-ruby-lib/relation.rb +233 -0
- data/lib/commonsense-ruby-lib/relation/sensor_data_relation.rb +116 -0
- data/lib/commonsense-ruby-lib/relation/sensor_relation.rb +162 -0
- data/lib/commonsense-ruby-lib/serializer.rb +20 -0
- data/lib/commonsense-ruby-lib/session.rb +105 -0
- data/lib/commonsense-ruby-lib/version.rb +3 -0
- data/spec/features/sensor_data_management_spec.rb +4 -0
- data/spec/features/sensor_management_spec.rb +105 -0
- data/spec/features/user_management_spec.rb +70 -0
- data/spec/lib/commonsense-ruby-lib/end_point/sensor_data_spec.rb +68 -0
- data/spec/lib/commonsense-ruby-lib/end_point/sensor_spec.rb +98 -0
- data/spec/lib/commonsense-ruby-lib/end_point/user_spec.rb +36 -0
- data/spec/lib/commonsense-ruby-lib/end_point_spec.rb +190 -0
- data/spec/lib/commonsense-ruby-lib/relation/sensor_data_relation_spec.rb +444 -0
- data/spec/lib/commonsense-ruby-lib/relation/sensor_relation_spec.rb +165 -0
- data/spec/lib/commonsense-ruby-lib/session_spec.rb +43 -0
- data/spec/lib/commonsense-ruby-lib_spec.rb +51 -0
- data/spec/spec_helper.rb +40 -0
- data/spec/support/spec_config.yml.sample +6 -0
- data/spec/support/vcr.rb +5 -0
- metadata +175 -0
@@ -0,0 +1,101 @@
|
|
1
|
+
require "oauth"
|
2
|
+
|
3
|
+
module CommonSense
|
4
|
+
module Auth
|
5
|
+
class OAuth
|
6
|
+
|
7
|
+
attr_accessor :response_body, :response_code, :response_headers, :errors, :consumer_key,
|
8
|
+
:consumer_secret, :access_token, :access_token_secret
|
9
|
+
|
10
|
+
def initialize(consumer_key, consumer_secret, access_token, access_token_secret, uri=nil)
|
11
|
+
@consumer_key = consumer_key
|
12
|
+
@consumer_secret = consumer_secret
|
13
|
+
@access_token = access_token
|
14
|
+
@access_token_secret = access_token_secret
|
15
|
+
oauth_base = ::OAuth::Consumer.new(consumer_key, consumer_secret, :site => uri)
|
16
|
+
@oauth = ::OAuth::AccessToken.new(oauth_base, access_token, access_token_secret)
|
17
|
+
reset
|
18
|
+
end
|
19
|
+
|
20
|
+
def oauth
|
21
|
+
@oauth
|
22
|
+
end
|
23
|
+
|
24
|
+
def get(path, query={}, headers = {})
|
25
|
+
reset
|
26
|
+
headers = default_headers.merge(headers)
|
27
|
+
response = oauth.get(path, headers)
|
28
|
+
parse_response(response)
|
29
|
+
@response_body
|
30
|
+
end
|
31
|
+
|
32
|
+
def post(path, body = '', headers = {})
|
33
|
+
reset
|
34
|
+
headers = default_headers.merge(headers)
|
35
|
+
response = oauth.post(path, body.to_json, headers)
|
36
|
+
parse_response(response)
|
37
|
+
|
38
|
+
@response_body
|
39
|
+
end
|
40
|
+
|
41
|
+
def put(path, body = '', headers = {})
|
42
|
+
reset
|
43
|
+
headers = default_headers.merge(headers)
|
44
|
+
response = oauth.put(path, body.to_json, headers)
|
45
|
+
parse_response(response)
|
46
|
+
|
47
|
+
@response_body
|
48
|
+
end
|
49
|
+
|
50
|
+
def delete(path, query={}, headers = {})
|
51
|
+
reset
|
52
|
+
headers = default_headers.merge(headers)
|
53
|
+
response = oauth.delete(path, headers)
|
54
|
+
parse_response(response)
|
55
|
+
|
56
|
+
@response_body
|
57
|
+
end
|
58
|
+
|
59
|
+
def head(path, headers = {})
|
60
|
+
reset
|
61
|
+
headers = default_headers.merge(headers)
|
62
|
+
response = oauth.head(path, headers)
|
63
|
+
parse_response(response)
|
64
|
+
|
65
|
+
@response_body
|
66
|
+
end
|
67
|
+
|
68
|
+
def base_uri=(uri = nil)
|
69
|
+
self.class.base_uri uri
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
private
|
74
|
+
def default_headers
|
75
|
+
{"Content-Type" => "application/json"}
|
76
|
+
end
|
77
|
+
|
78
|
+
def reset
|
79
|
+
@errors = nil
|
80
|
+
@response_code = nil
|
81
|
+
@response_body = nil
|
82
|
+
end
|
83
|
+
|
84
|
+
# convert the body to hash if response is "application/json"
|
85
|
+
def parse_response(response)
|
86
|
+
@response_body = response.content_type == "application/json" ? (JSON(response.body) rescue nil) : response.body
|
87
|
+
@response_code = response.code.to_i
|
88
|
+
|
89
|
+
@response_headers = response.to_hash
|
90
|
+
@response_headers.each do |k,v|
|
91
|
+
@response_headers[k] = v[0] rescue nil
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
if @response_code >= 400
|
96
|
+
@errors = [response_body['error']]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,276 @@
|
|
1
|
+
require 'commonsense-ruby-lib/serializer'
|
2
|
+
module CommonSense
|
3
|
+
module EndPoint
|
4
|
+
include CommonSense::Serializer
|
5
|
+
|
6
|
+
attr_accessor :session
|
7
|
+
|
8
|
+
def initialize(hash={})
|
9
|
+
from_hash(hash)
|
10
|
+
end
|
11
|
+
|
12
|
+
# generate a hash representation of this end point
|
13
|
+
def to_parameters
|
14
|
+
r = self.resource rescue nil
|
15
|
+
r.nil? ? self.to_h(false) : { self.resource => self.to_h(false) }
|
16
|
+
end
|
17
|
+
|
18
|
+
def inspect
|
19
|
+
inspection = self.to_h.collect {|k,v| "#{k}: #{v.inspect}"}.compact.join(", ")
|
20
|
+
"#<#{self.class} #{inspection}>"
|
21
|
+
end
|
22
|
+
|
23
|
+
# get value of property name
|
24
|
+
def parameter(name)
|
25
|
+
self.instance_variable_get(name)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Persist end point object to CS. It will create a new record on CS
|
29
|
+
# if it's a new object or it will update the object. It will throw an exception
|
30
|
+
# if it could not persist object to CS
|
31
|
+
#
|
32
|
+
# example for {Sensor} object:
|
33
|
+
#
|
34
|
+
# sensor = client.sensors.build
|
35
|
+
# sensor.name = "accelerometer"
|
36
|
+
# sensor.display_name = "Accelerometer"
|
37
|
+
# sensor.device_type = "BMA123"
|
38
|
+
# sensor.pager_type = "email"
|
39
|
+
# sensor.data_type = "json"
|
40
|
+
# sensor.data_structure = {"x-axis" => "Float", "y-axis" => "Float", "z-axis" => "Float"}
|
41
|
+
#
|
42
|
+
# sensor.save! # this will create new sensor on CS
|
43
|
+
# sensor.id # should give you the id of the sensor
|
44
|
+
#
|
45
|
+
# sensor.name = "accelerometer edit"
|
46
|
+
# sensor.save! # this will update the sensor
|
47
|
+
def save!
|
48
|
+
check_session!
|
49
|
+
|
50
|
+
if @id
|
51
|
+
self.update!
|
52
|
+
else
|
53
|
+
self.create!
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# it will persist data to CS just like {#save!} but it will return nil instead of exception
|
58
|
+
# if it encouter error while persiting data
|
59
|
+
def save
|
60
|
+
save! rescue nil
|
61
|
+
end
|
62
|
+
|
63
|
+
# Create a new end point object to CS. It will raise an exception if there is an error
|
64
|
+
#
|
65
|
+
# example for {Sensor} object:
|
66
|
+
#
|
67
|
+
# sensor = client.sensors.build
|
68
|
+
# sensor.name = "accelerometer"
|
69
|
+
# sensor.display_name = "Accelerometer"
|
70
|
+
# sensor.device_type = "BMA123"
|
71
|
+
# sensor.pager_type = "email"
|
72
|
+
# sensor.data_type = "json"
|
73
|
+
# sensor.data_structure = {"x-axis" => "Float", "y-axis" => "Float", "z-axis" => "Float"}
|
74
|
+
#
|
75
|
+
# sensor.create! # this will create new sensor on CS
|
76
|
+
# sensor.id # should give you the id of the sensor
|
77
|
+
def create!
|
78
|
+
parameter = self.to_parameters
|
79
|
+
res = session.post(post_url, parameter)
|
80
|
+
|
81
|
+
if session.response_code != 201
|
82
|
+
errors = session.errors rescue nil
|
83
|
+
raise Error::ResponseError, errors
|
84
|
+
end
|
85
|
+
|
86
|
+
location_header = session.response_headers["location"]
|
87
|
+
id = scan_header_for_id(location_header)
|
88
|
+
self.id = id[0] if id
|
89
|
+
|
90
|
+
true
|
91
|
+
end
|
92
|
+
|
93
|
+
# Create a new endpoint object to CS, just like {#create!} but it will return nil
|
94
|
+
# if there is an error.
|
95
|
+
def create
|
96
|
+
result = create! rescue nil
|
97
|
+
not result.nil?
|
98
|
+
end
|
99
|
+
|
100
|
+
# Retrieve Data from CS of the current object based on the id of the object.
|
101
|
+
# It will return an exception if there is an error
|
102
|
+
#
|
103
|
+
# example for {Sensor} object:
|
104
|
+
#
|
105
|
+
# sensor = client.sensors.build
|
106
|
+
# sensor.id = "1"
|
107
|
+
# sensor.retrieve!
|
108
|
+
def retrieve!
|
109
|
+
check_session!
|
110
|
+
raise Error::ResourceIdError unless @id
|
111
|
+
|
112
|
+
res = session.get(get_url)
|
113
|
+
if session.response_code != 200
|
114
|
+
errors = session.errors rescue nil
|
115
|
+
raise Error::ResponseError, errors
|
116
|
+
end
|
117
|
+
|
118
|
+
from_hash(res[resource.to_s]) if res
|
119
|
+
true
|
120
|
+
end
|
121
|
+
|
122
|
+
# alias for {#retrieve!}
|
123
|
+
def reload!
|
124
|
+
retieve!
|
125
|
+
end
|
126
|
+
|
127
|
+
# it will retrieve / reload current object form CS, just like {#retrieve!} but it
|
128
|
+
# will return nil instead of raise an exception if there is an error.
|
129
|
+
def retrieve
|
130
|
+
result = retrieve! rescue nil
|
131
|
+
not result.nil?
|
132
|
+
end
|
133
|
+
|
134
|
+
# alias for {#retrieve}
|
135
|
+
def reload
|
136
|
+
retrieve
|
137
|
+
end
|
138
|
+
|
139
|
+
# Update current end point object to CS. It will throw an exception if there is an error
|
140
|
+
#
|
141
|
+
# example for {Sensor} object:
|
142
|
+
#
|
143
|
+
# sensor = client.sensors.find(1)
|
144
|
+
# sensor.name = "new name"
|
145
|
+
# sensor.update!
|
146
|
+
def update!
|
147
|
+
check_session!
|
148
|
+
raise Error::ResourceIdError unless @id
|
149
|
+
|
150
|
+
parameter = self.to_parameters
|
151
|
+
res = session.put(put_url, parameter)
|
152
|
+
|
153
|
+
if session.response_code != 200
|
154
|
+
errors = session.errors rescue nil
|
155
|
+
raise Error::ResponseError, errors
|
156
|
+
end
|
157
|
+
|
158
|
+
true
|
159
|
+
end
|
160
|
+
|
161
|
+
# Update current end point object to CS, just like {#update!} but it will return nil
|
162
|
+
# if there is an error
|
163
|
+
def update
|
164
|
+
result = update! rescue nil
|
165
|
+
not result.nil?
|
166
|
+
end
|
167
|
+
|
168
|
+
|
169
|
+
# Delete the current end point object from CS. It will throw an exception if there is an error
|
170
|
+
#
|
171
|
+
# example for {Sensor} object:
|
172
|
+
#
|
173
|
+
# sensor = client.sensors.find(1)
|
174
|
+
# sensor.name = "new name"
|
175
|
+
# sensor.delete!
|
176
|
+
def delete!
|
177
|
+
check_session!
|
178
|
+
raise Error::ResourceIdError unless @id
|
179
|
+
|
180
|
+
res = session.delete(delete_url)
|
181
|
+
|
182
|
+
if session.response_code != 200
|
183
|
+
errors = session.errors rescue nil
|
184
|
+
raise Error::ResponseError, errors
|
185
|
+
end
|
186
|
+
|
187
|
+
self.id = nil
|
188
|
+
|
189
|
+
true
|
190
|
+
end
|
191
|
+
|
192
|
+
# Delete the current end point object from CS, just like {#delete!} but it will return nil
|
193
|
+
# if there is an error
|
194
|
+
def delete
|
195
|
+
result = delete! rescue nil
|
196
|
+
not result.nil?
|
197
|
+
end
|
198
|
+
|
199
|
+
# return the commonsense URL for method
|
200
|
+
# vaild value for method is `:get`, `:post`, `:put`, or `:delete`
|
201
|
+
def url_for(method, id=nil)
|
202
|
+
raise Error::ResourcesError if resources.nil?
|
203
|
+
url = self.class.class_variable_get("@@#{method}_url".to_sym)
|
204
|
+
url = url.sub(":id", "#{@id}") if id
|
205
|
+
url
|
206
|
+
end
|
207
|
+
|
208
|
+
protected
|
209
|
+
def scan_header_for_id(location_header)
|
210
|
+
location_header.scan(/.*\/#{resources}\/(.*)/)[0] if location_header
|
211
|
+
end
|
212
|
+
|
213
|
+
def post_url
|
214
|
+
url_for(:post)
|
215
|
+
end
|
216
|
+
|
217
|
+
def get_url
|
218
|
+
url_for(:get, self.id)
|
219
|
+
end
|
220
|
+
|
221
|
+
def put_url
|
222
|
+
url_for(:put, self.id)
|
223
|
+
end
|
224
|
+
|
225
|
+
def delete_url
|
226
|
+
url_for(:delete, self.id)
|
227
|
+
end
|
228
|
+
|
229
|
+
def self.included(base)
|
230
|
+
base.extend(ClassMethod)
|
231
|
+
end
|
232
|
+
|
233
|
+
def resource
|
234
|
+
self.class.class_variable_get(:@@resource)
|
235
|
+
rescue
|
236
|
+
raise Error::ResourceError, "'resource' is not set up for class : #{self.class}"
|
237
|
+
end
|
238
|
+
|
239
|
+
def resources
|
240
|
+
self.class.class_variable_get(:@@resources)
|
241
|
+
end
|
242
|
+
|
243
|
+
private
|
244
|
+
def check_session!
|
245
|
+
raise Error::SessionEmptyError unless @session
|
246
|
+
end
|
247
|
+
|
248
|
+
module ClassMethod
|
249
|
+
def attribute(*args)
|
250
|
+
attr_accessor *args
|
251
|
+
|
252
|
+
unless @attribute_set
|
253
|
+
@attribute_set = Set.new([:id])
|
254
|
+
attr_accessor :id
|
255
|
+
end
|
256
|
+
@attribute_set.merge(args)
|
257
|
+
end
|
258
|
+
|
259
|
+
def attribute_set
|
260
|
+
@attribute_set
|
261
|
+
end
|
262
|
+
|
263
|
+
def resources(resources)
|
264
|
+
class_variable_set(:@@resources, resources)
|
265
|
+
class_variable_set(:@@post_url, "/#{resources}.json")
|
266
|
+
class_variable_set(:@@get_url, "/#{resources}/:id.json")
|
267
|
+
class_variable_set(:@@put_url, "/#{resources}/:id.json")
|
268
|
+
class_variable_set(:@@delete_url, "/#{resources}/:id.json")
|
269
|
+
end
|
270
|
+
|
271
|
+
def resource(resource)
|
272
|
+
class_variable_set(:@@resource, resource)
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module CommonSense
|
2
|
+
module EndPoint
|
3
|
+
class Group
|
4
|
+
include EndPoint
|
5
|
+
|
6
|
+
attr_accessor :id, :name, :description, :public
|
7
|
+
# get groups that user belongs to
|
8
|
+
def current_groups(options={})
|
9
|
+
res = session.get("/groups.json", options)
|
10
|
+
return nil unless res
|
11
|
+
|
12
|
+
group_list = res['groups']
|
13
|
+
|
14
|
+
|
15
|
+
groups =[]
|
16
|
+
if group_list
|
17
|
+
group_list.each do |group|
|
18
|
+
g = Group.new(group)
|
19
|
+
g.session = session
|
20
|
+
groups << g
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
groups
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module CommonSense
|
2
|
+
module EndPoint
|
3
|
+
class Sensor
|
4
|
+
include EndPoint
|
5
|
+
|
6
|
+
attribute :name, :display_name, :device_type, :pager_type, :data_type, :data_structure
|
7
|
+
resources :sensors
|
8
|
+
resource :sensor
|
9
|
+
|
10
|
+
def initialize(hash={})
|
11
|
+
from_hash(hash)
|
12
|
+
if self.data_type == "json"
|
13
|
+
if self.data_structure && self.data_structure.kind_of?(String)
|
14
|
+
self.data_structure = JSON.parse(self.data_structure) rescue nil
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# overide Endpoint#to_parameters
|
20
|
+
def to_parameters
|
21
|
+
param = self.to_h(false)
|
22
|
+
if param[:data_type] == "json"
|
23
|
+
if param[:data_structure] && !param[:data_structure].kind_of?(String)
|
24
|
+
param[:data_structure] = param[:data_structure].to_json
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
{sensor: param}
|
29
|
+
end
|
30
|
+
|
31
|
+
def data
|
32
|
+
Relation::SensorDataRelation.new(self.id, self.session)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module CommonSense
|
2
|
+
module EndPoint
|
3
|
+
# This object represent a sensor data point
|
4
|
+
# usage example:
|
5
|
+
#
|
6
|
+
# client = CommonSense::Client.new
|
7
|
+
# client.login('username', 'password')
|
8
|
+
#
|
9
|
+
# # Find the first position sensor
|
10
|
+
# sensor = client.sensors.find_by_name(/position/).first
|
11
|
+
#
|
12
|
+
# # save data point
|
13
|
+
# data = sensor.data.build
|
14
|
+
# data.date = Time.now
|
15
|
+
# data.value = {"lux": 1}
|
16
|
+
# data.save!
|
17
|
+
#
|
18
|
+
# # more compact version
|
19
|
+
# sensor.data.build(date: Time.now, value: {"lux => 1}).save!
|
20
|
+
#
|
21
|
+
class SensorData
|
22
|
+
include CommonSense::EndPoint
|
23
|
+
|
24
|
+
attr_accessor :month, :week, :year
|
25
|
+
attribute :date, :value, :sensor_id
|
26
|
+
resource :data
|
27
|
+
|
28
|
+
def to_parameters
|
29
|
+
param = self.to_h(false)
|
30
|
+
param.delete(:sensor_id)
|
31
|
+
value = param[:value]
|
32
|
+
if value
|
33
|
+
param[:value] = value.to_json unless value.kind_of?(String) || value.kind_of?(Numeric)
|
34
|
+
end
|
35
|
+
|
36
|
+
{data: [param]}
|
37
|
+
end
|
38
|
+
|
39
|
+
# there is no currently end point for geting data by id
|
40
|
+
def retrieve!
|
41
|
+
raise Error::NotImplementedError, "There is no current end point to get sensor data by id"
|
42
|
+
end
|
43
|
+
|
44
|
+
# there is no currently end point for updating data
|
45
|
+
def update!
|
46
|
+
raise Error::NotImplementedError, "There is no current end point to update sensor data by id"
|
47
|
+
end
|
48
|
+
|
49
|
+
def scan_header_for_id(location_header)
|
50
|
+
location_header.scan(/.*\/sensors\/(.*)\/(.*)/)[1] if location_header
|
51
|
+
end
|
52
|
+
|
53
|
+
def post_url
|
54
|
+
"/sensors/#{sensor_id}/data.json"
|
55
|
+
end
|
56
|
+
|
57
|
+
def get_url
|
58
|
+
"/sensors/#{sensor_id}/data/#{id}.json"
|
59
|
+
end
|
60
|
+
|
61
|
+
def put_url
|
62
|
+
"/sensors/#{sensor_id}/data/#{id}.json"
|
63
|
+
end
|
64
|
+
|
65
|
+
def delete_url
|
66
|
+
"/sensors/#{sensor_id}/data/#{id}.json"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|