dgrid 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 +15 -0
- data/lib/dgrid.rb +1 -0
- data/lib/dgrid/api.rb +20 -0
- data/lib/dgrid/api/connection.rb +444 -0
- data/lib/dgrid/api/entity.rb +89 -0
- data/lib/dgrid/api/get_test_login_info.rb +19 -0
- data/lib/dgrid/api/gps_coordinates.rb +14 -0
- data/lib/dgrid/api/incident.rb +78 -0
- data/lib/dgrid/api/item.rb +60 -0
- data/lib/dgrid/api/keyword.rb +38 -0
- data/lib/dgrid/api/lens.rb +38 -0
- data/lib/dgrid/api/link.rb +40 -0
- data/lib/dgrid/api/named_entity.rb +20 -0
- data/lib/dgrid/api/organization.rb +28 -0
- data/lib/dgrid/api/person.rb +39 -0
- data/lib/dgrid/api/place.rb +47 -0
- data/lib/dgrid/api/workspace.rb +567 -0
- data/lib/dgrid/argument_validation.rb +17 -0
- data/lib/dgrid/set_members_from_hash.rb +41 -0
- metadata +90 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
OWM5ODE0NDQwYTg3YmI3OGI3YTI4NTk1ZGI5MzU3OTg4YWM3OWVlNA==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NjExMjkyNjY5ZGIxOTQyMWRhY2Q4M2Y1NDZlMTk1YzVjNDk4NjYwOA==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
OWJhZTA5MzQzN2ZkNDVhYjg2ZTAzZDNhYjQ0NDRjYjFkNzJiNzM2MzhhNjQw
|
10
|
+
YzMwYzJlMDY4N2JmMWMwMDA2YjAzYjE5M2I1YjAyYjhjYTk4OTI2MGFjYjE4
|
11
|
+
MjY1Yzk4ZjkwYTljMzE1YWUyYmU5NjM2MTMwNzU1YjQyOWQ5NzU=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
OWNhY2NiYTgxYzRiYTJkOGQ0MDIyZjA2Y2E1Yzk5MmYxZDE0MGY5N2U3Zjdh
|
14
|
+
NDc0MTI3NDI4NzZhOTEwZGRlNzJhZjdjYjAxNzJmOTIyNzg4NjNhMDBjNWQy
|
15
|
+
ZThkZmIzMjVhZDkyNmM3MDY2OTUxMzMzZjkyZDM2NjE0ODAwMGI=
|
data/lib/dgrid.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'dgrid/api'
|
data/lib/dgrid/api.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# gem dependencies
|
2
|
+
require 'set'
|
3
|
+
require 'date'
|
4
|
+
require 'dgrid/argument_validation'
|
5
|
+
require 'dgrid/set_members_from_hash'
|
6
|
+
|
7
|
+
# gem components
|
8
|
+
require 'dgrid/api/gps_coordinates'
|
9
|
+
require 'dgrid/api/connection'
|
10
|
+
require 'dgrid/api/entity'
|
11
|
+
require 'dgrid/api/named_entity'
|
12
|
+
require 'dgrid/api/person'
|
13
|
+
require 'dgrid/api/place'
|
14
|
+
require 'dgrid/api/organization'
|
15
|
+
require 'dgrid/api/incident'
|
16
|
+
require 'dgrid/api/item'
|
17
|
+
require 'dgrid/api/keyword'
|
18
|
+
require 'dgrid/api/lens'
|
19
|
+
require 'dgrid/api/link'
|
20
|
+
require 'dgrid/api/workspace'
|
@@ -0,0 +1,444 @@
|
|
1
|
+
|
2
|
+
require 'yaml'
|
3
|
+
require 'net/http/post/multipart'
|
4
|
+
|
5
|
+
module Dgrid
|
6
|
+
module API
|
7
|
+
require 'json'
|
8
|
+
class Workspace # just a forward declaration
|
9
|
+
end
|
10
|
+
|
11
|
+
class HTTPError < Exception
|
12
|
+
end
|
13
|
+
|
14
|
+
class NotFoundError < HTTPError
|
15
|
+
end
|
16
|
+
|
17
|
+
class ServerError < HTTPError
|
18
|
+
end
|
19
|
+
|
20
|
+
class AuthError < HTTPError
|
21
|
+
end
|
22
|
+
|
23
|
+
# Decorates URI::* with a params method that parses the query
|
24
|
+
# into a hash of parameters.
|
25
|
+
# Frankly this should be in URI::* and I am very tempted to
|
26
|
+
# monkey-patch it in there but Erik held me back. :)
|
27
|
+
class AugmentedURI
|
28
|
+
def initialize(url_string)
|
29
|
+
@delegate = URI.parse(url_string)
|
30
|
+
end
|
31
|
+
|
32
|
+
def method_missing(*args, &block)
|
33
|
+
@delegate.send(*args,&block)
|
34
|
+
end
|
35
|
+
|
36
|
+
def params
|
37
|
+
Hash[ @delegate.query.split('&').map {|x| x.split('=')} ]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class RestAdapter
|
42
|
+
def rest_post(path, params = {})
|
43
|
+
raise "you must override rest_post in subclasses"
|
44
|
+
end
|
45
|
+
|
46
|
+
def rest_get(path, params = {})
|
47
|
+
raise "you must override rest_get in subclasses"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class ServerRestAdapter < RestAdapter
|
52
|
+
require 'rest-client'
|
53
|
+
|
54
|
+
|
55
|
+
@@BASE_URLS = { :development => "http://localhost:3000",
|
56
|
+
:preview => "https://bridge-preview.decisiongrid.com/",
|
57
|
+
:production => "https://app.decisiongrid.com/" }
|
58
|
+
|
59
|
+
def initialize
|
60
|
+
@environment = select_environment
|
61
|
+
# FIXME: Remove this once the server has been made less picky.
|
62
|
+
# HACK
|
63
|
+
# Prior to 12/2013, the server rejected all requests from any
|
64
|
+
# unsupported browser. Forging chrome identity was a hack
|
65
|
+
# workaround for this ill-advised "feature" (bug).
|
66
|
+
@user_agent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36'
|
67
|
+
end
|
68
|
+
|
69
|
+
def rest_post(path, params = {})
|
70
|
+
full_url = "#{base_url}/#{path}"
|
71
|
+
debug_puts "about to post to #{full_url} with #{params.to_json}"
|
72
|
+
response = ::RestClient.post(full_url, params.to_json,
|
73
|
+
{ :content_type => :json,
|
74
|
+
:accept => :json,
|
75
|
+
:user_agent => @user_agent},
|
76
|
+
&method(:permit_redirects))
|
77
|
+
debug_puts "post response was #{response}"
|
78
|
+
process_rest_response(response)
|
79
|
+
end
|
80
|
+
|
81
|
+
def rest_get(path, params = {})
|
82
|
+
url_params_list = []
|
83
|
+
params.each { |k,v| url_params_list << "#{k}=#{v}" }
|
84
|
+
url_params = url_params_list.join('&')
|
85
|
+
full_url = (path =~ /^https?:\/\//) ? path : "#{base_url}/#{path}"
|
86
|
+
full_url += ("?#{url_params}") if url_params.length > 0
|
87
|
+
debug_puts "about to get #{full_url}"
|
88
|
+
response = RestClient.get(full_url,
|
89
|
+
{ :content_type => :json,
|
90
|
+
:accept => :json, :user_agent => @user_agent},
|
91
|
+
&method(:permit_redirects))
|
92
|
+
debug_puts "get response was #{response}"
|
93
|
+
process_rest_response(response)
|
94
|
+
end
|
95
|
+
|
96
|
+
def rest_delete(path, params = {})
|
97
|
+
url_params_list = []
|
98
|
+
params.each { |k,v| url_params_list << "#{k}=#{v}" }
|
99
|
+
url_params = url_params_list.join('&')
|
100
|
+
full_url = "#{base_url}/#{path}"
|
101
|
+
full_url += ("?#{url_params}") if url_params.length > 0
|
102
|
+
debug_puts "about to delete #{full_url}"
|
103
|
+
response = RestClient.delete(full_url,
|
104
|
+
{ :content_type => :json,
|
105
|
+
:accept => :json,
|
106
|
+
:user_agent => @user_agent},
|
107
|
+
&method(:permit_redirects))
|
108
|
+
debug_puts "delete response was #{response}"
|
109
|
+
process_rest_response(response, expect_json = false)
|
110
|
+
end
|
111
|
+
|
112
|
+
protected
|
113
|
+
attr_accessor :environment
|
114
|
+
|
115
|
+
# A block to pass to RestClient transactions that prevents them from freaking out
|
116
|
+
# about redirect responses.
|
117
|
+
def permit_redirects(response,request,result,&block)
|
118
|
+
redirect_response_codes = [301, 302, 307]
|
119
|
+
if redirect_response_codes.include? response.code
|
120
|
+
return response # do not throw exception just because of redirect
|
121
|
+
else
|
122
|
+
response.return!(request,result,&block) # do the usual thing for all other cases
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
def select_environment
|
128
|
+
env_str = ENV.include?('DGRID_ENV') ? ENV['DGRID_ENV'] : 'development'
|
129
|
+
case env_str.downcase
|
130
|
+
when 'development'
|
131
|
+
return :development
|
132
|
+
when 'preview'
|
133
|
+
return :preview
|
134
|
+
when 'production'
|
135
|
+
return :production
|
136
|
+
else
|
137
|
+
raise "invalid DGRID_ENV: #{env_str}"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def base_url
|
142
|
+
@@BASE_URLS[@environment]
|
143
|
+
end
|
144
|
+
|
145
|
+
def process_rest_response(response, expect_json = true)
|
146
|
+
case response.code
|
147
|
+
# TODO enumerate more codes if needed
|
148
|
+
when 400
|
149
|
+
raise NotFoundError
|
150
|
+
when 401
|
151
|
+
raise AuthError
|
152
|
+
when 500..599
|
153
|
+
raise ServerError
|
154
|
+
when 200..299
|
155
|
+
# various success codes
|
156
|
+
when 300..308
|
157
|
+
# TODO: should we really be accepting all of these
|
158
|
+
# redirects as success?
|
159
|
+
return Hash.new # avoid json parsing errors below
|
160
|
+
else
|
161
|
+
raise HTTPError
|
162
|
+
end
|
163
|
+
if expect_json
|
164
|
+
json = response.body
|
165
|
+
obj = JSON.parse(json)
|
166
|
+
obj
|
167
|
+
else
|
168
|
+
response.body
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def debug_puts(message)
|
173
|
+
STDERR.puts message
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
177
|
+
|
178
|
+
class Connection
|
179
|
+
include ::Dgrid::ArgumentValidation
|
180
|
+
include ::Dgrid::SetMembersFromHash
|
181
|
+
|
182
|
+
@@default_rest_adapter = ServerRestAdapter
|
183
|
+
def self.default_rest_adapter=(new_default)
|
184
|
+
@@default_rest_adapter = new_default
|
185
|
+
end
|
186
|
+
|
187
|
+
option :username, String, :required
|
188
|
+
option :password, String, :required
|
189
|
+
option :rest_adapter, RestAdapter
|
190
|
+
def initialize(options, &block)
|
191
|
+
@auth, other_members = split_hash(options,[:username,:password])
|
192
|
+
set_members_from_hash(other_members)
|
193
|
+
@rest_adapter ||= @@default_rest_adapter.new
|
194
|
+
|
195
|
+
confirm_authentication
|
196
|
+
yield self if block_given?
|
197
|
+
end
|
198
|
+
|
199
|
+
|
200
|
+
argument :name, String
|
201
|
+
def create_workspace(name, &block)
|
202
|
+
workspace = Workspace.new(self,name)
|
203
|
+
if has_multi_workspace?
|
204
|
+
workspace_params = rest_post('/workspaces', :name => name)
|
205
|
+
workspace.id = workspace_params['id']
|
206
|
+
@workspaces_response = nil # clear cache of workspaces_response
|
207
|
+
else
|
208
|
+
workspace.id = '0'
|
209
|
+
end
|
210
|
+
yield workspace if block_given?
|
211
|
+
workspace
|
212
|
+
end
|
213
|
+
|
214
|
+
#argument :entity, Entity
|
215
|
+
def create_entity(entity, workspace_id)
|
216
|
+
singular_name = entity.class.name.split('::').last.downcase # e.g Dgrid::API::Person => 'person'
|
217
|
+
plural_name = entity.class.pluralized
|
218
|
+
path_parts =[plural_name]
|
219
|
+
if has_multi_workspace?
|
220
|
+
path_parts = ['workspaces',workspace_id ] + path_parts
|
221
|
+
end
|
222
|
+
path = path_parts.join('/')
|
223
|
+
params = entity.to_hash
|
224
|
+
returned_params = rest_post(path,params)
|
225
|
+
entity_params = returned_params[singular_name]
|
226
|
+
raise "Did not get an id for new #{singular_name} #{entity.to_s} in #{entity_params.to_s}" unless entity_params.include?("id") && entity_params["id"]
|
227
|
+
entity.id = entity_params["id"]
|
228
|
+
entity
|
229
|
+
end
|
230
|
+
|
231
|
+
#argument :entity, Entity
|
232
|
+
#argument :workspace_id, String
|
233
|
+
def delete_entity_from_workspace(entity,workspace_id)
|
234
|
+
raise "Entity must have id to be deleted" unless entity.id
|
235
|
+
plural_name = entity.class.pluralized
|
236
|
+
path_parts =[plural_name, entity.id]
|
237
|
+
if has_multi_workspace?
|
238
|
+
path_parts = ['workspaces',workspace_id ] + path_parts
|
239
|
+
end
|
240
|
+
path = path_parts.join('/')
|
241
|
+
returned_params = rest_delete(path)
|
242
|
+
end
|
243
|
+
|
244
|
+
|
245
|
+
#argument :entity1, Entity
|
246
|
+
#argument :entity2, Entity
|
247
|
+
#argument :workspace_id, String
|
248
|
+
#option :link_type, String
|
249
|
+
def create_link(entity1, entity2, workspace_id, options)
|
250
|
+
# FIXME It is completely unknown if this is the correct url structure
|
251
|
+
raise UnimplementedFunctionality
|
252
|
+
path_parts =['links']
|
253
|
+
params = {:left_guid => left_entity.id, :right_guid => right_entity.id, :description => options[:link_type]}
|
254
|
+
if has_multi_workspace?
|
255
|
+
path_parts = ['workspaces',workspace_id ] + path_parts
|
256
|
+
end
|
257
|
+
path = path_parts.join('/')
|
258
|
+
returned_params = rest_post(path,params)
|
259
|
+
end
|
260
|
+
|
261
|
+
def attach_file_to_entity_in_workspace(entity,filename,workspace_id)
|
262
|
+
# TODO Need to support other entity types than incident
|
263
|
+
raise "Only attaching to incidents supported at this time" unless entity.is_a?(Incident)
|
264
|
+
|
265
|
+
raise "File #{filename} not found" unless File.exists?(filename)
|
266
|
+
raise "Cannot attach files to an unsaved #{entity.class.name} " if entity.new_record?
|
267
|
+
|
268
|
+
# TODO Use workspace-independent route when it becomes available in the server
|
269
|
+
new_attachment_path = "/workspaces/#{workspace_id}/"
|
270
|
+
new_attachment_path += "#{entity.class.pluralized}/#{entity.id}/attachments/new"
|
271
|
+
|
272
|
+
presigned_post = rest_get(new_attachment_path)
|
273
|
+
post_response = post_form_with_file(presigned_post, filename)
|
274
|
+
redirected_to = post_response.header['location']
|
275
|
+
# Need to parse the redirect url so we can augment the params with
|
276
|
+
# auth info in rest_get.
|
277
|
+
redirected_url = AugmentedURI.new(redirected_to)
|
278
|
+
redirection_path = redirected_url.path
|
279
|
+
redirection_params = redirected_url.params
|
280
|
+
redirection_response = rest_get(redirection_path,redirection_params)
|
281
|
+
end
|
282
|
+
|
283
|
+
|
284
|
+
# Make entity subordinate to another within the specified workspace
|
285
|
+
# argument :entity, Entity
|
286
|
+
# argument :other, Entity
|
287
|
+
# argument :workspace_id, String
|
288
|
+
def subordinate_entity_to_other_entity_in_workspace(entity, other, workspace_id)
|
289
|
+
raise "Cannot subordiante unsaved #{entity} to #{other.type} #{other}" if entity.new_record?
|
290
|
+
raise "Cannot subordiante #{entity} to unsaved #{other.type} #{other}" if other.new_record?
|
291
|
+
entity_type = entity.class.pluralized
|
292
|
+
path_parts =[other.class.pluralized, other.id, entity_type, entity.id,'add']
|
293
|
+
|
294
|
+
if has_multi_workspace?
|
295
|
+
path_parts = ['workspaces',workspace_id ] + path_parts
|
296
|
+
end
|
297
|
+
|
298
|
+
path = path_parts.join('/')
|
299
|
+
returned_params = rest_get(path)
|
300
|
+
end
|
301
|
+
|
302
|
+
# list of current workspace objects
|
303
|
+
def workspaces
|
304
|
+
result = []
|
305
|
+
if has_multi_workspace?
|
306
|
+
workspaces_response = workspaces_rest_call
|
307
|
+
workspaces_list = workspaces_response['workspaces']
|
308
|
+
workspaces_list.each do |ws_params|
|
309
|
+
ws = Workspace.new(self, ws_params['name'], ws_params['description'])
|
310
|
+
ws.id = ws_params['id']
|
311
|
+
result << ws
|
312
|
+
end
|
313
|
+
else
|
314
|
+
ws = Workspace.new(self,'Default Workspace');
|
315
|
+
ws.id = '0';
|
316
|
+
result << ws
|
317
|
+
end
|
318
|
+
result
|
319
|
+
end
|
320
|
+
|
321
|
+
def get_workspace(name)
|
322
|
+
workspace = self.workspaces.detect {|ws| ws.name.downcase == name.downcase}
|
323
|
+
yield workspace if block_given?
|
324
|
+
workspace
|
325
|
+
end
|
326
|
+
|
327
|
+
def get_in_workspace(workspace_id, type, base_path_parts = [] )
|
328
|
+
params = type == 'links'? {:flat => 1} : {}
|
329
|
+
path_parts = base_path_parts + [type]
|
330
|
+
if has_multi_workspace?
|
331
|
+
path_parts = ['workspaces', workspace_id] + path_parts
|
332
|
+
end
|
333
|
+
path = path_parts.join('/')
|
334
|
+
returned_params = rest_get(path, params)
|
335
|
+
|
336
|
+
# FIXME Remove this once the production bug is fixed.
|
337
|
+
# HACK
|
338
|
+
# This is a workaround for a production bug that existed for
|
339
|
+
# a few weeks in November of 2013
|
340
|
+
hack_to_work_around_lenses_items_index_change(returned_params)
|
341
|
+
|
342
|
+
|
343
|
+
# FIXME
|
344
|
+
# HACK
|
345
|
+
# This is an ugly hack to deal with inconsistency in REST results
|
346
|
+
# We should probably fix the REST routes and undo this
|
347
|
+
if returned_params.include?('item_ids') && base_path_parts.include?('lenses')
|
348
|
+
type = 'item_ids'
|
349
|
+
elsif returned_params.include?('incident_ids') && base_path_parts.include?('items')
|
350
|
+
type = 'incident_ids'
|
351
|
+
end
|
352
|
+
returned_params[type]
|
353
|
+
end
|
354
|
+
|
355
|
+
%w(items links people places organizations incidents keywords).each do |type|
|
356
|
+
define_method("get_#{type}_in_workspace") { |workspace_id|
|
357
|
+
get_in_workspace(workspace_id, type)
|
358
|
+
}
|
359
|
+
end
|
360
|
+
|
361
|
+
def get_incidents_in_item(workspace_id, item_id)
|
362
|
+
get_in_workspace(workspace_id, 'incidents', ['items', item_id])
|
363
|
+
end
|
364
|
+
|
365
|
+
def get_items_in_lens(workspace_id, lens_id)
|
366
|
+
get_in_workspace(workspace_id, 'items', ['lenses',lens_id])
|
367
|
+
end
|
368
|
+
|
369
|
+
protected
|
370
|
+
attr_accessor :auth, :rest_adapter
|
371
|
+
def workspaces_rest_call
|
372
|
+
if @workspaces_response.nil?
|
373
|
+
weird_required_params = {as_selection: 1}
|
374
|
+
@workspaces_response = rest_get('/workspaces', weird_required_params)
|
375
|
+
end
|
376
|
+
@workspaces_response
|
377
|
+
end
|
378
|
+
|
379
|
+
def post_form_with_file(post_form_params, filename)
|
380
|
+
raise "File #{filename} not found" unless File.exists?(filename)
|
381
|
+
post_params = post_form_params.clone
|
382
|
+
post_action = post_params.delete('action')
|
383
|
+
post_url = URI.parse(post_action)
|
384
|
+
mime_type = 'application/octet-stream'
|
385
|
+
post_params['file'] = UploadIO.new(File.new(filename), mime_type ,filename)
|
386
|
+
req = Net::HTTP::Post::Multipart.new(post_url.path, post_params)
|
387
|
+
n = Net::HTTP.new(post_url.host,post_url.port)
|
388
|
+
n.use_ssl = ('https' == post_url.scheme.downcase )
|
389
|
+
response = n.start do |http|
|
390
|
+
http.request(req)
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
def rest_post(path,params = {})
|
395
|
+
full_params = params.merge(@auth)
|
396
|
+
rest_adapter.rest_post(path,full_params)
|
397
|
+
end
|
398
|
+
|
399
|
+
def rest_get(path,params = {})
|
400
|
+
full_params = params.merge(@auth)
|
401
|
+
rest_adapter.rest_get(path,full_params)
|
402
|
+
end
|
403
|
+
|
404
|
+
def rest_delete(path,params = {})
|
405
|
+
full_params = params.merge(@auth)
|
406
|
+
rest_adapter.rest_delete(path,full_params)
|
407
|
+
end
|
408
|
+
|
409
|
+
def confirm_authentication
|
410
|
+
rest_get('/profile') # confirm proper credentials by trying to do something
|
411
|
+
end
|
412
|
+
|
413
|
+
def has_multi_workspace?
|
414
|
+
@workspace_mode ||= determine_workspace_mode
|
415
|
+
return :multi_workspace == @workspace_mode
|
416
|
+
end
|
417
|
+
|
418
|
+
def determine_workspace_mode
|
419
|
+
begin
|
420
|
+
workspaces_rest_call
|
421
|
+
return :multi_workspace
|
422
|
+
rescue NotFoundError => e
|
423
|
+
return :single_workspace
|
424
|
+
rescue RestClient::ResourceNotFound => e
|
425
|
+
return :single_workspace
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
# Transform data returned by one rest call back to its rightful
|
430
|
+
# form.
|
431
|
+
# There was a bug introduced in November 2013 which changed
|
432
|
+
# the structure of data returned by the /workspaces/id/lenses/id/items
|
433
|
+
# route. This method modifies the returned_params to look the way
|
434
|
+
# they should ( {'items_ids' => {....} } )
|
435
|
+
def hack_to_work_around_lenses_items_index_change(returned_params)
|
436
|
+
if returned_params.include?('lensings') && !returned_params.include?('item_ids')
|
437
|
+
returned_params['item_ids'] = returned_params['lensings'].map {|lensing| lensing['item_id']}
|
438
|
+
# STDERR.puts "transformed this: #{returned_params['lensings'].inspect} into this: #{returned_params['item_ids']}"
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
end
|
443
|
+
end
|
444
|
+
end # module Dgrid
|