offroad 0.0.2 → 0.0.3
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.
- data/LICENSE +674 -674
- data/README.rdoc +29 -29
- data/Rakefile +75 -75
- data/TODO +42 -42
- data/lib/app/models/offroad/group_state.rb +85 -85
- data/lib/app/models/offroad/mirror_info.rb +53 -53
- data/lib/app/models/offroad/model_state.rb +36 -36
- data/lib/app/models/offroad/received_record_state.rb +115 -115
- data/lib/app/models/offroad/sendable_record_state.rb +91 -91
- data/lib/app/models/offroad/system_state.rb +33 -33
- data/lib/cargo_streamer.rb +222 -222
- data/lib/controller_extensions.rb +74 -74
- data/lib/exceptions.rb +16 -16
- data/lib/migrate/20100512164608_create_offroad_tables.rb +72 -72
- data/lib/mirror_data.rb +376 -376
- data/lib/model_extensions.rb +378 -377
- data/lib/module_funcs.rb +94 -94
- data/lib/offroad.rb +41 -41
- data/lib/version.rb +3 -3
- data/lib/view_helper.rb +7 -7
- data/templates/offline.rb +36 -36
- data/templates/offline_database.yml +7 -7
- data/templates/offroad.yml +6 -6
- data/test/app_root/app/controllers/application_controller.rb +2 -2
- data/test/app_root/app/controllers/group_controller.rb +28 -28
- data/test/app_root/app/models/global_record.rb +10 -10
- data/test/app_root/app/models/group.rb +12 -12
- data/test/app_root/app/models/group_owned_record.rb +68 -68
- data/test/app_root/app/models/guest.rb +7 -7
- data/test/app_root/app/models/subrecord.rb +12 -12
- data/test/app_root/app/models/unmirrored_record.rb +4 -4
- data/test/app_root/app/views/group/download_down_mirror.html.erb +3 -3
- data/test/app_root/app/views/group/download_initial_down_mirror.html.erb +3 -3
- data/test/app_root/app/views/group/download_up_mirror.html.erb +5 -5
- data/test/app_root/app/views/layouts/mirror.html.erb +8 -8
- data/test/app_root/config/boot.rb +115 -115
- data/test/app_root/config/database-pg.yml +8 -8
- data/test/app_root/config/database.yml +5 -5
- data/test/app_root/config/environment.rb +24 -24
- data/test/app_root/config/environments/test.rb +17 -17
- data/test/app_root/config/offroad.yml +6 -6
- data/test/app_root/config/routes.rb +4 -4
- data/test/app_root/db/migrate/20100529235049_create_tables.rb +64 -64
- data/test/app_root/lib/common_hobo.rb +15 -15
- data/test/app_root/vendor/plugins/offroad/init.rb +2 -2
- data/test/functional/mirror_operations_test.rb +148 -148
- data/test/test_helper.rb +453 -453
- data/test/unit/app_state_tracking_test.rb +275 -275
- data/test/unit/cargo_streamer_test.rb +332 -332
- data/test/unit/global_data_test.rb +102 -102
- data/test/unit/group_controller_test.rb +152 -152
- data/test/unit/group_data_test.rb +442 -435
- data/test/unit/group_single_test.rb +136 -136
- data/test/unit/hobo_permissions_test.rb +57 -57
- data/test/unit/mirror_data_test.rb +1283 -1283
- data/test/unit/mirror_info_test.rb +31 -31
- data/test/unit/module_funcs_test.rb +37 -37
- data/test/unit/pathological_model_test.rb +62 -62
- data/test/unit/test_framework_test.rb +86 -86
- data/test/unit/unmirrored_data_test.rb +14 -14
- metadata +6 -8
data/lib/model_extensions.rb
CHANGED
@@ -1,377 +1,378 @@
|
|
1
|
-
module Offroad
|
2
|
-
module ModelExtensions
|
3
|
-
OFFROAD_VALID_MODES = [:group_base, :group_owned, :group_single, :global]
|
4
|
-
OFFROAD_GROUP_MODES = [:group_base, :group_owned, :group_single]
|
5
|
-
|
6
|
-
def acts_as_offroadable(mode, opts = {})
|
7
|
-
raise ModelError.new("You can only call acts_as_offroadable once per model") if acts_as_offroadable?
|
8
|
-
raise ModelError.new("You must specify a mode, one of " + OFFROAD_VALID_MODES.map(&:inspect).join("/")) unless OFFROAD_VALID_MODES.include?(mode)
|
9
|
-
|
10
|
-
set_internal_cattr :offroad_mode, mode
|
11
|
-
|
12
|
-
case mode
|
13
|
-
when :group_owned then
|
14
|
-
raise ModelError.new("For :group_owned models, need to specify :parent") unless opts[:parent]
|
15
|
-
assoc = reflect_on_association(opts.delete(:parent))
|
16
|
-
raise ModelError.new("No such parent associaton") unless assoc
|
17
|
-
raise ModelError.new("Parent association must be a belongs_to association") unless assoc.belongs_to?
|
18
|
-
raise ModelError.new("Parent association must be to a group data model") unless assoc.klass.offroad_group_data?
|
19
|
-
|
20
|
-
set_internal_cattr :offroad_parent_assoc, assoc
|
21
|
-
Offroad::note_group_owned_model(self)
|
22
|
-
when :group_single then
|
23
|
-
Offroad::note_group_single_model(self)
|
24
|
-
when :group_base then
|
25
|
-
Offroad::note_group_base_model(self)
|
26
|
-
when :global then
|
27
|
-
Offroad::note_global_data_model(self)
|
28
|
-
end
|
29
|
-
|
30
|
-
# We should have deleted all the options from the hash by this point
|
31
|
-
raise ModelError.new("Unknown or inapplicable option(s) specified") unless opts.size == 0
|
32
|
-
|
33
|
-
case mode
|
34
|
-
when :group_base then
|
35
|
-
named_scope :owned_by_offroad_group, lambda { |group| { :conditions => { :id => group.id } } }
|
36
|
-
named_scope :offline_groups, {
|
37
|
-
:joins =>
|
38
|
-
"INNER JOIN \"#{Offroad::GroupState.table_name}\" ON \"#{Offroad::GroupState.table_name}\".app_group_id = \"#{table_name}\".\"#{primary_key}\""
|
39
|
-
}
|
40
|
-
named_scope :online_groups, {
|
41
|
-
:joins =>
|
42
|
-
"LEFT JOIN \"#{Offroad::GroupState.table_name}\" ON \"#{Offroad::GroupState.table_name}\".app_group_id = \"#{table_name}\".\"#{primary_key}\"",
|
43
|
-
:conditions =>
|
44
|
-
"\"#{Offroad::GroupState.table_name}\".app_group_id IS NULL"
|
45
|
-
}
|
46
|
-
when :group_owned then
|
47
|
-
named_scope :owned_by_offroad_group, lambda { |group| args_for_ownership_scope(group) }
|
48
|
-
when :group_single then
|
49
|
-
named_scope :owned_by_offroad_group, lambda { |group| {
|
50
|
-
:conditions => (Offroad::GroupState.count > 0 && group == Offroad::GroupState.first.app_group) ? "1=1" : "1=0"
|
51
|
-
} }
|
52
|
-
end
|
53
|
-
|
54
|
-
if offroad_group_data?
|
55
|
-
include GroupDataInstanceMethods
|
56
|
-
else
|
57
|
-
include GlobalDataInstanceMethods
|
58
|
-
end
|
59
|
-
include CommonInstanceMethods
|
60
|
-
|
61
|
-
before_destroy :before_mirrored_data_destroy
|
62
|
-
after_destroy :after_mirrored_data_destroy
|
63
|
-
before_save :before_mirrored_data_save
|
64
|
-
after_save :after_mirrored_data_save
|
65
|
-
|
66
|
-
if Object.const_defined?(:Hobo) and included_modules.include?(Hobo::Model)
|
67
|
-
include HoboPermissionsInstanceMethods
|
68
|
-
[
|
69
|
-
[:create, :save],
|
70
|
-
[:update, :save],
|
71
|
-
[:destroy, :destroy]
|
72
|
-
].each do |perm_name, check_name|
|
73
|
-
define_method "#{perm_name}_permitted_with_offroad_check?".to_sym do
|
74
|
-
pre_check_passed?("before_mirrored_data_#{check_name}".to_sym) && send("#{perm_name}_permitted_without_offroad_check?")
|
75
|
-
end
|
76
|
-
alias_method_chain "#{perm_name}_permitted?", "offroad_check"
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def offroad_model_state
|
82
|
-
model_scope = Offroad::ModelState::for_model(self)
|
83
|
-
return model_scope.first || model_scope.create
|
84
|
-
end
|
85
|
-
|
86
|
-
def acts_as_offroadable?
|
87
|
-
respond_to? :offroad_mode
|
88
|
-
end
|
89
|
-
|
90
|
-
def safe_to_load_from_cargo_stream?
|
91
|
-
acts_as_offroadable?
|
92
|
-
end
|
93
|
-
|
94
|
-
def offroad_group_base?
|
95
|
-
acts_as_offroadable? && offroad_mode == :group_base
|
96
|
-
end
|
97
|
-
|
98
|
-
def offroad_group_data?
|
99
|
-
acts_as_offroadable? && OFFROAD_GROUP_MODES.include?(offroad_mode)
|
100
|
-
end
|
101
|
-
|
102
|
-
def offroad_global_data?
|
103
|
-
acts_as_offroadable? && offroad_mode == :global
|
104
|
-
end
|
105
|
-
|
106
|
-
private
|
107
|
-
|
108
|
-
def set_internal_cattr(name, value)
|
109
|
-
write_inheritable_attribute name, value
|
110
|
-
class_inheritable_reader name
|
111
|
-
end
|
112
|
-
|
113
|
-
def args_for_ownership_scope(group)
|
114
|
-
included_assocs = []
|
115
|
-
conditions = []
|
116
|
-
assoc_owner = self
|
117
|
-
assoc = offroad_parent_assoc
|
118
|
-
while true
|
119
|
-
if assoc.klass.offroad_group_base?
|
120
|
-
conditions << "\"#{assoc_owner.table_name}\".\"#{assoc.primary_key_name}\" = #{group.id}"
|
121
|
-
break
|
122
|
-
else
|
123
|
-
conditions << "\"#{assoc_owner.table_name}\".\"#{assoc.primary_key_name}\" = \"#{assoc.klass.table_name}\".\"#{assoc.klass.primary_key}\""
|
124
|
-
included_assocs << assoc
|
125
|
-
assoc_owner = assoc.klass
|
126
|
-
assoc = assoc.klass.offroad_parent_assoc
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
includes = {}
|
131
|
-
included_assocs.reverse.each do |assoc|
|
132
|
-
includes = {assoc.name => includes}
|
133
|
-
end
|
134
|
-
|
135
|
-
return {:include => includes, :conditions => conditions.join(" AND ")}
|
136
|
-
end
|
137
|
-
|
138
|
-
module CommonInstanceMethods
|
139
|
-
# Methods below this point are only to be used internally by Offroad
|
140
|
-
# However, making all of them private would make using them from elsewhere troublesome
|
141
|
-
|
142
|
-
# TODO Should put common save and destroy wrappers in here, with access to a method that checks if SRS needed
|
143
|
-
# TODO That method should also be used in import_model_cargo instead of explicitly trying to find the srs
|
144
|
-
|
145
|
-
#:nodoc:#
|
146
|
-
def validate_changed_id_columns
|
147
|
-
changes.each do |colname, arr|
|
148
|
-
orig_val = arr[0]
|
149
|
-
new_val = arr[1]
|
150
|
-
|
151
|
-
raise DataError.new("Cannot change id of offroad-tracked records (orig #{orig_val.inspect}, new #{new_val.inspect}") if colname == self.class.primary_key && orig_val != nil
|
152
|
-
|
153
|
-
# FIXME : Use association reflection instead
|
154
|
-
next unless colname.end_with? "_id"
|
155
|
-
accessor_name = colname[0, colname.size-3]
|
156
|
-
next unless respond_to? accessor_name
|
157
|
-
obj = send(accessor_name)
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
#
|
164
|
-
if
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
#
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
return true if Offroad::
|
221
|
-
return
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
:
|
238
|
-
:
|
239
|
-
:
|
240
|
-
:
|
241
|
-
:
|
242
|
-
:
|
243
|
-
:
|
244
|
-
:
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
return
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
#
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
Offroad::
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
return
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
end
|
1
|
+
module Offroad
|
2
|
+
module ModelExtensions
|
3
|
+
OFFROAD_VALID_MODES = [:group_base, :group_owned, :group_single, :global]
|
4
|
+
OFFROAD_GROUP_MODES = [:group_base, :group_owned, :group_single]
|
5
|
+
|
6
|
+
def acts_as_offroadable(mode, opts = {})
|
7
|
+
raise ModelError.new("You can only call acts_as_offroadable once per model") if acts_as_offroadable?
|
8
|
+
raise ModelError.new("You must specify a mode, one of " + OFFROAD_VALID_MODES.map(&:inspect).join("/")) unless OFFROAD_VALID_MODES.include?(mode)
|
9
|
+
|
10
|
+
set_internal_cattr :offroad_mode, mode
|
11
|
+
|
12
|
+
case mode
|
13
|
+
when :group_owned then
|
14
|
+
raise ModelError.new("For :group_owned models, need to specify :parent") unless opts[:parent]
|
15
|
+
assoc = reflect_on_association(opts.delete(:parent))
|
16
|
+
raise ModelError.new("No such parent associaton") unless assoc
|
17
|
+
raise ModelError.new("Parent association must be a belongs_to association") unless assoc.belongs_to?
|
18
|
+
raise ModelError.new("Parent association must be to a group data model") unless assoc.klass.offroad_group_data?
|
19
|
+
|
20
|
+
set_internal_cattr :offroad_parent_assoc, assoc
|
21
|
+
Offroad::note_group_owned_model(self)
|
22
|
+
when :group_single then
|
23
|
+
Offroad::note_group_single_model(self)
|
24
|
+
when :group_base then
|
25
|
+
Offroad::note_group_base_model(self)
|
26
|
+
when :global then
|
27
|
+
Offroad::note_global_data_model(self)
|
28
|
+
end
|
29
|
+
|
30
|
+
# We should have deleted all the options from the hash by this point
|
31
|
+
raise ModelError.new("Unknown or inapplicable option(s) specified") unless opts.size == 0
|
32
|
+
|
33
|
+
case mode
|
34
|
+
when :group_base then
|
35
|
+
named_scope :owned_by_offroad_group, lambda { |group| { :conditions => { :id => group.id } } }
|
36
|
+
named_scope :offline_groups, {
|
37
|
+
:joins =>
|
38
|
+
"INNER JOIN \"#{Offroad::GroupState.table_name}\" ON \"#{Offroad::GroupState.table_name}\".app_group_id = \"#{table_name}\".\"#{primary_key}\""
|
39
|
+
}
|
40
|
+
named_scope :online_groups, {
|
41
|
+
:joins =>
|
42
|
+
"LEFT JOIN \"#{Offroad::GroupState.table_name}\" ON \"#{Offroad::GroupState.table_name}\".app_group_id = \"#{table_name}\".\"#{primary_key}\"",
|
43
|
+
:conditions =>
|
44
|
+
"\"#{Offroad::GroupState.table_name}\".app_group_id IS NULL"
|
45
|
+
}
|
46
|
+
when :group_owned then
|
47
|
+
named_scope :owned_by_offroad_group, lambda { |group| args_for_ownership_scope(group) }
|
48
|
+
when :group_single then
|
49
|
+
named_scope :owned_by_offroad_group, lambda { |group| {
|
50
|
+
:conditions => (Offroad::GroupState.count > 0 && group == Offroad::GroupState.first.app_group) ? "1=1" : "1=0"
|
51
|
+
} }
|
52
|
+
end
|
53
|
+
|
54
|
+
if offroad_group_data?
|
55
|
+
include GroupDataInstanceMethods
|
56
|
+
else
|
57
|
+
include GlobalDataInstanceMethods
|
58
|
+
end
|
59
|
+
include CommonInstanceMethods
|
60
|
+
|
61
|
+
before_destroy :before_mirrored_data_destroy
|
62
|
+
after_destroy :after_mirrored_data_destroy
|
63
|
+
before_save :before_mirrored_data_save
|
64
|
+
after_save :after_mirrored_data_save
|
65
|
+
|
66
|
+
if Object.const_defined?(:Hobo) and included_modules.include?(Hobo::Model)
|
67
|
+
include HoboPermissionsInstanceMethods
|
68
|
+
[
|
69
|
+
[:create, :save],
|
70
|
+
[:update, :save],
|
71
|
+
[:destroy, :destroy]
|
72
|
+
].each do |perm_name, check_name|
|
73
|
+
define_method "#{perm_name}_permitted_with_offroad_check?".to_sym do
|
74
|
+
pre_check_passed?("before_mirrored_data_#{check_name}".to_sym) && send("#{perm_name}_permitted_without_offroad_check?")
|
75
|
+
end
|
76
|
+
alias_method_chain "#{perm_name}_permitted?", "offroad_check"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def offroad_model_state
|
82
|
+
model_scope = Offroad::ModelState::for_model(self)
|
83
|
+
return model_scope.first || model_scope.create
|
84
|
+
end
|
85
|
+
|
86
|
+
def acts_as_offroadable?
|
87
|
+
respond_to? :offroad_mode
|
88
|
+
end
|
89
|
+
|
90
|
+
def safe_to_load_from_cargo_stream?
|
91
|
+
acts_as_offroadable?
|
92
|
+
end
|
93
|
+
|
94
|
+
def offroad_group_base?
|
95
|
+
acts_as_offroadable? && offroad_mode == :group_base
|
96
|
+
end
|
97
|
+
|
98
|
+
def offroad_group_data?
|
99
|
+
acts_as_offroadable? && OFFROAD_GROUP_MODES.include?(offroad_mode)
|
100
|
+
end
|
101
|
+
|
102
|
+
def offroad_global_data?
|
103
|
+
acts_as_offroadable? && offroad_mode == :global
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def set_internal_cattr(name, value)
|
109
|
+
write_inheritable_attribute name, value
|
110
|
+
class_inheritable_reader name
|
111
|
+
end
|
112
|
+
|
113
|
+
def args_for_ownership_scope(group)
|
114
|
+
included_assocs = []
|
115
|
+
conditions = []
|
116
|
+
assoc_owner = self
|
117
|
+
assoc = offroad_parent_assoc
|
118
|
+
while true
|
119
|
+
if assoc.klass.offroad_group_base?
|
120
|
+
conditions << "\"#{assoc_owner.table_name}\".\"#{assoc.primary_key_name}\" = #{group.id}"
|
121
|
+
break
|
122
|
+
else
|
123
|
+
conditions << "\"#{assoc_owner.table_name}\".\"#{assoc.primary_key_name}\" = \"#{assoc.klass.table_name}\".\"#{assoc.klass.primary_key}\""
|
124
|
+
included_assocs << assoc
|
125
|
+
assoc_owner = assoc.klass
|
126
|
+
assoc = assoc.klass.offroad_parent_assoc
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
includes = {}
|
131
|
+
included_assocs.reverse.each do |assoc|
|
132
|
+
includes = {assoc.name => includes}
|
133
|
+
end
|
134
|
+
|
135
|
+
return {:include => includes, :conditions => conditions.join(" AND ")}
|
136
|
+
end
|
137
|
+
|
138
|
+
module CommonInstanceMethods
|
139
|
+
# Methods below this point are only to be used internally by Offroad
|
140
|
+
# However, making all of them private would make using them from elsewhere troublesome
|
141
|
+
|
142
|
+
# TODO Should put common save and destroy wrappers in here, with access to a method that checks if SRS needed
|
143
|
+
# TODO That method should also be used in import_model_cargo instead of explicitly trying to find the srs
|
144
|
+
|
145
|
+
#:nodoc:#
|
146
|
+
def validate_changed_id_columns
|
147
|
+
changes.each do |colname, arr|
|
148
|
+
orig_val = arr[0]
|
149
|
+
new_val = arr[1]
|
150
|
+
|
151
|
+
raise DataError.new("Cannot change id of offroad-tracked records (orig #{orig_val.inspect}, new #{new_val.inspect}") if colname == self.class.primary_key && orig_val != nil
|
152
|
+
|
153
|
+
# FIXME : Use association reflection instead
|
154
|
+
next unless colname.end_with? "_id"
|
155
|
+
accessor_name = colname[0, colname.size-3]
|
156
|
+
next unless respond_to? accessor_name
|
157
|
+
obj = send(accessor_name)
|
158
|
+
next unless obj
|
159
|
+
|
160
|
+
raise DataError.new("Mirrored data cannot hold a foreign key to unmirrored data") unless obj.class.acts_as_offroadable?
|
161
|
+
|
162
|
+
if !new_record? and offroad_mode == :group_owned and colname == offroad_parent_assoc.primary_key_name
|
163
|
+
# obj is our parent
|
164
|
+
# FIXME: What if we can't find orig_val?
|
165
|
+
if obj.owning_group != obj.class.find(orig_val).owning_group
|
166
|
+
raise DataError.new("Group-owned data cannot be transferred between groups")
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
if self.class.offroad_group_data?
|
171
|
+
if obj.class.offroad_group_data? && obj.owning_group && obj.owning_group.id != owning_group.id
|
172
|
+
raise DataError.new("Invalid #{colname}: Group data cannot hold a foreign key to data owned by another group")
|
173
|
+
end
|
174
|
+
elsif self.class.offroad_global_data?
|
175
|
+
unless obj.class.offroad_global_data?
|
176
|
+
raise DataError.new("Invalid #{colname}: Global mirrored data cannot hold a foreign key to group data")
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
|
184
|
+
module GlobalDataInstanceMethods
|
185
|
+
# Methods below this point are only to be used internally by Offroad
|
186
|
+
# However, marking all of them private would make using them from elsewhere troublesome
|
187
|
+
|
188
|
+
def locked_by_offroad?
|
189
|
+
Offroad::app_offline?
|
190
|
+
end
|
191
|
+
|
192
|
+
#:nodoc#
|
193
|
+
def before_mirrored_data_destroy
|
194
|
+
raise ActiveRecord::ReadOnlyRecord if locked_by_offroad?
|
195
|
+
return true
|
196
|
+
end
|
197
|
+
|
198
|
+
#:nodoc#
|
199
|
+
def after_mirrored_data_destroy
|
200
|
+
Offroad::SendableRecordState::note_record_destroyed(self) if Offroad::app_online?
|
201
|
+
return true
|
202
|
+
end
|
203
|
+
|
204
|
+
#:nodoc#
|
205
|
+
def before_mirrored_data_save
|
206
|
+
raise ActiveRecord::ReadOnlyRecord if locked_by_offroad?
|
207
|
+
validate_changed_id_columns
|
208
|
+
return true
|
209
|
+
end
|
210
|
+
|
211
|
+
#:nodoc#
|
212
|
+
def after_mirrored_data_save
|
213
|
+
Offroad::SendableRecordState::note_record_created_or_updated(self) if Offroad::app_online? && changed?
|
214
|
+
return true
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
module GroupDataInstanceMethods
|
219
|
+
def locked_by_offroad?
|
220
|
+
return true if Offroad::app_online? && group_offline?
|
221
|
+
return true if Offroad::app_offline? && (!group_state || group_state.group_locked?)
|
222
|
+
return false
|
223
|
+
end
|
224
|
+
|
225
|
+
# If called on a group_owned_model, methods below bubble up to the group_base_model
|
226
|
+
|
227
|
+
def offroad_group_lock!
|
228
|
+
raise DataError.new("Cannot lock groups from online app") if Offroad::app_online?
|
229
|
+
group_state.update_attribute(:group_locked, true)
|
230
|
+
end
|
231
|
+
|
232
|
+
# Returns a hash with the latest information about this group in the offline app
|
233
|
+
def last_known_status
|
234
|
+
raise DataError.new("This method is only for offline groups") if group_online?
|
235
|
+
s = group_state
|
236
|
+
fields_of_interest = [
|
237
|
+
:last_installer_downloaded_at,
|
238
|
+
:last_installation_at,
|
239
|
+
:last_down_mirror_created_at,
|
240
|
+
:last_down_mirror_loaded_at,
|
241
|
+
:last_up_mirror_created_at,
|
242
|
+
:last_up_mirror_loaded_at,
|
243
|
+
:launcher_version,
|
244
|
+
:app_version,
|
245
|
+
:operating_system
|
246
|
+
]
|
247
|
+
return fields_of_interest.map {|field_name| s.send(field_name)}
|
248
|
+
end
|
249
|
+
|
250
|
+
def group_offline?
|
251
|
+
not group_online?
|
252
|
+
end
|
253
|
+
|
254
|
+
def group_online?
|
255
|
+
return group_state.nil?
|
256
|
+
end
|
257
|
+
|
258
|
+
def group_offline=(b)
|
259
|
+
raise DataError.new("Unable to change a group's offline status in offline app") if Offroad::app_offline?
|
260
|
+
|
261
|
+
if b and Offroad::group_single_models.size > 0 and Offroad::GroupState.count > 0
|
262
|
+
raise DataError.new("Unable to set more than one group offline if there are any group single models")
|
263
|
+
end
|
264
|
+
|
265
|
+
if b && !group_state
|
266
|
+
Offroad::GroupState.for_group(owning_group).create!
|
267
|
+
elsif group_state
|
268
|
+
group_state.destroy
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
def owning_group
|
273
|
+
return nil if unlocked_group_single_record?
|
274
|
+
return Offroad::GroupState.first.app_group if offroad_mode == :group_single
|
275
|
+
|
276
|
+
# Recurse upwards until we get to the group base
|
277
|
+
if self.class.offroad_group_base?
|
278
|
+
return self
|
279
|
+
else
|
280
|
+
parent = send(offroad_parent_assoc.name)
|
281
|
+
if parent
|
282
|
+
return parent.owning_group
|
283
|
+
else
|
284
|
+
return nil
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
# Methods below this point are only to be used internally by Offroad
|
290
|
+
# However, marking them private makes using them from elsewhere troublesome
|
291
|
+
|
292
|
+
#:nodoc#
|
293
|
+
def before_mirrored_data_destroy
|
294
|
+
if group_offline? && offroad_mode == :group_base
|
295
|
+
group_state.update_attribute(:group_being_destroyed, true)
|
296
|
+
end
|
297
|
+
|
298
|
+
return true if unlocked_group_single_record?
|
299
|
+
|
300
|
+
if locked_by_offroad?
|
301
|
+
# The only thing that can be deleted is the entire group (possibly with its dependent records), and only if we're online
|
302
|
+
raise ActiveRecord::ReadOnlyRecord unless Offroad::app_online? and (offroad_mode == :group_base or group_being_destroyed)
|
303
|
+
end
|
304
|
+
|
305
|
+
# If the app is offline, the only thing that CAN'T be deleted even if unlocked is the group
|
306
|
+
raise ActiveRecord::ReadOnlyRecord if Offroad::app_offline? and offroad_mode == :group_base
|
307
|
+
|
308
|
+
return true
|
309
|
+
end
|
310
|
+
|
311
|
+
#:nodoc#
|
312
|
+
def after_mirrored_data_destroy
|
313
|
+
Offroad::SendableRecordState::note_record_destroyed(self) if Offroad::app_offline?
|
314
|
+
Offroad::GroupState::note_group_destroyed(self) if group_offline? && offroad_mode == :group_base
|
315
|
+
return true
|
316
|
+
end
|
317
|
+
|
318
|
+
#:nodoc#
|
319
|
+
def before_mirrored_data_save
|
320
|
+
return true if unlocked_group_single_record?
|
321
|
+
|
322
|
+
raise DataError.new("Invalid owning group") unless owning_group
|
323
|
+
|
324
|
+
if Offroad::app_offline?
|
325
|
+
case offroad_mode
|
326
|
+
when :group_base
|
327
|
+
raise DataError.new("Cannot create groups in offline mode") if new_record?
|
328
|
+
when :group_owned
|
329
|
+
raise DataError.new("Owning group must be the offline group") if owning_group != Offroad::offline_group
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
validate_changed_id_columns
|
334
|
+
|
335
|
+
raise ActiveRecord::ReadOnlyRecord if locked_by_offroad?
|
336
|
+
|
337
|
+
return true
|
338
|
+
end
|
339
|
+
|
340
|
+
#:nodoc#
|
341
|
+
def after_mirrored_data_save
|
342
|
+
Offroad::SendableRecordState::note_record_created_or_updated(self) if Offroad::app_offline? && changed?
|
343
|
+
return true
|
344
|
+
end
|
345
|
+
|
346
|
+
#:nodoc#
|
347
|
+
def group_state
|
348
|
+
Offroad::GroupState.for_group(owning_group).first
|
349
|
+
end
|
350
|
+
|
351
|
+
#:nodoc:#
|
352
|
+
def group_being_destroyed
|
353
|
+
return true unless owning_group # If the group doesn't exist anymore, then it's pretty well destroyed
|
354
|
+
return group_state.group_being_destroyed
|
355
|
+
end
|
356
|
+
|
357
|
+
#:nodoc:#
|
358
|
+
def unlocked_group_single_record?
|
359
|
+
offroad_mode == :group_single && Offroad::GroupState.count == 0
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
module HoboPermissionsInstanceMethods
|
364
|
+
private
|
365
|
+
|
366
|
+
def pre_check_passed?(method_name)
|
367
|
+
begin
|
368
|
+
send(method_name)
|
369
|
+
rescue ActiveRecord::ReadOnlyRecord
|
370
|
+
return false
|
371
|
+
rescue Offroad::DataError
|
372
|
+
return false
|
373
|
+
end
|
374
|
+
return true
|
375
|
+
end
|
376
|
+
end
|
377
|
+
end
|
378
|
+
end
|