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.
Files changed (61) hide show
  1. data/LICENSE +674 -674
  2. data/README.rdoc +29 -29
  3. data/Rakefile +75 -75
  4. data/TODO +42 -42
  5. data/lib/app/models/offroad/group_state.rb +85 -85
  6. data/lib/app/models/offroad/mirror_info.rb +53 -53
  7. data/lib/app/models/offroad/model_state.rb +36 -36
  8. data/lib/app/models/offroad/received_record_state.rb +115 -115
  9. data/lib/app/models/offroad/sendable_record_state.rb +91 -91
  10. data/lib/app/models/offroad/system_state.rb +33 -33
  11. data/lib/cargo_streamer.rb +222 -222
  12. data/lib/controller_extensions.rb +74 -74
  13. data/lib/exceptions.rb +16 -16
  14. data/lib/migrate/20100512164608_create_offroad_tables.rb +72 -72
  15. data/lib/mirror_data.rb +376 -376
  16. data/lib/model_extensions.rb +378 -377
  17. data/lib/module_funcs.rb +94 -94
  18. data/lib/offroad.rb +41 -41
  19. data/lib/version.rb +3 -3
  20. data/lib/view_helper.rb +7 -7
  21. data/templates/offline.rb +36 -36
  22. data/templates/offline_database.yml +7 -7
  23. data/templates/offroad.yml +6 -6
  24. data/test/app_root/app/controllers/application_controller.rb +2 -2
  25. data/test/app_root/app/controllers/group_controller.rb +28 -28
  26. data/test/app_root/app/models/global_record.rb +10 -10
  27. data/test/app_root/app/models/group.rb +12 -12
  28. data/test/app_root/app/models/group_owned_record.rb +68 -68
  29. data/test/app_root/app/models/guest.rb +7 -7
  30. data/test/app_root/app/models/subrecord.rb +12 -12
  31. data/test/app_root/app/models/unmirrored_record.rb +4 -4
  32. data/test/app_root/app/views/group/download_down_mirror.html.erb +3 -3
  33. data/test/app_root/app/views/group/download_initial_down_mirror.html.erb +3 -3
  34. data/test/app_root/app/views/group/download_up_mirror.html.erb +5 -5
  35. data/test/app_root/app/views/layouts/mirror.html.erb +8 -8
  36. data/test/app_root/config/boot.rb +115 -115
  37. data/test/app_root/config/database-pg.yml +8 -8
  38. data/test/app_root/config/database.yml +5 -5
  39. data/test/app_root/config/environment.rb +24 -24
  40. data/test/app_root/config/environments/test.rb +17 -17
  41. data/test/app_root/config/offroad.yml +6 -6
  42. data/test/app_root/config/routes.rb +4 -4
  43. data/test/app_root/db/migrate/20100529235049_create_tables.rb +64 -64
  44. data/test/app_root/lib/common_hobo.rb +15 -15
  45. data/test/app_root/vendor/plugins/offroad/init.rb +2 -2
  46. data/test/functional/mirror_operations_test.rb +148 -148
  47. data/test/test_helper.rb +453 -453
  48. data/test/unit/app_state_tracking_test.rb +275 -275
  49. data/test/unit/cargo_streamer_test.rb +332 -332
  50. data/test/unit/global_data_test.rb +102 -102
  51. data/test/unit/group_controller_test.rb +152 -152
  52. data/test/unit/group_data_test.rb +442 -435
  53. data/test/unit/group_single_test.rb +136 -136
  54. data/test/unit/hobo_permissions_test.rb +57 -57
  55. data/test/unit/mirror_data_test.rb +1283 -1283
  56. data/test/unit/mirror_info_test.rb +31 -31
  57. data/test/unit/module_funcs_test.rb +37 -37
  58. data/test/unit/pathological_model_test.rb +62 -62
  59. data/test/unit/test_framework_test.rb +86 -86
  60. data/test/unit/unmirrored_data_test.rb +14 -14
  61. 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