rubyrep 1.0.9 → 1.1.0

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 (38) hide show
  1. data/History.txt +5 -0
  2. data/config/hoe.rb +2 -2
  3. data/config/requirements.rb +1 -1
  4. data/lib/rubyrep/committers/buffered_committer.rb +20 -15
  5. data/lib/rubyrep/configuration.rb +11 -7
  6. data/lib/rubyrep/connection_extenders/connection_extenders.rb +1 -1
  7. data/lib/rubyrep/connection_extenders/jdbc_extender.rb +16 -34
  8. data/lib/rubyrep/connection_extenders/postgresql_extender.rb +66 -0
  9. data/lib/rubyrep/proxy_connection.rb +30 -3
  10. data/lib/rubyrep/replication_helper.rb +33 -3
  11. data/lib/rubyrep/replication_run.rb +20 -1
  12. data/lib/rubyrep/replicators/replicators.rb +6 -1
  13. data/lib/rubyrep/replicators/two_way_replicator.rb +12 -20
  14. data/lib/rubyrep/sync_helper.rb +14 -6
  15. data/lib/rubyrep/syncers/syncers.rb +3 -3
  16. data/lib/rubyrep/syncers/two_way_syncer.rb +4 -4
  17. data/lib/rubyrep/table_sync.rb +23 -1
  18. data/lib/rubyrep/version.rb +2 -2
  19. data/spec/configuration_spec.rb +5 -0
  20. data/spec/connection_extender_interface_spec.rb +16 -21
  21. data/spec/db_specific_connection_extenders_spec.rb +1 -1
  22. data/spec/logged_change_spec.rb +2 -2
  23. data/spec/noisy_connection_spec.rb +7 -9
  24. data/spec/postgresql_schema_support_spec.rb +5 -2
  25. data/spec/postgresql_support_spec.rb +8 -2
  26. data/spec/proxy_connection_spec.rb +34 -30
  27. data/spec/replication_helper_spec.rb +40 -0
  28. data/spec/replication_run_spec.rb +114 -3
  29. data/spec/replicators_spec.rb +5 -0
  30. data/spec/strange_name_support_spec.rb +14 -3
  31. data/spec/sync_helper_spec.rb +11 -4
  32. data/spec/syncers_spec.rb +6 -6
  33. data/spec/table_sync_spec.rb +60 -4
  34. data/spec/two_way_replicator_spec.rb +18 -38
  35. data/spec/two_way_syncer_spec.rb +6 -6
  36. data/spec/type_casting_cursor_spec.rb +8 -8
  37. data/tasks/database.rake +6 -0
  38. metadata +4 -4
data/History.txt CHANGED
@@ -1,3 +1,8 @@
1
+ == 1.1.0 2009-12-15
2
+
3
+ * Feature: filtering of sync / replication events with custom specified functionality
4
+ * Feature: compatibility with JRuby 1.4.0 and ActiveRecord 2.3.5
5
+
1
6
  == 1.0.9 2009-10-14
2
7
 
3
8
  * Bug fix: ReplicationRun#run should load only one change log record to figure out if there are pending changes
data/config/hoe.rb CHANGED
@@ -59,8 +59,8 @@ hoe = Hoe.new(GEM_NAME, VERS) do |p|
59
59
  p.changes = p.paragraphs_of("History.txt", 0..1).join("\\n\\n")
60
60
  #p.extra_deps = [] # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
61
61
  p.extra_deps = [
62
- ['activesupport', '>= 2.2.2'],
63
- ['activerecord' , '>= 2.2.2']
62
+ ['activesupport', '>= 2.3.5'],
63
+ ['activerecord' , '>= 2.3.5']
64
64
  ]
65
65
 
66
66
  #p.spec_extras = {} # A hash of extra values to set in the gemspec.
@@ -4,7 +4,7 @@ include FileUtils
4
4
  require 'rubygems'
5
5
 
6
6
  # Load essential gems. Exit if not available.
7
- %w[rake activerecord].each do |req_gem|
7
+ %w[rake active_record].each do |req_gem|
8
8
  begin
9
9
  require req_gem
10
10
  rescue LoadError
@@ -16,11 +16,13 @@ module RR
16
16
 
