offroad 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|