rubyrep 1.0.9 → 1.1.0

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