17
17
  # Switches the trigger mode of the specified +table+ in the specified
18
18
  # +database+ to ignore rubyrep activity.
19
+ # * +database+: identifying the database (either :+left+ or :+right+)
20
+ # * +table+: name of the table
19
21
  def exclude_rr_activity(database, table)
20
22
  trigger_mode_switcher.exclude_rr_activity database, table
21
23
  end
22
24
 
23
- # Returns the trigger mode switcher (creates it if necessary)
25
+ # Returns the TriggerModeSwitcher (creates it if necessary)
24
26
  def trigger_mode_switcher
25
27
  @trigger_mode_switcher ||= TriggerModeSwitcher.new session
26
28
  end
@@ -96,36 +98,39 @@ module RR
96
98
  end
97
99
 
98
100
  # A new committer is created for each table sync.
99
- # * session: a Session object representing the current database session
101
+ # * session: a Session object representing the current database session
100
102
  def initialize(session)
101
103
  super
102
104
  begin_db_transactions
103
105
  end
104
106
 
105
- # Inserts the specified record in the specified +database+ (either :left or :right).
106
- # +table+ is the name of the target table.
107
- # +values+ is a hash of column_name => value pairs.
107
+ # Inserts the specified record in the specified database.
108
+ # * +database+: identifying the database (either :+left+ or :+right+)
109
+ # * +table+: name of the table
110
+ # * +values+: a hash of column_name => value pairs.
108
111
  def insert_record(database, table, values)
109
112
  exclude_rr_activity database, table
110
113
  super
111
114
  commit
112
115
  end
113
116
 
114
- # Updates the specified record in the specified +database+ (either :left or :right).
115
- # +table+ is the name of the target table.
116
- # +values+ is a hash of column_name => value pairs.
117
- # +old_key+ is a column_name => value hash with the original primary key.
118
- # If +old_key+ is +nil+, then the primary key must be contained in +values+.
117
+ # Updates the specified record in the specified database.
118
+ # * +database+: identifying the database (either :+left+ or :+right+)
119
+ # * +table+: name of the table
120
+ # * +values+: a hash of column_name => value pairs.
121
+ # * +old_key+:
122
+ # A column_name => value hash identifying original primary key.
123
+ # If +nil+, then the primary key must be contained in +values+.
119
124
  def update_record(database, table, values, old_key = nil)
120
125
  exclude_rr_activity database, table
121
126
  super
122
127
  commit
123
128
  end
124
129
 
125
- # Deletes the specified record in the specified +database+ (either :left or :right).
126
- # +table+ is the name of the target table.
127
- # +values+ is a hash of column_name => value pairs. (Only the primary key
128
- # values will be used and must be included in the hash.)
130
+ # Deletes the specified record in the specified database.
131
+ # * +database+: identifying the database (either :+left+ or :+right+)
132
+ # * +table+: name of the table
133
+ # * +values+: a hash of column_name => value pairs (must only contain primary key columns).
129
134
  def delete_record(database, table, values)
130
135
  exclude_rr_activity database, table
131
136
  super
@@ -133,7 +138,7 @@ module RR
133
138
  end
134
139
 
135
140
  # Is called after the last insert / update / delete query.
136
- # +success+ should be true if there were no problems, false otherwise.
141
+ # * +success+: should be true if there were no problems, false otherwise.
137
142
  def finalize(success = true)
138
143
  if success
139
144
  commit_db_transactions
@@ -239,14 +239,18 @@ module RR
239
239
  resulting_options.merge! table_options[1] if match
240
240
  end
241
241
 
242
- # Merge the default syncer options in (if syncer has some)
243
- syncer_class = Syncers.configured_syncer(resulting_options)
244
- if syncer_class.respond_to? :default_options
245
- default_syncer_options = syncer_class.default_options.clone
246
- else
247
- default_syncer_options = {}
242
+ # Merge the default syncer& replicator options in
243
+ [
244
+ Syncers.configured_syncer(resulting_options),
245
+ Replicators.configured_replicator(resulting_options)
246
+ ].each do |processor_class|
247
+ if processor_class.respond_to? :default_options
248
+ default_processor_options = processor_class.default_options.clone
249
+ else
250
+ default_processor_options = {}
251
+ end
252
+ resulting_options = default_processor_options.merge!(resulting_options)
248
253
  end
