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
@@ -1,53 +1,53 @@
|
|
1
|
-
module Offroad
|
2
|
-
private
|
3
|
-
|
4
|
-
# Non-database model representing general information attached to any mirror file
|
5
|
-
# Based on the pattern found here: http://stackoverflow.com/questions/315850/rails-model-without-database
|
6
|
-
class MirrorInfo < ActiveRecord::Base
|
7
|
-
self.abstract_class = true
|
8
|
-
|
9
|
-
def self.columns
|
10
|
-
@columns ||= []
|
11
|
-
end
|
12
|
-
|
13
|
-
[
|
14
|
-
[:created_at, :datetime],
|
15
|
-
[:online_site, :string],
|
16
|
-
[:app, :string],
|
17
|
-
[:app_mode, :string],
|
18
|
-
[:app_version, :string],
|
19
|
-
[:operating_system, :string],
|
20
|
-
[:generator, :string],
|
21
|
-
[:schema_migrations, :string],
|
22
|
-
[:initial_file, :boolean]
|
23
|
-
].each do |attr_name, attr_type|
|
24
|
-
columns << ActiveRecord::ConnectionAdapters::Column.new(attr_name.to_s, nil, attr_type.to_s, true)
|
25
|
-
validates_presence_of attr_name unless attr_type == :boolean
|
26
|
-
end
|
27
|
-
|
28
|
-
def self.safe_to_load_from_cargo_stream?
|
29
|
-
true
|
30
|
-
end
|
31
|
-
|
32
|
-
def self.new_from_group(group, initial_file = false)
|
33
|
-
mode = Offroad::app_online? ? "online" : "offline"
|
34
|
-
migration_query = "SELECT version FROM schema_migrations ORDER BY version"
|
35
|
-
migrations = Offroad::group_base_model.connection.select_all(migration_query).map{ |r| r["version"] }
|
36
|
-
return MirrorInfo.new(
|
37
|
-
:created_at => Time.now.to_s,
|
38
|
-
:online_site => Offroad::online_url,
|
39
|
-
:app => Offroad::app_name,
|
40
|
-
:app_mode => mode.titleize,
|
41
|
-
:app_version => Offroad::app_version,
|
42
|
-
:operating_system => RUBY_PLATFORM,
|
43
|
-
:generator => "Offroad " + Offroad::VERSION,
|
44
|
-
:schema_migrations => migrations.join(","),
|
45
|
-
:initial_file => initial_file
|
46
|
-
)
|
47
|
-
end
|
48
|
-
|
49
|
-
def save
|
50
|
-
raise DataError.new("Cannot save MirrorInfo records")
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
1
|
+
module Offroad
|
2
|
+
private
|
3
|
+
|
4
|
+
# Non-database model representing general information attached to any mirror file
|
5
|
+
# Based on the pattern found here: http://stackoverflow.com/questions/315850/rails-model-without-database
|
6
|
+
class MirrorInfo < ActiveRecord::Base
|
7
|
+
self.abstract_class = true
|
8
|
+
|
9
|
+
def self.columns
|
10
|
+
@columns ||= []
|
11
|
+
end
|
12
|
+
|
13
|
+
[
|
14
|
+
[:created_at, :datetime],
|
15
|
+
[:online_site, :string],
|
16
|
+
[:app, :string],
|
17
|
+
[:app_mode, :string],
|
18
|
+
[:app_version, :string],
|
19
|
+
[:operating_system, :string],
|
20
|
+
[:generator, :string],
|
21
|
+
[:schema_migrations, :string],
|
22
|
+
[:initial_file, :boolean]
|
23
|
+
].each do |attr_name, attr_type|
|
24
|
+
columns << ActiveRecord::ConnectionAdapters::Column.new(attr_name.to_s, nil, attr_type.to_s, true)
|
25
|
+
validates_presence_of attr_name unless attr_type == :boolean
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.safe_to_load_from_cargo_stream?
|
29
|
+
true
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.new_from_group(group, initial_file = false)
|
33
|
+
mode = Offroad::app_online? ? "online" : "offline"
|
34
|
+
migration_query = "SELECT version FROM schema_migrations ORDER BY version"
|
35
|
+
migrations = Offroad::group_base_model.connection.select_all(migration_query).map{ |r| r["version"] }
|
36
|
+
return MirrorInfo.new(
|
37
|
+
:created_at => Time.now.to_s,
|
38
|
+
:online_site => Offroad::online_url,
|
39
|
+
:app => Offroad::app_name,
|
40
|
+
:app_mode => mode.titleize,
|
41
|
+
:app_version => Offroad::app_version,
|
42
|
+
:operating_system => RUBY_PLATFORM,
|
43
|
+
:generator => "Offroad " + Offroad::VERSION,
|
44
|
+
:schema_migrations => migrations.join(","),
|
45
|
+
:initial_file => initial_file
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
def save
|
50
|
+
raise DataError.new("Cannot save MirrorInfo records")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -1,36 +1,36 @@
|
|
1
|
-
module Offroad
|
2
|
-
private
|
3
|
-
|
4
|
-
class ModelState < ActiveRecord::Base
|
5
|
-
set_table_name "offroad_model_states"
|
6
|
-
|
7
|
-
validates_presence_of :app_model_name
|
8
|
-
|
9
|
-
def validate
|
10
|
-
model = nil
|
11
|
-
begin
|
12
|
-
model = app_model
|
13
|
-
rescue NameError
|
14
|
-
errors.add_to_base "Given model name does not correspond to a constant"
|
15
|
-
end
|
16
|
-
|
17
|
-
if model
|
18
|
-
errors.add_to_base "Constant is not a mirrored model" unless self.class.valid_model?(model)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
named_scope :for_model, lambda { |model| { :conditions => {
|
23
|
-
:app_model_name => valid_model?(model) ? model.name : nil
|
24
|
-
} } }
|
25
|
-
|
26
|
-
def app_model
|
27
|
-
app_model_name.constantize
|
28
|
-
end
|
29
|
-
|
30
|
-
private
|
31
|
-
|
32
|
-
def self.valid_model?(model)
|
33
|
-
model.respond_to?(:acts_as_offroadable?) && model.acts_as_offroadable?
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
1
|
+
module Offroad
|
2
|
+
private
|
3
|
+
|
4
|
+
class ModelState < ActiveRecord::Base
|
5
|
+
set_table_name "offroad_model_states"
|
6
|
+
|
7
|
+
validates_presence_of :app_model_name
|
8
|
+
|
9
|
+
def validate
|
10
|
+
model = nil
|
11
|
+
begin
|
12
|
+
model = app_model
|
13
|
+
rescue NameError
|
14
|
+
errors.add_to_base "Given model name does not correspond to a constant"
|
15
|
+
end
|
16
|
+
|
17
|
+
if model
|
18
|
+
errors.add_to_base "Constant is not a mirrored model" unless self.class.valid_model?(model)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
named_scope :for_model, lambda { |model| { :conditions => {
|
23
|
+
:app_model_name => valid_model?(model) ? model.name : nil
|
24
|
+
} } }
|
25
|
+
|
26
|
+
def app_model
|
27
|
+
app_model_name.constantize
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def self.valid_model?(model)
|
33
|
+
model.respond_to?(:acts_as_offroadable?) && model.acts_as_offroadable?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -1,115 +1,115 @@
|
|
1
|
-
module Offroad
|
2
|
-
private
|
3
|
-
|
4
|
-
class ReceivedRecordState < ActiveRecord::Base
|
5
|
-
set_table_name "offroad_received_record_states"
|
6
|
-
|
7
|
-
belongs_to :model_state, :class_name => "::Offroad::ModelState"
|
8
|
-
|
9
|
-
belongs_to :group_state, :class_name => "::Offroad::GroupState"
|
10
|
-
|
11
|
-
def validate
|
12
|
-
unless model_state
|
13
|
-
errors.add_to_base "Cannot find associated model state"
|
14
|
-
return
|
15
|
-
end
|
16
|
-
model = model_state.app_model
|
17
|
-
|
18
|
-
if Offroad::app_offline?
|
19
|
-
if model.offroad_group_data?
|
20
|
-
errors.add_to_base "Cannot allow received record state for group data in offline app"
|
21
|
-
end
|
22
|
-
elsif Offroad::app_online?
|
23
|
-
if model.offroad_global_data?
|
24
|
-
errors.add_to_base "Cannot allow received record state for global records in online app"
|
25
|
-
elsif group_state.nil?
|
26
|
-
errors.add_to_base "Cannot allow received record state for online group records in online app"
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
if model.offroad_global_data? && group_state
|
31
|
-
errors.add_to_base "Cannot allow received record state for global records to also be assoc with a group"
|
32
|
-
end
|
33
|
-
|
34
|
-
begin
|
35
|
-
app_record
|
36
|
-
rescue ActiveRecord::RecordNotFound
|
37
|
-
errors.add_to_base "Cannot find associated app record"
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
named_scope :for_model, lambda { |model| { :conditions => {
|
42
|
-
:model_state_id => model.offroad_model_state.id
|
43
|
-
} } }
|
44
|
-
|
45
|
-
named_scope :for_model_and_group_if_apropos, lambda { |model, group| { :conditions => {
|
46
|
-
:model_state_id => model.offroad_model_state.id,
|
47
|
-
:group_state_id => (group && model.offroad_group_data? && group.group_state) ? group.group_state.id : 0
|
48
|
-
} } }
|
49
|
-
|
50
|
-
named_scope :for_record, lambda { |rec| { :conditions => {
|
51
|
-
:model_state_id => rec.class.offroad_model_state.id,
|
52
|
-
:group_state_id => (rec.class.offroad_group_data? && rec.group_state) ? rec.group_state.id : 0,
|
53
|
-
:local_record_id => rec.id
|
54
|
-
} } }
|
55
|
-
|
56
|
-
def app_record
|
57
|
-
model_state.app_model.find(local_record_id)
|
58
|
-
end
|
59
|
-
|
60
|
-
def self.redirect_to_local_ids(records, column, model, group)
|
61
|
-
column = column.to_sym
|
62
|
-
source = self.for_model_and_group_if_apropos(model, group)
|
63
|
-
already_allocated = source.all(:conditions => { :remote_record_id => records.map{|r| r[column]} }).index_by(&:remote_record_id)
|
64
|
-
|
65
|
-
remaining = {} # Maps newly discovered remote id to list of records in batch that reference that id
|
66
|
-
records.each do |r|
|
67
|
-
remote_id = r[column]
|
68
|
-
next unless remote_id && remote_id > 0
|
69
|
-
# TODO Check for illegal references here (i.e. group model referencing global model)
|
70
|
-
if already_allocated.has_key?(remote_id)
|
71
|
-
r[column] = already_allocated[remote_id].local_record_id
|
72
|
-
else
|
73
|
-
# Target doesn't exist yet, we'll figure out what its local id will be later
|
74
|
-
if remaining.has_key?(remote_id)
|
75
|
-
remaining[remote_id] << r
|
76
|
-
else
|
77
|
-
remaining[remote_id] = [r]
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
return unless remaining.size > 0
|
83
|
-
|
84
|
-
# Reserve access to a block of local ids
|
85
|
-
if model.connection.adapter_name.downcase.include?("postgres")
|
86
|
-
nextval_cmd = model.connection.select_value("SELECT column_default FROM information_schema.columns WHERE table_name = '#{model.table_name}' AND column_name = '#{model.primary_key}'")
|
87
|
-
local_ids = model.connection.select_values("SELECT #{nextval_cmd} FROM generate_series(1,#{remaining.size})").map(&:to_i)
|
88
|
-
else
|
89
|
-
# Reserve access to a block of local ids by creating temporary records to advance the autoincrement counter
|
90
|
-
# TODO I'm pretty sure this is safe because it'll always be used in a transaction, but I should check
|
91
|
-
model.import([model.primary_key.to_sym], [[nil]]*remaining.size, :validate => false, :timestamps => false)
|
92
|
-
last_id = model.last(:select => model.primary_key, :order => model.primary_key).id
|
93
|
-
local_ids = ((last_id+1-remaining.size)..last_id).to_a
|
94
|
-
model.delete(local_ids)
|
95
|
-
end
|
96
|
-
|
97
|
-
# Create the corresponding RRSes
|
98
|
-
model_state_id = model.offroad_model_state.id
|
99
|
-
group_state = model.offroad_group_data? && group ? group.group_state : nil
|
100
|
-
group_state_id = group_state ? group_state.id : 0
|
101
|
-
self.import(
|
102
|
-
[:model_state_id, :group_state_id, :local_record_id, :remote_record_id],
|
103
|
-
local_ids.zip(remaining.keys).map{|here, there| [model_state_id, group_state_id, here, there]},
|
104
|
-
:validate => false, :timestamps => false
|
105
|
-
)
|
106
|
-
|
107
|
-
# Finally do the redirection to the new ids
|
108
|
-
remaining.each_key.each_with_index do |remote_id, i|
|
109
|
-
remaining[remote_id].each do |r|
|
110
|
-
r[column] = local_ids[i]
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
115
|
-
end
|
1
|
+
module Offroad
|
2
|
+
private
|
3
|
+
|
4
|
+
class ReceivedRecordState < ActiveRecord::Base
|
5
|
+
set_table_name "offroad_received_record_states"
|
6
|
+
|
7
|
+
belongs_to :model_state, :class_name => "::Offroad::ModelState"
|
8
|
+
|
9
|
+
belongs_to :group_state, :class_name => "::Offroad::GroupState"
|
10
|
+
|
11
|
+
def validate
|
12
|
+
unless model_state
|
13
|
+
errors.add_to_base "Cannot find associated model state"
|
14
|
+
return
|
15
|
+
end
|
16
|
+
model = model_state.app_model
|
17
|
+
|
18
|
+
if Offroad::app_offline?
|
19
|
+
if model.offroad_group_data?
|
20
|
+
errors.add_to_base "Cannot allow received record state for group data in offline app"
|
21
|
+
end
|
22
|
+
elsif Offroad::app_online?
|
23
|
+
if model.offroad_global_data?
|
24
|
+
errors.add_to_base "Cannot allow received record state for global records in online app"
|
25
|
+
elsif group_state.nil?
|
26
|
+
errors.add_to_base "Cannot allow received record state for online group records in online app"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
if model.offroad_global_data? && group_state
|
31
|
+
errors.add_to_base "Cannot allow received record state for global records to also be assoc with a group"
|
32
|
+
end
|
33
|
+
|
34
|
+
begin
|
35
|
+
app_record
|
36
|
+
rescue ActiveRecord::RecordNotFound
|
37
|
+
errors.add_to_base "Cannot find associated app record"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
named_scope :for_model, lambda { |model| { :conditions => {
|
42
|
+
:model_state_id => model.offroad_model_state.id
|
43
|
+
} } }
|
44
|
+
|
45
|
+
named_scope :for_model_and_group_if_apropos, lambda { |model, group| { :conditions => {
|
46
|
+
:model_state_id => model.offroad_model_state.id,
|
47
|
+
:group_state_id => (group && model.offroad_group_data? && group.group_state) ? group.group_state.id : 0
|
48
|
+
} } }
|
49
|
+
|
50
|
+
named_scope :for_record, lambda { |rec| { :conditions => {
|
51
|
+
:model_state_id => rec.class.offroad_model_state.id,
|
52
|
+
:group_state_id => (rec.class.offroad_group_data? && rec.group_state) ? rec.group_state.id : 0,
|
53
|
+
:local_record_id => rec.id
|
54
|
+
} } }
|
55
|
+
|
56
|
+
def app_record
|
57
|
+
model_state.app_model.find(local_record_id)
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.redirect_to_local_ids(records, column, model, group)
|
61
|
+
column = column.to_sym
|
62
|
+
source = self.for_model_and_group_if_apropos(model, group)
|
63
|
+
already_allocated = source.all(:conditions => { :remote_record_id => records.map{|r| r[column]} }).index_by(&:remote_record_id)
|
64
|
+
|
65
|
+
remaining = {} # Maps newly discovered remote id to list of records in batch that reference that id
|
66
|
+
records.each do |r|
|
67
|
+
remote_id = r[column]
|
68
|
+
next unless remote_id && remote_id > 0
|
69
|
+
# TODO Check for illegal references here (i.e. group model referencing global model)
|
70
|
+
if already_allocated.has_key?(remote_id)
|
71
|
+
r[column] = already_allocated[remote_id].local_record_id
|
72
|
+
else
|
73
|
+
# Target doesn't exist yet, we'll figure out what its local id will be later
|
74
|
+
if remaining.has_key?(remote_id)
|
75
|
+
remaining[remote_id] << r
|
76
|
+
else
|
77
|
+
remaining[remote_id] = [r]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
return unless remaining.size > 0
|
83
|
+
|
84
|
+
# Reserve access to a block of local ids
|
85
|
+
if model.connection.adapter_name.downcase.include?("postgres")
|
86
|
+
nextval_cmd = model.connection.select_value("SELECT column_default FROM information_schema.columns WHERE table_name = '#{model.table_name}' AND column_name = '#{model.primary_key}'")
|
87
|
+
local_ids = model.connection.select_values("SELECT #{nextval_cmd} FROM generate_series(1,#{remaining.size})").map(&:to_i)
|
88
|
+
else
|
89
|
+
# Reserve access to a block of local ids by creating temporary records to advance the autoincrement counter
|
90
|
+
# TODO I'm pretty sure this is safe because it'll always be used in a transaction, but I should check
|
91
|
+
model.import([model.primary_key.to_sym], [[nil]]*remaining.size, :validate => false, :timestamps => false)
|
92
|
+
last_id = model.last(:select => model.primary_key, :order => model.primary_key).id
|
93
|
+
local_ids = ((last_id+1-remaining.size)..last_id).to_a
|
94
|
+
model.delete(local_ids)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Create the corresponding RRSes
|
98
|
+
model_state_id = model.offroad_model_state.id
|
99
|
+
group_state = model.offroad_group_data? && group ? group.group_state : nil
|
100
|
+
group_state_id = group_state ? group_state.id : 0
|
101
|
+
self.import(
|
102
|
+
[:model_state_id, :group_state_id, :local_record_id, :remote_record_id],
|
103
|
+
local_ids.zip(remaining.keys).map{|here, there| [model_state_id, group_state_id, here, there]},
|
104
|
+
:validate => false, :timestamps => false
|
105
|
+
)
|
106
|
+
|
107
|
+
# Finally do the redirection to the new ids
|
108
|
+
remaining.each_key.each_with_index do |remote_id, i|
|
109
|
+
remaining[remote_id].each do |r|
|
110
|
+
r[column] = local_ids[i]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -1,91 +1,91 @@
|
|
1
|
-
module Offroad
|
2
|
-
private
|
3
|
-
|
4
|
-
class SendableRecordState < ActiveRecord::Base
|
5
|
-
set_table_name "offroad_sendable_record_states"
|
6
|
-
|
7
|
-
belongs_to :model_state, :class_name => "::Offroad::ModelState"
|
8
|
-
|
9
|
-
def validate
|
10
|
-
unless model_state
|
11
|
-
errors.add_to_base "Cannot find associated model state"
|
12
|
-
return
|
13
|
-
end
|
14
|
-
|
15
|
-
rec = nil
|
16
|
-
unless deleted
|
17
|
-
begin
|
18
|
-
rec = app_record
|
19
|
-
rescue ActiveRecord::RecordNotFound
|
20
|
-
errors.add_to_base "Cannot find associated app record"
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
if rec
|
25
|
-
if Offroad::app_offline? && app_record.class.offroad_global_data?
|
26
|
-
errors.add_to_base "Cannot create sendable record state for global data in offline app"
|
27
|
-
elsif Offroad::app_online? && app_record.class.offroad_group_data?
|
28
|
-
errors.add_to_base "Cannot create sendable record state for group data in online app"
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
named_scope :for_model, lambda { |model| { :conditions => {
|
34
|
-
:model_state_id => model.offroad_model_state.id
|
35
|
-
} } }
|
36
|
-
|
37
|
-
named_scope :for_deleted_records, :conditions => { :deleted => true }
|
38
|
-
named_scope :for_non_deleted_records, :conditions => { :deleted => false }
|
39
|
-
|
40
|
-
named_scope :with_version_greater_than, lambda { |v| { :conditions => ["mirror_version > ?", v] } }
|
41
|
-
|
42
|
-
named_scope :for_record, lambda { |rec| { :conditions => {
|
43
|
-
:model_state_id => rec.class.offroad_model_state.id,
|
44
|
-
:local_record_id => rec.id
|
45
|
-
} } }
|
46
|
-
|
47
|
-
def app_record
|
48
|
-
model_state.app_model.find(local_record_id)
|
49
|
-
end
|
50
|
-
|
51
|
-
# We put SRS records in mirror files to represent deleted records
|
52
|
-
def self.safe_to_load_from_cargo_stream?
|
53
|
-
true
|
54
|
-
end
|
55
|
-
|
56
|
-
def self.note_record_destroyed(record)
|
57
|
-
mark_record_changes(record) do |rec|
|
58
|
-
rec.deleted = true
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def self.note_record_created_or_updated(record)
|
63
|
-
mark_record_changes(record)
|
64
|
-
end
|
65
|
-
|
66
|
-
def self.setup_imported(model, batch)
|
67
|
-
model_state_id = model.offroad_model_state.id
|
68
|
-
mirror_version = SystemState::current_mirror_version
|
69
|
-
self.import(
|
70
|
-
[:model_state_id, :local_record_id, :mirror_version],
|
71
|
-
batch.map{|r| [model_state_id, r.id, mirror_version]},
|
72
|
-
:validate => false,
|
73
|
-
:timestamps => false
|
74
|
-
)
|
75
|
-
end
|
76
|
-
|
77
|
-
private
|
78
|
-
|
79
|
-
def self.mark_record_changes(record)
|
80
|
-
transaction do
|
81
|
-
scope = for_record(record)
|
82
|
-
rec_state = scope.first || scope.create
|
83
|
-
rec_state.lock!
|
84
|
-
rec_state.mirror_version = SystemState::current_mirror_version
|
85
|
-
yield(rec_state) if block_given?
|
86
|
-
rec_state.save!
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
end
|
91
|
-
end
|
1
|
+
module Offroad
|
2
|
+
private
|
3
|
+
|
4
|
+
class SendableRecordState < ActiveRecord::Base
|
5
|
+
set_table_name "offroad_sendable_record_states"
|
6
|
+
|
7
|
+
belongs_to :model_state, :class_name => "::Offroad::ModelState"
|
8
|
+
|
9
|
+
def validate
|
10
|
+
unless model_state
|
11
|
+
errors.add_to_base "Cannot find associated model state"
|
12
|
+
return
|
13
|
+
end
|
14
|
+
|
15
|
+
rec = nil
|
16
|
+
unless deleted
|
17
|
+
begin
|
18
|
+
rec = app_record
|
19
|
+
rescue ActiveRecord::RecordNotFound
|
20
|
+
errors.add_to_base "Cannot find associated app record"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
if rec
|
25
|
+
if Offroad::app_offline? && app_record.class.offroad_global_data?
|
26
|
+
errors.add_to_base "Cannot create sendable record state for global data in offline app"
|
27
|
+
elsif Offroad::app_online? && app_record.class.offroad_group_data?
|
28
|
+
errors.add_to_base "Cannot create sendable record state for group data in online app"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
named_scope :for_model, lambda { |model| { :conditions => {
|
34
|
+
:model_state_id => model.offroad_model_state.id
|
35
|
+
} } }
|
36
|
+
|
37
|
+
named_scope :for_deleted_records, :conditions => { :deleted => true }
|
38
|
+
named_scope :for_non_deleted_records, :conditions => { :deleted => false }
|
39
|
+
|
40
|
+
named_scope :with_version_greater_than, lambda { |v| { :conditions => ["mirror_version > ?", v] } }
|
41
|
+
|
42
|
+
named_scope :for_record, lambda { |rec| { :conditions => {
|
43
|
+
:model_state_id => rec.class.offroad_model_state.id,
|
44
|
+
:local_record_id => rec.id
|
45
|
+
} } }
|
46
|
+
|
47
|
+
def app_record
|
48
|
+
model_state.app_model.find(local_record_id)
|
49
|
+
end
|
50
|
+
|
51
|
+
# We put SRS records in mirror files to represent deleted records
|
52
|
+
def self.safe_to_load_from_cargo_stream?
|
53
|
+
true
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.note_record_destroyed(record)
|
57
|
+
mark_record_changes(record) do |rec|
|
58
|
+
rec.deleted = true
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.note_record_created_or_updated(record)
|
63
|
+
mark_record_changes(record)
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.setup_imported(model, batch)
|
67
|
+
model_state_id = model.offroad_model_state.id
|
68
|
+
mirror_version = SystemState::current_mirror_version
|
69
|
+
self.import(
|
70
|
+
[:model_state_id, :local_record_id, :mirror_version],
|
71
|
+
batch.map{|r| [model_state_id, r.id, mirror_version]},
|
72
|
+
:validate => false,
|
73
|
+
:timestamps => false
|
74
|
+
)
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def self.mark_record_changes(record)
|
80
|
+
transaction do
|
81
|
+
scope = for_record(record)
|
82
|
+
rec_state = scope.first || scope.create
|
83
|
+
rec_state.lock!
|
84
|
+
rec_state.mirror_version = SystemState::current_mirror_version
|
85
|
+
yield(rec_state) if block_given?
|
86
|
+
rec_state.save!
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|