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
@@ -0,0 +1,47 @@
|
|
1
|
+
module Dgrid
|
2
|
+
module API
|
3
|
+
class Place < NamedEntity
|
4
|
+
attr_accessor :lat_long
|
5
|
+
|
6
|
+
option :name, String, :required # unique for this workspace
|
7
|
+
option :description, String # typically an address
|
8
|
+
option :lat_long, GPSCoordinates # When known. Description will be geocoded if this is not supplied.
|
9
|
+
option :latitude, String # alternative input method for db restoration
|
10
|
+
option :longitude, String
|
11
|
+
option :elevation, String
|
12
|
+
def initialize(options)
|
13
|
+
parent_opts, my_opts = split_hash(options,[:name, :description])
|
14
|
+
super(parent_opts)
|
15
|
+
gps_opts, other_opts = split_hash(options,[:latitude, :longitude, :elevation])
|
16
|
+
set_members_from_hash(other_opts)
|
17
|
+
if !other_opts.include?(:lat_long) && !gps_opts.empty?
|
18
|
+
self.lat_long = coalesce_gps_opts(gps_opts)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
def self.db_fields
|
24
|
+
%w(id name description latitude longitude elevation)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.pluralized
|
28
|
+
'places'
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_hash
|
32
|
+
h = super
|
33
|
+
if lat_long
|
34
|
+
h.merge!({:latitude => lat_long.lat, :longitude => lat_long.long, :elevation => lat_long.elevation})
|
35
|
+
end
|
36
|
+
h
|
37
|
+
end
|
38
|
+
|
39
|
+
protected
|
40
|
+
def coalesce_gps_opts(gps_opts)
|
41
|
+
gps_opts[:elevation] ||= 0
|
42
|
+
gps_args = [:latitude,:longitude,:elevation].map {|k| gps_opts[k]}
|
43
|
+
GPSCoordinates.new(*gps_args)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,567 @@
|
|
1
|
+
module Dgrid
|
2
|
+
module API
|
3
|
+
class Workspace
|
4
|
+
include ArgumentValidation
|
5
|
+
include SetMembersFromHash
|
6
|
+
|
7
|
+
attr_accessor :id, :name, :description, :connection
|
8
|
+
|
9
|
+
argument :connection, Dgrid::API::Connection
|
10
|
+
argument :name, String
|
11
|
+
argument :description, String
|
12
|
+
def initialize(connection, name='DEFAULT WORKSPACE', description = "")
|
13
|
+
@id = nil
|
14
|
+
@connection = connection
|
15
|
+
@name = name
|
16
|
+
@item_ids = {} # lens_id => [item_id]
|
17
|
+
@entity_cache = Cache.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.pluralized
|
21
|
+
'workspaces'
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.singular
|
25
|
+
name.split('::').last.downcase
|
26
|
+
end
|
27
|
+
|
28
|
+
def type
|
29
|
+
self.class.name.split('::').last
|
30
|
+
end
|
31
|
+
|
32
|
+
argument :person, Dgrid::API::Person
|
33
|
+
def add_person(person)
|
34
|
+
add_entity(person)
|
35
|
+
end
|
36
|
+
|
37
|
+
argument :place, Dgrid::API::Place
|
38
|
+
def add_place(place)
|
39
|
+
add_entity(place)
|
40
|
+
end
|
41
|
+
|
42
|
+
argument :organization, Dgrid::API::Organization
|
43
|
+
def add_organization(organization)
|
44
|
+
add_entity(organization)
|
45
|
+
end
|
46
|
+
|
47
|
+
argument :item, Dgrid::API::Item
|
48
|
+
def add_item(item)
|
49
|
+
add_entity(item)
|
50
|
+
end
|
51
|
+
|
52
|
+
argument :incident, Dgrid::API::Incident
|
53
|
+
argument :item, Dgrid::API::Item
|
54
|
+
def add_incident(incident, item = nil)
|
55
|
+
if item
|
56
|
+
raise "Cannot add an Incident in a Workspace unless owning Item is already in the Workspace" unless item.in_workspace?(self)
|
57
|
+
raise "Cannot add an Incident to an unsaved Item" if item.new_record?
|
58
|
+
end
|
59
|
+
if incident.new_record?
|
60
|
+
incident = connection.create_entity(incident, self.id)
|
61
|
+
end
|
62
|
+
if !incident.in_workspace?(self)
|
63
|
+
incident.add_workspace(self)
|
64
|
+
end
|
65
|
+
if item
|
66
|
+
connection.subordinate_entity_to_other_entity_in_workspace(incident, item, self.id)
|
67
|
+
end
|
68
|
+
incident
|
69
|
+
end
|
70
|
+
|
71
|
+
argument :keyword, Dgrid::API::Keyword
|
72
|
+
def add_keyword(keyword)
|
73
|
+
add_entity(keyword)
|
74
|
+
end
|
75
|
+
|
76
|
+
def subordinate_entity_to_other_entity(entity, other)
|
77
|
+
connection.subordinate_entity_to_other_entity_in_workspace(entity, other, self.id)
|
78
|
+
@entity_cache.invalidate(Link)
|
79
|
+
end
|
80
|
+
|
81
|
+
#============
|
82
|
+
|
83
|
+
# TODO auto-generate this templatesque bullshit
|
84
|
+
def create_person(options = {})
|
85
|
+
person = add_person(Dgrid::API::Person.new(options))
|
86
|
+
person
|
87
|
+
end
|
88
|
+
|
89
|
+
def create_place(options = {})
|
90
|
+
place = add_place(Dgrid::API::Place.new(options))
|
91
|
+
place
|
92
|
+
end
|
93
|
+
|
94
|
+
def create_organization(options = {}, &block)
|
95
|
+
organization = add_organization(Dgrid::API::Organization.new(options))
|
96
|
+
yield organization if block_given?
|
97
|
+
organization
|
98
|
+
end
|
99
|
+
|
100
|
+
def create_item(options = {}, &block)
|
101
|
+
item = add_item(Dgrid::API::Item.new(options))
|
102
|
+
yield item if block_given?
|
103
|
+
item
|
104
|
+
end
|
105
|
+
|
106
|
+
argument :left_entity, Dgrid::API::Entity
|
107
|
+
argument :right_entity, Dgrid::API::Entity
|
108
|
+
option :link_type, String, :required
|
109
|
+
def link(left_entity, right_entity, options = {})
|
110
|
+
raise "Entity #{left_entity.to_s} is not in this workspace" unless left_entity.in_workspace?(self)
|
111
|
+
raise "Entity #{right_entity.to_s} is not in this workspace" unless right_entity.in_workspace?(self)
|
112
|
+
connection.create_link(left_entity, right_entity, self.id, options)
|
113
|
+
@entity_cache.invalidate(Link)
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
#============
|
118
|
+
|
119
|
+
def items
|
120
|
+
get_entities(Item)
|
121
|
+
end
|
122
|
+
def people
|
123
|
+
get_entities(Person)
|
124
|
+
end
|
125
|
+
def places
|
126
|
+
get_entities(Place)
|
127
|
+
end
|
128
|
+
|
129
|
+
def organizations
|
130
|
+
get_entities(Organization)
|
131
|
+
end
|
132
|
+
|
133
|
+
def links
|
134
|
+
get_entities(Link)
|
135
|
+
end
|
136
|
+
|
137
|
+
def lenses
|
138
|
+
get_entities(Lens)
|
139
|
+
end
|
140
|
+
|
141
|
+
def incidents
|
142
|
+
get_entities(Incident)
|
143
|
+
end
|
144
|
+
|
145
|
+
def keywords
|
146
|
+
get_entities(Keyword)
|
147
|
+
end
|
148
|
+
|
149
|
+
argument :lens, Dgrid::API::Lens
|
150
|
+
def item_ids_in_lens(lens)
|
151
|
+
item_ids = connection.get_items_in_lens(self.id, lens.id)
|
152
|
+
end
|
153
|
+
|
154
|
+
argument :lens, Dgrid::API::Item
|
155
|
+
def incident_ids_in_item(item)
|
156
|
+
incident_ids = connection.get_incidents_in_item(self.id, item.id)
|
157
|
+
end
|
158
|
+
|
159
|
+
def add_entity(entity)
|
160
|
+
if entity.new_record?
|
161
|
+
entity = connection.create_entity(entity,self.id)
|
162
|
+
end
|
163
|
+
if !entity.in_workspace?(self)
|
164
|
+
entity.add_workspace(self)
|
165
|
+
end
|
166
|
+
@entity_cache.invalidate(entity.class)
|
167
|
+
entity
|
168
|
+
end
|
169
|
+
|
170
|
+
def get_entities(klass)
|
171
|
+
pluralized_name = klass.pluralized
|
172
|
+
return @entity_cache.get(klass) if @entity_cache.include?(klass)
|
173
|
+
construction_fields = klass.db_fields
|
174
|
+
entities_params = connection.get_in_workspace(self.id, pluralized_name)
|
175
|
+
entities = []
|
176
|
+
entities_params.each do |entity_params|
|
177
|
+
construction_params, other_params = split_hash(entity_params, construction_fields)
|
178
|
+
entity = klass.new(change_string_keys_to_symbol_keys(construction_params))
|
179
|
+
entity.add_workspace(self)
|
180
|
+
entities << entity
|
181
|
+
end
|
182
|
+
@entity_cache.store(klass,entities)
|
183
|
+
end
|
184
|
+
|
185
|
+
def invalidate_cache(klass)
|
186
|
+
@entity_cache.invalidate(klass)
|
187
|
+
end
|
188
|
+
|
189
|
+
class Backup
|
190
|
+
include SetMembersFromHash
|
191
|
+
|
192
|
+
def initialize(workspace = nil)
|
193
|
+
raise "Backup is a pure virtual class. Use Backup.create instead of Backup.new" unless self.class != Backup
|
194
|
+
hashify_all_members(workspace) unless workspace.nil?
|
195
|
+
end
|
196
|
+
|
197
|
+
def self.current_valid_backup_class
|
198
|
+
# Backup_0_1
|
199
|
+
Backup_0_2
|
200
|
+
end
|
201
|
+
|
202
|
+
def current_valid_backup_class
|
203
|
+
self.class.current_valid_backup_class
|
204
|
+
end
|
205
|
+
|
206
|
+
def self.create(workspace)
|
207
|
+
self.current_valid_backup_class.new(workspace)
|
208
|
+
end
|
209
|
+
|
210
|
+
def self.entity_classes
|
211
|
+
raise "Pure Virtual self.entity_classes needs to be overridden in #{self.class.name}"
|
212
|
+
end
|
213
|
+
|
214
|
+
def entity_classes
|
215
|
+
self.class.entity_classes
|
216
|
+
end
|
217
|
+
|
218
|
+
def self.backed_up_classes
|
219
|
+
self.entity_classes
|
220
|
+
end
|
221
|
+
|
222
|
+
def backed_up_classes
|
223
|
+
self.class.backed_up_classes
|
224
|
+
end
|
225
|
+
|
226
|
+
def restore_into(workspace)
|
227
|
+
if self.class == current_valid_backup_class
|
228
|
+
do_restore(workspace)
|
229
|
+
else
|
230
|
+
restorable_backup = current_valid_backup_class.migrate(self)
|
231
|
+
restorable_backup.restore_into(workspace)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
def self.migrate(backup_of_wrong_class)
|
236
|
+
raise "Cannot migrate anything to class #{self.name}" unless self.predecessor
|
237
|
+
backup_of_predecessor_class = (backup_of_wrong_class.class == self.predecessor) ? backup_of_wrong_class : self.predecessor.migrate(backup_of_wrong_class)
|
238
|
+
migrated = self.new
|
239
|
+
migrated.migrate_from_predecessor(backup_of_predecessor_class)
|
240
|
+
migrated
|
241
|
+
end
|
242
|
+
|
243
|
+
def migrate_from_predecessor(predecessor)
|
244
|
+
raise "Pure Virtual migrate_from_predecessor needs to be overridden in #{self.class.name}"
|
245
|
+
end
|
246
|
+
|
247
|
+
|
248
|
+
protected
|
249
|
+
def self.predecessor
|
250
|
+
raise "Pure Virtual self.predecessor needs to be overridden in #{self.class.name}"
|
251
|
+
end
|
252
|
+
|
253
|
+
def do_restore(workspace)
|
254
|
+
raise "Pure Virtual do_restore needs to be overridden in #{self.class.name}"
|
255
|
+
end
|
256
|
+
|
257
|
+
def set_entities(klass, obj_list)
|
258
|
+
raise "argumet_error: expected array of #{klass.name}" unless obj_list.is_a?(Array) && (obj_list.empty? || obj_list.first.is_a?(klass))
|
259
|
+
set_entities_hash(klass,hashify_obj_list(obj_list))
|
260
|
+
end
|
261
|
+
|
262
|
+
def get_entities_hash(klass)
|
263
|
+
raise "invalid member type #{type}" unless backed_up_classes.include?(klass)
|
264
|
+
@members[klass.pluralized]
|
265
|
+
end
|
266
|
+
|
267
|
+
def set_entities_hash(klass,h)
|
268
|
+
raise "invalid member type #{type}" unless backed_up_classes.include?(klass)
|
269
|
+
raise "invalid hash #{h}" unless h.is_a?(Hash) && h.values.all? {|v| v.is_a?(Hash)}
|
270
|
+
@members[klass.pluralized] = h
|
271
|
+
end
|
272
|
+
|
273
|
+
def lensings(lens_id)
|
274
|
+
@members['lensings'][lens_id]
|
275
|
+
end
|
276
|
+
|
277
|
+
def set_lensings(lens_id,item_ids)
|
278
|
+
@members['lensings'][lens_id] = item_ids
|
279
|
+
end
|
280
|
+
|
281
|
+
def hashify_obj_list(obj_list)
|
282
|
+
result = {}
|
283
|
+
obj_list.each do |obj|
|
284
|
+
result[obj.id] = obj.to_hash
|
285
|
+
end
|
286
|
+
result
|
287
|
+
end
|
288
|
+
|
289
|
+
private
|
290
|
+
def hashify_all_members(workspace)
|
291
|
+
raise "Pure Virtual hashify_all_members needs to be overridden in #{self.class.name}"
|
292
|
+
end
|
293
|
+
|
294
|
+
end
|
295
|
+
|
296
|
+
# ==============
|
297
|
+
#
|
298
|
+
class Backup_0_1 < Backup
|
299
|
+
|
300
|
+
def self.entity_classes
|
301
|
+
@@entity_classes ||= Set.new(
|
302
|
+
[Item,
|
303
|
+
Person,
|
304
|
+
Place,
|
305
|
+
Organization,
|
306
|
+
Link,
|
307
|
+
Lens,
|
308
|
+
])
|
309
|
+
end
|
310
|
+
|
311
|
+
def self.backed_up_classes
|
312
|
+
self.entity_classes + [ Incident]
|
313
|
+
end
|
314
|
+
|
315
|
+
protected
|
316
|
+
def self.predecessor
|
317
|
+
nil
|
318
|
+
end
|
319
|
+
|
320
|
+
def do_restore(workspace)
|
321
|
+
@new_entities = {}
|
322
|
+
restore_entities_into(workspace)
|
323
|
+
restore_weird_special_cases_into(workspace)
|
324
|
+
restore_lenses_into(workspace)
|
325
|
+
restore_links_into(workspace)
|
326
|
+
end
|
327
|
+
|
328
|
+
def incidents_are_entities?
|
329
|
+
if @incidents_are_entities.nil?
|
330
|
+
@incidents_are_entities = entity_classes.include?(Incident)
|
331
|
+
end
|
332
|
+
return @incidents_are_entities
|
333
|
+
end
|
334
|
+
|
335
|
+
private
|
336
|
+
|
337
|
+
def incidents(item_id)
|
338
|
+
raise "The method incidents(item_id) is invalid in class #{self.class.name}" if incidents_are_entities?
|
339
|
+
@members[Incident.pluralized][item_id]
|
340
|
+
end
|
341
|
+
|
342
|
+
def set_incidents(item_id,incidents)
|
343
|
+
raise "The method set_incidents(item_id,incidents) is invalid in class #{self.class.name}" if incidents_are_entities?
|
344
|
+
raise "argument_error: expected String id" unless item_id.is_a?(String)
|
345
|
+
raise "argumet_error: expected array of Incident" unless incidents.is_a?(Array) && (incidents.empty? || incidents.first.is_a?(Dgrid::API::Incident))
|
346
|
+
@members[Incident.pluralized][item_id] = hashify_obj_list(incidents)
|
347
|
+
end
|
348
|
+
|
349
|
+
def hashify_all_members(workspace)
|
350
|
+
@members = {'lensings'=>{}}
|
351
|
+
@members[Incident.pluralized] = {} unless incidents_are_entities?
|
352
|
+
hashify_entities(workspace)
|
353
|
+
if !workspace.nil?
|
354
|
+
hashify_incidents(workspace) unless incidents_are_entities?
|
355
|
+
hashify_lensings(workspace)
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
def hashify_entities(workspace)
|
360
|
+
backed_up_classes.each do |klass|
|
361
|
+
if !get_entities_hash(klass)
|
362
|
+
set_entities(klass, workspace.nil? ? nil : workspace.send(klass.pluralized))
|
363
|
+
end
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
def hashify_incidents(workspace)
|
368
|
+
workspace.items.each do |item|
|
369
|
+
set_incidents(item.id,item.incidents)
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
def hashify_lensings(workspace)
|
374
|
+
workspace.lenses.each do |lens|
|
375
|
+
set_lensings(lens.id,lens.item_ids)
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
def restore_entities_into(workspace)
|
380
|
+
entity_classes.each do |entity_class|
|
381
|
+
next if entity_class.type == 'Link'
|
382
|
+
entity_hash = get_entities_hash(entity_class)
|
383
|
+
entity_hash.each do |old_id, entity_params|
|
384
|
+
new_entity = workspace.add_entity(entity_class.new(change_string_keys_to_symbol_keys(entity_params)))
|
385
|
+
entity_key =[entity_class.type,old_id]
|
386
|
+
@new_entities[entity_key] = new_entity
|
387
|
+
end
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
def restore_lenses_into(workspace)
|
392
|
+
get_entities_hash(Lens).each_pair do |old_lens_id, old_lens_params|
|
393
|
+
lens_key = [Lens.type, old_lens_id]
|
394
|
+
new_lens = @new_entities[lens_key]
|
395
|
+
raise "lens #{lens_key.inspect} not found in #{@new_entities.inspect}" unless new_lens
|
396
|
+
|
397
|
+
lensings(old_lens_id).each do |old_item_id|
|
398
|
+
item_key = [Item.type, old_item_id]
|
399
|
+
new_item = @new_entities[item_key]
|
400
|
+
raise "item #{item_key.inspect} not found in #{@new_entities.inspect}" unless new_item
|
401
|
+
new_lens.add_item(new_item)
|
402
|
+
end
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
def restore_links_into(workspace)
|
407
|
+
get_entities_hash(Link).each_pair do |old_link_id, old_link_params|
|
408
|
+
left_type = old_link_params[:left_type]
|
409
|
+
right_type = old_link_params[:right_type]
|
410
|
+
left_guid = old_link_params[:left_guid]
|
411
|
+
right_guid = old_link_params[:right_guid]
|
412
|
+
# Incident to Item links are covered during incident creation so we can skip those
|
413
|
+
new_left = @new_entities[[left_type,left_guid]]
|
414
|
+
new_right = @new_entities[[right_type,right_guid]]
|
415
|
+
if left_type == 'Incident' && right_type == 'Item'
|
416
|
+
if incidents_are_entities?
|
417
|
+
workspace.subordinate_entity_to_other_entity(new_left,new_right)
|
418
|
+
end
|
419
|
+
next
|
420
|
+
end
|
421
|
+
if right_type == 'Incident' && left_type == 'Item'
|
422
|
+
if incidents_are_entities?
|
423
|
+
workspace.subordinate_entity_to_other_entity(new_right,new_left)
|
424
|
+
end
|
425
|
+
next
|
426
|
+
end
|
427
|
+
|
428
|
+
STDERR.puts "Warning: Dangling link #{old_link_id}: #{old_link_params}" unless new_left && new_right
|
429
|
+
if new_left.is_a?(Dgrid::API::Incident) || (new_left.is_a?(Dgrid::API::Organization) && new_right.is_a?(Dgrid::API::Person))
|
430
|
+
workspace.subordinate_entity_to_other_entity(new_right,new_left)
|
431
|
+
elsif new_right.is_a?(Dgrid::API::Incident) || (new_right.is_a?(Dgrid::API::Organization) && new_left.is_a?(Dgrid::API::Person))
|
432
|
+
workspace.subordinate_entity_to_other_entity(new_left,new_right)
|
433
|
+
else
|
434
|
+
workspace.link(new_left,new_right, old_link_params[:description])
|
435
|
+
end
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
def restore_weird_special_cases_into(workspace)
|
440
|
+
get_entities_hash(Item).each_pair do |old_item_id, old_item_params|
|
441
|
+
item_key = [Item.type, old_item_id]
|
442
|
+
new_item = @new_entities[item_key]
|
443
|
+
raise "item #{item_key.inspect} not found in #{@new_entities.inspect}" unless new_item
|
444
|
+
incidents(old_item_id).each_pair do |old_incident_id, old_incident_params|
|
445
|
+
new_incident = workspace.add_incident(Incident.new(change_string_keys_to_symbol_keys(old_incident_params)), new_item)
|
446
|
+
incident_key =[Incident.type,old_incident_id]
|
447
|
+
@new_entities[incident_key] = new_incident
|
448
|
+
end
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
end # of class Backup_0_1
|
453
|
+
|
454
|
+
# ==============
|
455
|
+
#
|
456
|
+
class Backup_0_2 < Backup_0_1
|
457
|
+
|
458
|
+
def self.entity_classes
|
459
|
+
# This is the only real difference between versions 0_1 and 0_2
|
460
|
+
# Incident promoted to base-level entity and Keywords added.
|
461
|
+
@@entity_classes ||= predecessor.entity_classes + [Incident, Keyword]
|
462
|
+
end
|
463
|
+
|
464
|
+
def self.backed_up_classes
|
465
|
+
self.entity_classes
|
466
|
+
end
|
467
|
+
|
468
|
+
def migrate_from_predecessor(predecessor)
|
469
|
+
pred_entity_classes = Set.new(predecessor.class.entity_classes)
|
470
|
+
self.class.entity_classes.each do |entity_class|
|
471
|
+
if pred_entity_classes.include?(entity_class)
|
472
|
+
pred_entities = predecessor.get_entities_hash(entity_class)
|
473
|
+
set_entities_hash(my_entity_class,pred_entities.clone)
|
474
|
+
end
|
475
|
+
end
|
476
|
+
if self.incidents_are_entities? && !pred_entity_classes.include?(Incident)
|
477
|
+
migrate_item_subord_incidents_to_entity_incidents(predecessor)
|
478
|
+
end
|
479
|
+
end
|
480
|
+
|
481
|
+
|
482
|
+
protected
|
483
|
+
|
484
|
+
def self.predecessor
|
485
|
+
Backup_0_1
|
486
|
+
end
|
487
|
+
|
488
|
+
private
|
489
|
+
def restore_weird_special_cases_into(workspace)
|
490
|
+
# nothing to do, just overriding predecessor's implementation
|
491
|
+
end
|
492
|
+
|
493
|
+
def migrate_item_subord_incidents_to_entity_incidents(predecessor)
|
494
|
+
pred_item_ids = predecessor.get_entities_hash(Item).keys
|
495
|
+
incidents_hash = {}
|
496
|
+
links_hash = get_entities_hash(Link)
|
497
|
+
pred_item_ids.each do |item_id|
|
498
|
+
item_incidents = incidents(item_id)
|
499
|
+
item_incidents.each_pair do |incident_id,incident_params|
|
500
|
+
incidents_hash[incident_id] = incident_params
|
501
|
+
ensure_links_include_item_incident(links_hash, item_id,incident_id)
|
502
|
+
end
|
503
|
+
end
|
504
|
+
end
|
505
|
+
|
506
|
+
def ensure_links_include_item_incident(links_hash,item_id,incident_id)
|
507
|
+
if !links_include_relationship_between(links_hash,Item,item_id,Incident,incident_id)
|
508
|
+
new_link_id = generate_new_link_id
|
509
|
+
links_hash[new_link_id] = {
|
510
|
+
:left_type => Item.name,
|
511
|
+
:left_guid => item_id,
|
512
|
+
:right_type => Incident.guid,
|
513
|
+
:right_id => incident_id,
|
514
|
+
:description => 'item-incident'
|
515
|
+
}
|
516
|
+
end
|
517
|
+
end
|
518
|
+
def links_include_relationship_between(links_hash,type1,guid1,type2,guid2)
|
519
|
+
links_hash.values.any? { |link|
|
520
|
+
(link[:left_type] == type1 && link[:left_guid] == guid1 &&
|
521
|
+
link[:right_type] == type2 && link[:right_guid] == guid2) ||
|
522
|
+
(link[:right_type] == type1 && link[:right_guid] == guid1 &&
|
523
|
+
link[:left_type] == type2 && link[:left_guid] == guid2)
|
524
|
+
}
|
525
|
+
end
|
526
|
+
end # of class Backup_0_2
|
527
|
+
|
528
|
+
def backup()
|
529
|
+
backup = Backup.create(self)
|
530
|
+
end
|
531
|
+
|
532
|
+
argument :connection, Dgrid::API::Workspace
|
533
|
+
def clone_members_into(other_workspace)
|
534
|
+
backup().restore_into(other_workspace)
|
535
|
+
end
|
536
|
+
|
537
|
+
|
538
|
+
protected
|
539
|
+
class Cache
|
540
|
+
def initialize
|
541
|
+
@entries = {}
|
542
|
+
end
|
543
|
+
|
544
|
+
def include?(type)
|
545
|
+
@entries.include?(type)
|
546
|
+
end
|
547
|
+
|
548
|
+
def store(type,value)
|
549
|
+
@entries[type] = value
|
550
|
+
end
|
551
|
+
|
552
|
+
def get(type)
|
553
|
+
@entries[type]
|
554
|
+
end
|
555
|
+
|
556
|
+
def invalidate(type = nil)
|
557
|
+
if type.nil?
|
558
|
+
@entries = {}
|
559
|
+
else
|
560
|
+
@entries.delete(type)
|
561
|
+
end
|
562
|
+
end
|
563
|
+
end # class Cache
|
564
|
+
|
565
|
+
end
|
566
|
+
end
|
567
|
+
end
|