249
- resulting_options = default_syncer_options.merge! resulting_options
250
254
 
251
255
  resulting_options
252
256
  end
@@ -76,7 +76,7 @@ module RR
76
76
  # Hack to get Postgres schema support under JRuby to par with the standard
77
77
  # ruby version
78
78
  if RUBY_PLATFORM =~ /java/ and config[:adapter].to_sym == :postgresql
79
- connection.extend RR::ConnectionExtenders::JdbcPostgreSQLExtender
79
+ connection.extend RR::ConnectionExtenders::PostgreSQLExtender
80
80
  connection.initialize_search_path
81
81
  end
82
82
 
@@ -55,44 +55,26 @@ module RR
55
55
  result
56
56
  end
57
57
  end
58
+ end
59
+ end
58
60
 
59
- # PostgreSQL specific functionality not provided by the standard JDBC
60
- # connection extender:
61
- # * Hack to get schema support for Postgres under JRuby on par with the
62
- # standard ruby version.
63
- module JdbcPostgreSQLExtender
64
- require 'jdbc_adapter/jdbc_postgre'
65
- require "#{File.dirname(__FILE__)}/postgresql_extender"
66
-
67
- include PostgreSQLExtender
68
-
69
- class JdbcPostgreSQLColumn < ActiveRecord::ConnectionAdapters::Column
70
- include ::JdbcSpec::PostgreSQL::Column
71
- end
72
-
73
- # Returns the list of all column definitions for a table.
74
- def columns(table_name, name = nil)
75
- # Limit, precision, and scale are all handled by the superclass.
76
- column_definitions(table_name).collect do |name, type, default, notnull|
77
- JdbcPostgreSQLColumn.new(name, default, type, notnull == 'f')
78
- end
79
- end
80
-
81
- # Sets the schema search path as per configuration parameters
82
- def initialize_search_path
83
- execute "SET search_path TO #{config[:schema_search_path]}" if config[:schema_search_path]
61
+ # activerecord-jdbc-adapter 0.9.1 (7b3f3eca08149567070837fad63696052dc36cd6)
62
+ # improves SQLite binary support by overwriting the global string_to_binary
63
+ # methods.
64
+ # This appears to break binary support for MySQL.
65
+ # And here comes the monkey patch to revert it again...
66
+ require 'active_record/connection_adapters/jdbc_adapter_spec'
67
+ require 'jdbc_adapter/jdbc_sqlite3'
68
+ module ::ActiveRecord
69
+ module ConnectionAdapters
70
+ class JdbcColumn < Column
71
+ def self.string_to_binary(value)
72
+ value
84
73
  end
85
74
 
86
- # Converts the given Time object into the correctly formatted string
87
- # representation.
88
- #
89
- # Monkeypatched as activerecord-jdbcpostgresql-adapter (at least in version
90
- # 0.8.2) does otherwise "loose" the microseconds when writing Time values
91
- # to the database.
92
- def quoted_date(value)
93
- "#{value.strftime("%Y-%m-%d %H:%M:%S")}#{value.respond_to?(:usec) ? ".#{value.usec.to_s.rjust(6, '0')}" : ""}"
75
+ def self.binary_to_string(value)
76
+ value
94
77
  end
95
78
  end
96
79
  end
97
80
  end
98
-
@@ -107,6 +107,15 @@ module RR
107
107
  SQL
108
108
  end
109
109
 
110
+ # Disables schema extraction from table names by overwriting the according
111
+ # ActiveRecord method.
112
+ # Necessary to support table names containing dots (".").
113
+ # (This is possible as rubyrep exclusively uses the search_path setting to
114
+ # support PostgreSQL schemas.)
115
+ def extract_pg_identifier_from_name(name)
116
+ return name, nil
117
+ end
118
+
110
119
  # Returns an ordered list of primary key column names of the given table
111
120
  def primary_key_names(table)
112
121
  row = self.select_one(<<-end_sql)
@@ -177,6 +186,63 @@ module RR
177
186
  result
178
187
  end
179
188
 
