offroad 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -68,7 +68,7 @@ gemspec = Gem::Specification.new do |s|
68
68
  s.require_path = 'lib'
69
69
  s.rubyforge_project = '[none]'
70
70
 
71
- s.add_dependency('ar-extensions')
71
+ s.add_dependency('ar-extensions', '0.9.4')
72
72
  end
73
73
 
74
74
  Rake::GemPackageTask.new(gemspec) do |pkg|
@@ -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 by creating temporary records to advance the autoincrement counter
85
- # TODO I'm pretty sure this is safe because it'll always be used in a transaction, but I should check
86
- model.import([model.primary_key.to_sym], [[nil]]*remaining.size, :validate => false, :timestamps => false)
87
- last_id = model.last(:select => model.primary_key, :order => model.primary_key).id
88
- local_ids = (last_id+1-remaining.size)..last_id
89
- model.delete(local_ids)
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.first+i
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
- # Emptying sqlite_sequence resets SQLite's autoincrement counters.
93
- # SQLite's autoincrement is nice in that automatically picks largest ever id + 1.
94
- # This means that after clearing sqlite_sequence and then populating database with manually-id'd rows,
95
- # new records will be inserted with unique id's, no problem.
96
- tables = ["sqlite_sequence"] + ActiveRecord::Base.connection.tables
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?("VIRTUAL_") # Used in testing
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)
@@ -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 `#{Offroad::GroupState.table_name}` ON `#{Offroad::GroupState.table_name}`.app_group_id = `#{table_name}`.`#{primary_key}`"
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 `#{Offroad::GroupState.table_name}` ON `#{Offroad::GroupState.table_name}`.app_group_id = `#{table_name}`.`#{primary_key}`",
42
+ "LEFT JOIN \"#{Offroad::GroupState.table_name}\" ON \"#{Offroad::GroupState.table_name}\".app_group_id = \"#{table_name}\".\"#{primary_key}\"",
43
43
  :conditions =>
44
- "`#{Offroad::GroupState.table_name}`.app_group_id IS NULL"
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 << "`#{assoc_owner.table_name}`.`#{assoc.primary_key_name}` = #{group.id}"
120
+ conditions << "\"#{assoc_owner.table_name}\".\"#{assoc.primary_key_name}\" = #{group.id}"
121
121
  break
122
122
  else
123
- conditions << "`#{assoc_owner.table_name}`.`#{assoc.primary_key_name}` = `#{assoc.klass.table_name}`.`#{assoc.klass.primary_key}`"
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,3 +1,3 @@
1
1
  module Offroad
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -0,0 +1,8 @@
1
+ test:
2
+ adapter: postgresql
3
+ encoding: unicode
4
+ database: offroad_test
5
+ pool: 5
6
+ username: david
7
+ password: foobar
8
+ min_messages: WARNING
@@ -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
- e.include?("offroad") || !(e.include?("/ruby/") || e.include?("/gems/"))
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 = ["sqlite_sequence"] + ActiveRecord::Base.connection.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?("VIRTUAL_")
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
- "VIRTUAL_normal_#{@prefix}_"
137
+ "virtual_normal_#{@prefix}_"
122
138
  end
123
139
 
124
140
  def fresh_prefix
125
- "VIRTUAL_fresh_#{@prefix}_"
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 = ["sqlite_sequence"] + ActiveRecord::Base.connection.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?("VIRTUAL") && dst_table.start_with?("VIRTUAL")
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: 29
4
+ hash: 27
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 1
10
- version: 0.0.1
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-11 00:00:00 -05:00
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: 3
29
+ hash: 51
30
30
  segments:
31
31
  - 0
32
- version: "0"
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