offroad 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Rakefile +1 -1
- data/lib/app/models/offroad/received_record_state.rb +13 -7
- data/lib/mirror_data.rb +28 -6
- data/lib/model_extensions.rb +5 -5
- data/lib/offroad.rb +11 -0
- data/lib/version.rb +1 -1
- data/test/app_root/config/database-pg.yml +8 -0
- data/test/app_root/config/environment.rb +9 -0
- data/test/test_helper.rb +55 -7
- data/test/unit/mirror_data_test.rb +13 -1
- metadata +10 -7
data/Rakefile
CHANGED
@@ -81,12 +81,18 @@ module Offroad
|
|
81
81
|
|
82
82
|
return unless remaining.size > 0
|
83
83
|
|
84
|
-
# Reserve access to a block of local ids
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
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
|
90
96
|
|
91
97
|
# Create the corresponding RRSes
|
92
98
|
model_state_id = model.offroad_model_state.id
|
@@ -101,7 +107,7 @@ module Offroad
|
|
101
107
|
# Finally do the redirection to the new ids
|
102
108
|
remaining.each_key.each_with_index do |remote_id, i|
|
103
109
|
remaining[remote_id].each do |r|
|
104
|
-
r[column] = local_ids
|
110
|
+
r[column] = local_ids[i]
|
105
111
|
end
|
106
112
|
end
|
107
113
|
end
|
data/lib/mirror_data.rb
CHANGED
@@ -89,17 +89,28 @@ module Offroad
|
|
89
89
|
end
|
90
90
|
|
91
91
|
def delete_all_existing_database_records!
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
92
|
+
tables = ActiveRecord::Base.connection.tables
|
93
|
+
if ActiveRecord::Base.connection.adapter_name.downcase.include?("sqlite")
|
94
|
+
# Emptying sqlite_sequence resets SQLite's autoincrement counters.
|
95
|
+
# SQLite's autoincrement is nice in that it automatically picks largest ever id + 1.
|
96
|
+
# This means that after clearing sqlite_sequence and then populating database with manually-id'd rows,
|
97
|
+
# new records will be inserted with unique id's, no problem.
|
98
|
+
tables << "sqlite_sequence"
|
99
|
+
end
|
97
100
|
|
98
101
|
tables.each do |table|
|
99
|
-
next if table.start_with?("
|
102
|
+
next if table.start_with?("virtual_") # Used in testing # FIXME Should pick something less likely to collide with app name
|
100
103
|
next if table == "schema_migrations"
|
101
104
|
ActiveRecord::Base.connection.execute "DELETE FROM #{table}"
|
102
105
|
end
|
106
|
+
|
107
|
+
if ActiveRecord::Base.connection.adapter_name.downcase.include?("postgres")
|
108
|
+
# Reset all sequences so that autoincremented ids start from 1 again
|
109
|
+
seqnames = ActiveRecord::Base.connection.select_values "SELECT c.relname FROM pg_class c WHERE c.relkind = 'S'"
|
110
|
+
seqnames.each do |s|
|
111
|
+
ActiveRecord::Base.connection.execute "SELECT setval('#{s}', 1, false)"
|
112
|
+
end
|
113
|
+
end
|
103
114
|
end
|
104
115
|
|
105
116
|
def write_data(tgt)
|
@@ -292,6 +303,16 @@ module Offroad
|
|
292
303
|
batch.each { |rec| rec.after_offroad_upload }
|
293
304
|
end
|
294
305
|
end
|
306
|
+
if ActiveRecord::Base.connection.adapter_name.downcase.include?("postgres")
|
307
|
+
# Need to adjust the sequences so that records inserted from this point on don't collide with existing ids
|
308
|
+
cols = ActiveRecord::Base.connection.select_rows "select table_name, column_name, column_default from information_schema.columns WHERE column_default like 'nextval%'"
|
309
|
+
cols.each do |table_name, column_name, column_default|
|
310
|
+
if column_default =~ /nextval\('(.+)'(?:::.+)?\)/
|
311
|
+
seqname = $1
|
312
|
+
ActiveRecord::Base.connection.execute "SELECT setval('#{seqname}', (SELECT MAX(\"#{column_name}\") FROM \"#{table_name}\"))"
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
295
316
|
end
|
296
317
|
|
297
318
|
def import_non_initial_model_cargo(cs, model)
|
@@ -335,6 +356,7 @@ module Offroad
|
|
335
356
|
end
|
336
357
|
|
337
358
|
def validate_imported_models(cs)
|
359
|
+
Offroad::group_base_model.connection.clear_query_cache
|
338
360
|
while @imported_models_to_validate.size > 0
|
339
361
|
model = @imported_models_to_validate.pop
|
340
362
|
rrs_source = Offroad::ReceivedRecordState.for_model_and_group_if_apropos(model, @group)
|
data/lib/model_extensions.rb
CHANGED
@@ -35,13 +35,13 @@ module Offroad
|
|
35
35
|
named_scope :owned_by_offroad_group, lambda { |group| { :conditions => { :id => group.id } } }
|
36
36
|
named_scope :offline_groups, {
|
37
37
|
:joins =>
|
38
|
-
"INNER JOIN
|
38
|
+
"INNER JOIN \"#{Offroad::GroupState.table_name}\" ON \"#{Offroad::GroupState.table_name}\".app_group_id = \"#{table_name}\".\"#{primary_key}\""
|
39
39
|
}
|
40
40
|
named_scope :online_groups, {
|
41
41
|
:joins =>
|
42
|
-
"LEFT JOIN
|
42
|
+
"LEFT JOIN \"#{Offroad::GroupState.table_name}\" ON \"#{Offroad::GroupState.table_name}\".app_group_id = \"#{table_name}\".\"#{primary_key}\"",
|
43
43
|
:conditions =>
|
44
|
-
"
|
44
|
+
"\"#{Offroad::GroupState.table_name}\".app_group_id IS NULL"
|
45
45
|
}
|
46
46
|
when :group_owned then
|
47
47
|
named_scope :owned_by_offroad_group, lambda { |group| args_for_ownership_scope(group) }
|
@@ -117,10 +117,10 @@ module Offroad
|
|
117
117
|
assoc = offroad_parent_assoc
|
118
118
|
while true
|
119
119
|
if assoc.klass.offroad_group_base?
|
120
|
-
conditions << "
|
120
|
+
conditions << "\"#{assoc_owner.table_name}\".\"#{assoc.primary_key_name}\" = #{group.id}"
|
121
121
|
break
|
122
122
|
else
|
123
|
-
conditions << "
|
123
|
+
conditions << "\"#{assoc_owner.table_name}\".\"#{assoc.primary_key_name}\" = \"#{assoc.klass.table_name}\".\"#{assoc.klass.primary_key}\""
|
124
124
|
included_assocs << assoc
|
125
125
|
assoc_owner = assoc.klass
|
126
126
|
assoc = assoc.klass.offroad_parent_assoc
|
data/lib/offroad.rb
CHANGED
@@ -11,6 +11,17 @@ $LOAD_PATH << path
|
|
11
11
|
ActiveSupport::Dependencies.autoload_paths << path
|
12
12
|
|
13
13
|
require 'ar-extensions' # External dependency
|
14
|
+
# Monkey patch a bug in ar-extensions which breaks postgres compatibility
|
15
|
+
module ActiveRecord # :nodoc:
|
16
|
+
module ConnectionAdapters # :nodoc:
|
17
|
+
class AbstractAdapter # :nodoc:
|
18
|
+
def next_value_for_sequence(sequence_name)
|
19
|
+
%{nextval('#{sequence_name}')}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
14
25
|
|
15
26
|
require 'controller_extensions'
|
16
27
|
class ActionController::Base
|
data/lib/version.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), 'boot')
|
2
2
|
|
3
|
+
RAILS_GEM_VERSION = '2.3.11'
|
4
|
+
|
3
5
|
Rails::Initializer.run do |config|
|
4
6
|
config.cache_classes = false
|
5
7
|
config.whiny_nils = true
|
@@ -12,4 +14,11 @@ Rails::Initializer.run do |config|
|
|
12
14
|
else
|
13
15
|
HOBO_TEST_MODE = false
|
14
16
|
end
|
17
|
+
|
18
|
+
if ENV['PSQL_TEST_MODE']
|
19
|
+
puts "Using postgresql for a test database"
|
20
|
+
config.database_configuration_file = "#{RAILS_ROOT}/config/database-pg.yml"
|
21
|
+
else
|
22
|
+
puts "Using sqlite for a test database"
|
23
|
+
end
|
15
24
|
end
|
data/test/test_helper.rb
CHANGED
@@ -30,7 +30,11 @@ end
|
|
30
30
|
module Test::Unit::Util::BacktraceFilter
|
31
31
|
def filter_backtrace(backtrace, prefix = nil)
|
32
32
|
backtrace = backtrace.select do |e|
|
33
|
-
|
33
|
+
if ENV['FULL_BACKTRACE']
|
34
|
+
true
|
35
|
+
else
|
36
|
+
e.include?("offroad") || !(e.include?("/ruby/") || e.include?("/gems/"))
|
37
|
+
end
|
34
38
|
end
|
35
39
|
|
36
40
|
common_prefix = nil
|
@@ -101,12 +105,24 @@ class VirtualTestDatabase
|
|
101
105
|
end
|
102
106
|
|
103
107
|
def delete_all_rows
|
104
|
-
tables =
|
108
|
+
tables = ActiveRecord::Base.connection.tables
|
109
|
+
if ActiveRecord::Base.connection.adapter_name.downcase.include?("sqlite")
|
110
|
+
tables << "sqlite_sequence"
|
111
|
+
end
|
112
|
+
|
105
113
|
tables.each do |table|
|
106
|
-
next if table.start_with?("
|
114
|
+
next if table.downcase.start_with?("virtual_")
|
107
115
|
next if table == "schema_migrations"
|
108
116
|
ActiveRecord::Base.connection.execute "DELETE FROM #{table}"
|
109
117
|
end
|
118
|
+
|
119
|
+
if ActiveRecord::Base.connection.adapter_name.downcase.include?("postgres")
|
120
|
+
# Reset all sequences so that autoincremented ids start from 1 again
|
121
|
+
seqnames = ActiveRecord::Base.connection.select_values "SELECT c.relname FROM pg_class c WHERE c.relkind = 'S'"
|
122
|
+
seqnames.each do |s|
|
123
|
+
ActiveRecord::Base.connection.execute "SELECT setval('#{s}', 1, false)"
|
124
|
+
end
|
125
|
+
end
|
110
126
|
end
|
111
127
|
|
112
128
|
protected
|
@@ -118,11 +134,11 @@ class VirtualTestDatabase
|
|
118
134
|
private
|
119
135
|
|
120
136
|
def normal_prefix
|
121
|
-
"
|
137
|
+
"virtual_normal_#{@prefix}_"
|
122
138
|
end
|
123
139
|
|
124
140
|
def fresh_prefix
|
125
|
-
"
|
141
|
+
"virtual_fresh_#{@prefix}_"
|
126
142
|
end
|
127
143
|
|
128
144
|
def put_away
|
@@ -147,13 +163,16 @@ class VirtualTestDatabase
|
|
147
163
|
end
|
148
164
|
|
149
165
|
def copy_tables(src_prefix, dst_prefix)
|
150
|
-
tables =
|
166
|
+
tables = ActiveRecord::Base.connection.tables
|
167
|
+
if ActiveRecord::Base.connection.adapter_name.downcase.include?("sqlite")
|
168
|
+
tables << "sqlite_sequence"
|
169
|
+
end
|
151
170
|
tables.each do |src_table|
|
152
171
|
next if src_table.end_with?("schema_migrations")
|
153
172
|
next unless src_table.start_with?(src_prefix)
|
154
173
|
next if dst_prefix != "" && src_table.start_with?(dst_prefix)
|
155
174
|
dst_table = dst_prefix + src_table[(src_prefix.size)..(src_table.size)]
|
156
|
-
next if src_table.start_with?("
|
175
|
+
next if src_table.downcase.start_with?("virtual_") && dst_table.downcase.start_with?("virtual_")
|
157
176
|
if tables.include?(dst_table)
|
158
177
|
ActiveRecord::Base.connection.execute "DELETE FROM #{dst_table}"
|
159
178
|
ActiveRecord::Base.connection.execute "INSERT INTO #{dst_table} SELECT * FROM #{src_table}"
|
@@ -161,6 +180,24 @@ class VirtualTestDatabase
|
|
161
180
|
ActiveRecord::Base.connection.execute "CREATE TABLE #{dst_table} AS SELECT * FROM #{src_table}"
|
162
181
|
end
|
163
182
|
end
|
183
|
+
|
184
|
+
if ActiveRecord::Base.connection.adapter_name.downcase.include?("postgres")
|
185
|
+
# Manage postgresql sequences by keeping non-current sequence vals backed up in Ruby
|
186
|
+
@pg_seq_vals ||= {}
|
187
|
+
if src_prefix.blank?
|
188
|
+
# PG Sequences -> Ruby
|
189
|
+
@pg_seq_vals[dst_prefix] ||= {}
|
190
|
+
seqnames = ActiveRecord::Base.connection.select_values "SELECT c.relname FROM pg_class c WHERE c.relkind = 'S'"
|
191
|
+
seqnames.each do |s|
|
192
|
+
@pg_seq_vals[dst_prefix][s] = ActiveRecord::Base.connection.select_value "SELECT last_value FROM \"#{s}\""
|
193
|
+
end
|
194
|
+
else
|
195
|
+
# Ruby -> PG Sequences
|
196
|
+
@pg_seq_vals[src_prefix].each do |s, v|
|
197
|
+
ActiveRecord::Base.connection.execute "SELECT setval('#{s}', #{v}, true)"
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
164
201
|
end
|
165
202
|
|
166
203
|
def backup_instance_vars(key)
|
@@ -280,11 +317,22 @@ end
|
|
280
317
|
class Test::Unit::TestCase
|
281
318
|
@@online_database = nil
|
282
319
|
@@offline_database = nil
|
320
|
+
@@initial_setup = false
|
283
321
|
|
284
322
|
include Test::Unit::Util::BacktraceFilter
|
285
323
|
|
286
324
|
def setup
|
287
325
|
begin
|
326
|
+
unless @@initial_setup
|
327
|
+
if ActiveRecord::Base.connection.adapter_name.downcase.include?("postgresql")
|
328
|
+
puts "Dropping all postgresql tables"
|
329
|
+
tables = ActiveRecord::Base.connection.tables.each do |table|
|
330
|
+
ActiveRecord::Base.connection.execute "DROP TABLE #{table}"
|
331
|
+
end
|
332
|
+
end
|
333
|
+
@@initial_setup = true
|
334
|
+
end
|
335
|
+
|
288
336
|
unless ActiveRecord::Base.connection.table_exists?("schema_migrations")
|
289
337
|
# FIXME : Figure out why ActionController::TestCase keeps on deleting all the tables before each method
|
290
338
|
|
@@ -49,11 +49,23 @@ class MirrorDataTest < Test::Unit::TestCase
|
|
49
49
|
assert_equal @offline_group.id, group_state.app_group_id
|
50
50
|
end
|
51
51
|
|
52
|
+
def strip_msecs_in_values(hash)
|
53
|
+
new_hash = {}
|
54
|
+
hash.each do |k,v|
|
55
|
+
if v.is_a?(DateTime) || v.is_a?(Time)
|
56
|
+
new_hash[k] = v.to_i
|
57
|
+
else
|
58
|
+
new_hash[k] = v
|
59
|
+
end
|
60
|
+
end
|
61
|
+
new_hash
|
62
|
+
end
|
63
|
+
|
52
64
|
def assert_single_model_cargo_entry_matches(cs, record)
|
53
65
|
record.reload
|
54
66
|
data_name = Offroad::MirrorData.send(:data_cargo_name_for_model, record.class)
|
55
67
|
assert_single_cargo_section_named cs, data_name
|
56
|
-
assert_equal record.attributes, cs.first_cargo_element(data_name).attributes
|
68
|
+
assert_equal strip_msecs_in_values(record.attributes), strip_msecs_in_values(cs.first_cargo_element(data_name).attributes)
|
57
69
|
end
|
58
70
|
|
59
71
|
def assert_record_not_present(cs, record)
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: offroad
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 27
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 2
|
10
|
+
version: 0.0.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- David Mike Simon
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-04-
|
18
|
+
date: 2011-04-15 00:00:00 -05:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -24,12 +24,14 @@ dependencies:
|
|
24
24
|
requirement: &id001 !ruby/object:Gem::Requirement
|
25
25
|
none: false
|
26
26
|
requirements:
|
27
|
-
- - "
|
27
|
+
- - "="
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
hash:
|
29
|
+
hash: 51
|
30
30
|
segments:
|
31
31
|
- 0
|
32
|
-
|
32
|
+
- 9
|
33
|
+
- 4
|
34
|
+
version: 0.9.4
|
33
35
|
type: :runtime
|
34
36
|
version_requirements: *id001
|
35
37
|
description: Offroad manages offline instances of a Rails app on computers without Internet access. The online and offline instances can communicate via mirror files, transported by the user via thumbdrive, burned CD, etc.
|
@@ -79,6 +81,7 @@ files:
|
|
79
81
|
- test/app_root/app/views/group/upload_up_mirror.html.erb
|
80
82
|
- test/app_root/app/views/layouts/mirror.html.erb
|
81
83
|
- test/app_root/config/boot.rb
|
84
|
+
- test/app_root/config/database-pg.yml
|
82
85
|
- test/app_root/config/database.yml
|
83
86
|
- test/app_root/config/environment.rb
|
84
87
|
- test/app_root/config/environments/test.rb
|