189
+ # Sets the schema search path as per configuration parameters
190
+ def initialize_search_path
191
+ execute "SET search_path TO #{config[:schema_search_path] || 'public'}"
192
+ end
193
+
194
+ # *** Moneky patch***
195
+ # Returns the column objects for the named table.
196
+ # Fixes JRuby schema support
197
+ def columns(table_name, name = nil)
198
+ jdbc_connection = @connection.connection # the actual JDBC DatabaseConnection
199
+ @unquoted_schema ||= select_one("show search_path")['search_path']
200
+
201
+ # check if table exists
202
+ table_results = jdbc_connection.meta_data.get_tables(
203
+ jdbc_connection.catalog,
204
+ @unquoted_schema,
205
+ table_name,
206
+ ["TABLE","VIEW","SYNONYM"].to_java(:string)
207
+ )
208
+ table_exists = table_results.next
209
+ table_results.close
210
+ raise "table '#{table_name}' not found" unless table_exists
211
+
212
+ # get ResultSet for columns of table
213
+ column_results = jdbc_connection.meta_data.get_columns(
214
+ jdbc_connection.catalog,
215
+ @unquoted_schema,
216
+ table_name,
217
+ nil
218
+ )
219
+
220
+ # create the Column objects
221
+ columns = []
222
+ while column_results.next
223
+
224
+ # generate type clause
225
+ type_clause = column_results.get_string('TYPE_NAME')
226
+ precision = column_results.get_int('COLUMN_SIZE')
227
+ scale = column_results.get_int('DECIMAL_DIGITS')
228
+ if precision > 0
229
+ type_clause += "(#{precision}#{scale > 0 ? ",#{scale}" : ""})"
230
+ end
231
+
232
+ # create column
233
+ columns << ::ActiveRecord::ConnectionAdapters::JdbcColumn.new(
234
+ @config,
235
+ column_results.get_string('COLUMN_NAME'),
236
+ column_results.get_string('COLUMN_DEF'),
237
+ type_clause,
238
+ column_results.get_string('IS_NULLABLE').strip == "NO"
239
+ )
240
+ end
241
+ column_results.close
242
+
243
+ columns
244
+ end if RUBY_PLATFORM =~ /java/
245
+
180
246
  # *** Monkey patch***
181
247
  # Returns the list of a table's column names, data types, and default values.
182
248
  # This overwrites the according ActiveRecord::PostgreSQLAdapter method
@@ -184,19 +184,46 @@ module RR
184
184
  # * first build the query based on options forwarded to #table_select_query
185
185
  # +options+ is a hash with
186
186
  # * :+query+: executes the given query
187
- # * :+type_cast+: if +true+, build a type casting cursor around the result
188
- # * :+table+: name of the table from which to read data
187
+ # * :+type_cast+:
188
+ # Unless explicitely disabled with +false+, build type casting cursor
189
+ # around result.
190
+ # * :+table+:
191
+ # Name of the table from which to read data.
192
+ # Required unless type casting is disabled.
189
193
  # * further options as taken by #table_select_query to build the query
190
194
  # * :+row_buffer_size+:
191
195
  # Integer controlling how many rows a read into memory at one time.
192
196
  def select_cursor(options)
193
197
  cursor = ResultFetcher.new(self, options)
194
- if options[:type_cast]
198
+ unless options[:type_cast] == false
195
199
  cursor = TypeCastingCursor.new(self, options[:table], cursor)
196
200
  end
197
201
  cursor
198
202
  end
203
+
204
+ # Reads the designated record from the database.
205
+ # Refer to #select_cursor for details parameter description.
206
+ # Returns the first matching row (column_name => value hash or +nil+).
207
+ def select_record(options)
208
+ cursor = select_cursor({:row_buffer_size => 1}.merge(options))
209
+ row = cursor.next? ? cursor.next_row : nil
210
+ cursor.clear
211
+ row
212
+ end
199
213
 
214
+ # Reads the designated records from the database.
215
+ # Refer to #select_cursor for details parameter description.
216
+ # Returns an array of matching rows (column_name => value hashes).
217
+ def select_records(options)
218
+ cursor = select_cursor(options)
219
+ rows = []
220
+ while cursor.next?
221
+ rows << cursor.next_row
222
+ end
223
+ cursor.clear
224
+ rows
225
+ end
226
+
200
227
  # Create a session on the proxy side according to provided configuration hash.
201
228
  # +config+ is a hash as described by ActiveRecord::Base#establish_connection
202
229
  def initialize(config)
@@ -16,6 +16,16 @@ module RR
16
16
  # Current options
17
17
  def options; @options ||= session.configuration.options; end
18
18
 
19
+ # Returns the options for the specified table name.
20
+ # * +table+: name of the table (left database version)
21
+ def options_for_table(table)
22
+ @options_for_table ||= {}
23
+ unless @options_for_table.include? table
24
+ @options_for_table[table] = session.configuration.options_for_table(table)
25
+ end
26
+ @options_for_table[table]
27
+ end
28
+
19
29
  # Delegates to Session#corresponding_table
20
30
  def corresponding_table(db_arm, table); session.corresponding_table(db_arm, table); end
21
31
 
@@ -25,17 +35,17 @@ module RR
25
35
  committer.new_transaction?
26
36
  end
27
37
 
28
- # Delegates to Committer#insert_record
38
+ # Delegates to Committers::BufferedCommitter#insert_record
29
39
  def insert_record(database, table, values)
30
40
  committer.insert_record(database, table, values)
31
41
  end
32
42
 
33
- # Delegates to Committer#insert_record
43
+ # Delegates to Committers::BufferedCommitter#update_record
34
44
  def update_record(database, table, values, old_key = nil)
35
45
  committer.update_record(database, table, values, old_key)
36
46
  end
37
47
 
38
- # Delegates to Committer#insert_record
48
+ # Delegates to Committers::BufferedCommitter#delete_record
39
49
  def delete_record(database, table, values)
40
50
  committer.delete_record(database, table, values)
41
51
  end
@@ -67,6 +77,26 @@ module RR
67
77
  committer.finalize(success)
68
78
  end
69
79
 
80
+ # Converts the row values into their proper types as per table definition.
81
+ # * +table+: name of the table after whose columns is type-casted.
82
+ # * +row+: A column_name => value hash of the row
83
+ # Returns a copy of the column_name => value hash (with type-casted values).
84
+ def type_cast(table, row)
85
+ @table_columns ||= {}
86
+ unless @table_columns.include?(table)
87
+ column_array = session.left.columns(table)
88
+ column_hash = {}
89
+ column_array.each {|column| column_hash[column.name] = column}
90
+ @table_columns[table] = column_hash
91
+ end
92
+ columns = @table_columns[table]
93
+ type_casted_row = {}
94
+ row.each_pair do |column_name, value|
95
+ type_casted_row[column_name] = columns[column_name].type_cast(value)
96
+ end
97
+ type_casted_row
98
+ end
99
+
70
100
  # Logs the outcome of a replication into the replication log table.
71
101
  # * +diff+: the replicated ReplicationDifference
72
102
  # * +outcome+: string summarizing the outcome of the replication
@@ -22,6 +22,23 @@ module RR
22
22
  Replicators.replicators[session.configuration.options[:replicator]].new(helper)
23
23
  end
24
24
 
25
+ # Calls the event filter for the give difference.
26
+ # * +diff+: instance of ReplicationDifference
27
+ # Returns +true+ if replication of the difference should *not* proceed.
28
+ def event_filtered?(diff)
29
+ event_filter = helper.options_for_table(diff.changes[:left].table)[:event_filter]
30
+ if event_filter && event_filter.respond_to?(:before_replicate)
31
+ not event_filter.before_replicate(
32
+ diff.changes[:left].table,
33
+ helper.type_cast(diff.changes[:left].table, diff.changes[:left].key),
34
+ helper,
35
+ diff
36
+ )
37
+ else
38
+ false
39
+ end
40
+ end
41
+
25
42
  # Executes the replication run.
26
43
  def run
27
44
  return unless [:left, :right].any? do |database|
@@ -53,7 +70,9 @@ module RR
53
70
  diff.load
54
71
  break unless diff.loaded?
55
72
  break if sweeper.terminated?
56
- replicator.replicate_difference diff if diff.type != :no_diff
73
+ if diff.type != :no_diff and not event_filtered?(diff)
74
+ replicator.replicate_difference diff
75
+ end
57
76
  rescue Exception => e
58
77
  begin
59
78
  helper.log_replication_outcome diff, e.message,
@@ -23,7 +23,12 @@ module RR
23
23
  @replicators ||= {}
24
24
  @replicators
25
25
  end
26
-
26
+
27
+ # Returns the correct replicator class as per provided options hash
28
+ def self.configured_replicator(options)
29
+ replicators[options[:replicator]]
30
+ end
31
+
27
32
  # Registers one or multiple replicators.
28
33
  # syncer_hash is a Hash with
29
34
  # key:: The adapter symbol as used to reference the replicator
@@ -60,13 +60,16 @@ module RR
60
60
  # The current ReplicationHelper object
61
61
  attr_accessor :rep_helper
62
62
 
63
- # Default TwoWayReplicator options.
64
- DEFAULT_OPTIONS = {
65
- :left_change_handling => :replicate,
66
- :right_change_handling => :replicate,
67
- :replication_conflict_handling => :ignore,
68
- :logged_replication_events => [:ignored_conflicts],
69
- }
63
+ # Provides default option for the replicator. Optional.
64
+ # Returns a hash with key => value pairs.
65
+ def self.default_options
66
+ {
67
+ :left_change_handling => :replicate,
68
+ :right_change_handling => :replicate,
69
+ :replication_conflict_handling => :ignore,
70
+ :logged_replication_events => [:ignored_conflicts],
71
+ }
72
+ end
70
73
 
71
74
  # Checks if an option is configured correctly. Raises an ArgumentError if not.
72
75
  # * +table_spec+: the table specification to which the option belongs. May be +nil+.
@@ -173,23 +176,12 @@ module RR
173
176
  end
174
177
  end
175
178
 
176
- # Returns the options for the specified table name.
177
- # * +table+: name of the table (left database version)
178
- def options_for_table(table)
179
- @options_for_table ||= {}
180
- unless @options_for_table.include? table
181
- @options_for_table[table] = DEFAULT_OPTIONS.merge(
182
- rep_helper.session.configuration.options_for_table(table))
183
- end
184
- @options_for_table[table]
185
- end
186
-
187
179
  # Logs replication of the specified difference as per configured
188
180
  # :+replication_conflict_logging+ / :+left_change_logging+ / :+right_change_logging+ options.
189
181
  # * +winner+: Either the winner database (:+left+ or :+right+) or :+ignore+
190
182
  # * +diff+: the ReplicationDifference instance
191
183
  def log_replication_outcome(winner, diff)
192
- options = options_for_table(diff.changes[:left].table)
184
+ options = rep_helper.options_for_table(diff.changes[:left].table)
193
185
  option_values = [options[:logged_replication_events]].flatten # make sure I have an array
194
186
  if diff.type == :conflict
195
187
  return unless option_values.include?(:all_conflicts) or option_values.include?(:ignored_conflicts)
@@ -321,7 +313,7 @@ module RR
321
313
  # * :+previous_failure_description+: why the previous replication attempt failed
322
314
  def replicate_difference(diff, remaining_attempts = MAX_REPLICATION_ATTEMPTS, previous_failure_description = nil)
323
315
  raise Exception, previous_failure_description || "max replication attempts exceeded" if remaining_attempts == 0
324
- options = options_for_table(diff.changes[:left].table)
316
+ options = rep_helper.options_for_table(diff.changes[:left].table)
325
317
  if diff.type == :left or diff.type == :right
326
318
  key = diff.type == :left ? :left_change_handling : :right_change_handling
327
319
  option = options[key]
@@ -26,21 +26,29 @@ module RR
26
26
  @tables ||= {:left => left_table, :right => right_table}
27
27
  end
28
28
 
29
+ # Given a column_name => value hash of a full row, returns a
30
+ # column_name => value hash of the primary key columns.
31
+ # * +row+: the full row
32
+ # Returns
33
+ def extract_key(row)
34
+ row.reject {|column, value| not primary_key_names.include? column }
35
+ end
36
+
29
37
  # Sync options for the current table sync
30
38
  def sync_options; @sync_options ||= table_sync.sync_options; end
31
39
 
32
- # Delegates to Committer#insert_record
33
- def insert_record(database, values)
40
+ # Delegates to Committers::BufferedCommitter#insert_record
41
+ def insert_record(database, table, values)
34
42
  committer.insert_record(database, tables[database], values)
35
43
  end
36
44
 
37
- # Delegates to Committer#insert_record
38
- def update_record(database, values, old_key = nil)
45
+ # Delegates to Committers::BufferedCommitter#update_record
46
+ def update_record(database, table, values, old_key = nil)
39
47
  committer.update_record(database, tables[database], values, old_key)
40
48
  end
41
49
 
42
- # Delegates to Committer#insert_record
43
- def delete_record(database, values)
50
+ # Delegates to Committers::BufferedCommitter#delete_record
51
+ def delete_record(database, table, values)
44
52
  committer.delete_record(database, tables[database], values)
45
53
  end
46
54
 
@@ -94,15 +94,15 @@ module RR
94
94
  case type
95
95
  when source
96
96
  if sync_helper.sync_options[:insert]
97
- sync_helper.insert_record target, row
97
+ sync_helper.insert_record target, sync_helper.tables[target], row
98
98
  end
99
99
  when target
100
100
  if sync_helper.sync_options[:delete]
101
- sync_helper.delete_record target, row
101
+ sync_helper.delete_record target, sync_helper.tables[target], row
102
102
  end
103
103
  when :conflict
104
104
  if sync_helper.sync_options[:update]
105
- sync_helper.update_record target, row[source_record_index]
105
+ sync_helper.update_record target, sync_helper.tables[target], row[source_record_index]
106
106
  end
107
107
  end
108
108
  end
@@ -148,10 +148,10 @@ module RR
148
148
  if option == :ignore
149
149
  # nothing to do
150
150
  elsif option == :delete
151
- sync_helper.delete_record type, row
151
+ sync_helper.delete_record type, sync_helper.tables[type], row
152
152
  elsif option == :insert
153
153
  target = (type == :left ? :right : :left)
154
- sync_helper.insert_record target, row
154
+ sync_helper.insert_record target, sync_helper.tables[target], row
155
155
  else #option must be a Proc
156
156
  option.call sync_helper, type, row
157
157
  end
@@ -161,9 +161,9 @@ module RR
161
161
  if option == :ignore
162
162
  # nothing to do
163
163
  elsif option == :right_wins
164
- sync_helper.update_record :left, row[1]
164
+ sync_helper.update_record :left, sync_helper.tables[:left], row[1]
165
165
  elsif option == :left_wins
166
- sync_helper.update_record :right, row[0]
166
+ sync_helper.update_record :right, sync_helper.tables[:right], row[0]
167
167
  else #option must be a Proc
168
168
  option.call sync_helper, type, row
169
169
  end
@@ -34,6 +34,26 @@ module RR
34
34
  end
35
35
  end
36
36
 
37
+ # Calls the event filter for the give table difference.
38
+ # * +type+: type of difference
39
+ # * +row+: the differing row
40
+ # Refer to DirectTableScan#run for full description of +type+ and +row+.
41
+ # Returns +true+ if syncing of the difference should *not* proceed.
42
+ def event_filtered?(type, row)
43
+ event_filter = sync_options[:event_filter]
44
+ if event_filter && event_filter.respond_to?(:before_sync)
45
+ not event_filter.before_sync(
46
+ helper.left_table,
47
+ helper.extract_key([row].flatten.first),
48
+ helper,
49
+ type,
50
+ row
51
+ )
52
+ else
53
+ false
54
+ end
55
+ end
56
+
37
57
  # Executes the table sync. If a block is given, yields each difference with
38
58
  # the following 2 parameters
39
59
  # * +type+
@@ -54,7 +74,9 @@ module RR
54
74
 
55
75
  scan.run do |type, row|
56
76
  yield type, row if block_given? # To enable progress reporting
57
- syncer.sync_difference type, row
77
+ unless event_filtered?(type, row)
78
+ syncer.sync_difference type, row
79
+ end
58
80
  end
59
81
 
60
82
  execute_sync_hook :after_table_sync
@@ -1,8 +1,8 @@
1
1
  module RR #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 1
4
- MINOR = 0
5
- TINY = 9
4
+ MINOR = 1
5
+ TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end