Skip to content

Commit

Permalink
Separate storage of table options and list of tables to be processed;…
Browse files Browse the repository at this point in the history
… possibility to exclude tables from processing.
  • Loading branch information
alehmann committed Oct 19, 2008
1 parent 376d5ac commit 9387d6a
Show file tree
Hide file tree
Showing 10 changed files with 124 additions and 49 deletions.
4 changes: 2 additions & 2 deletions config/test_config.rb
Expand Up @@ -9,6 +9,6 @@
:syncer => :two_way,
:conflict_handling => :update_right
}
config.add_tables 'scanner_left_records_only'
config.add_tables 'table_with_manual_key', :primary_key_names => ['id']
config.include_tables 'scanner_left_records_only'
config.include_tables 'table_with_manual_key', :primary_key_names => ['id']
end
8 changes: 5 additions & 3 deletions lib/rubyrep/base_runner.rb
Expand Up @@ -151,10 +151,12 @@ def execute

# Use the command line provided table specs if provided. Otherwise the
# ones from the configuration file
table_specs = options[:table_specs]
table_specs = Initializer.configuration.tables if table_specs.empty?
included_table_specs = options[:table_specs]
included_table_specs = Initializer.configuration.included_table_specs if included_table_specs.empty?

table_pairs = resolver.resolve table_specs
table_pairs = resolver.resolve \
included_table_specs,
Initializer.configuration.excluded_table_specs
table_pairs = prepare_table_pairs(table_pairs)
table_pairs.each do |table_pair|
report_printer.scan table_pair[:left], table_pair[:right] do
Expand Down
42 changes: 32 additions & 10 deletions lib/rubyrep/configuration.rb
Expand Up @@ -67,32 +67,54 @@ def options=(options)
@options ||= {}
@options = @options.merge! options
end

# Array of table specifications for tables that should be processed
# Refer to #add_table_options for what constitutes a valid table specification.
def included_table_specs
@included_table_specs ||= []
end

# Array of table specifications for tables that should *not* be processed
# Refer to #add_table_options for what constitutes a valid table specification.
def excluded_table_specs
@excluded_table_specs ||= []
end

# A list of tables that should be processed (scanned, synced, ...) togehter
# with the table specific options.
# A list of tables having table specific options that should be considered
# during processing (scanned, synced, ...)
# +tables_with_options+ is a 2 element array with
# * first element: A +table_spec+ (either a table name or a regexp matching multiple tables)
# * second element: The +options+ hash (detailed format described in #add_tables
# Should only be accessed via #add_tables and #options_for_table
# Should only be accessed via #add_table_options and #options_for_table
def tables_with_options
@tables_with_options ||= []
end

# Returns an array containing the configured table specifications.
# (#add_tables describes the format of valid table specifications.)
def tables
tables_with_options.map {|table_options| table_options[0]}
# Adds the specified tables to the list of tables that should be processed.
# If options are provided, store them for future processing.
# Refer to #add_table_options for detailed description of parameters.
def include_tables(table_spec, options = nil)
included_table_specs << table_spec unless included_table_specs.include?(table_spec)
add_table_options(table_spec, options) if options
end

# Excludes the specified table from the list of tables that should be
# processed.
# Refer to #add_table_options for detailed description of what constitutes a
# valid table specification.
def exclude_tables(table_spec)
excluded_table_specs << table_spec unless excluded_table_specs.include?(table_spec)
end

# Adds the specified +table_spec+ and it's options (if provided).
# Adds the specified options for the provided +table_spec+.
# A +table_spec+ can be either
# * a table name or
# * a table pair (e. g. "my_left_table, my_right_table")
# * a regexp matching multiple tables.
# +options+ is hash with possible generic values as described under #options.
# Additional table specific options:
# Additional, exclusively table specific options:
# * :+primary_key_names+: array of primary key names
def add_tables(table_spec, options = {})
def add_table_options(table_spec, options)
i = nil
tables_with_options.each_with_index { |table_options, k|
i = k if table_options[0] == table_spec
Expand Down
2 changes: 1 addition & 1 deletion lib/rubyrep/session.rb
Expand Up @@ -42,7 +42,7 @@ def right=(connection)
def manual_primary_keys(db_arm)
manual_primary_keys = {}
resolver = TableSpecResolver.new self
table_pairs = resolver.resolve configuration.tables
table_pairs = resolver.resolve configuration.included_table_specs
table_pairs.each do |table_pair|
key_names = configuration.options_for_table(table_pair[:left])[:primary_key_names]
if key_names
Expand Down
43 changes: 36 additions & 7 deletions lib/rubyrep/table_spec_resolver.rb
Expand Up @@ -19,14 +19,27 @@ def initialize(session)
# Table specifications are either
# * strings as produced by BaseRunner#get_options or
# * actual regular expressions
# If +excluded_table_specs+ is provided, removes all tables that match it
# (even if otherwise matching +included_table_specs+).
# Returns an array of table name pairs in Hash form.
# For example something like
# [{:left => 'my_table', :right => 'my_table_backup'}]
# Takes care that a table is only returned once.
def resolve(table_specs)
def resolve(included_table_specs, excluded_table_specs = [])
table_pairs = expand_table_specs(included_table_specs)
table_pairs = table_pairs_without_duplicates(table_pairs)
table_pairs_without_excluded(table_pairs, excluded_table_specs)
end

# Helper for #resolve
# Takes the specified table_specifications and expands it into an array of
# according table pairs.
# Returns the result
# Refer to #resolve for a full description of parameters and result.
def expand_table_specs(table_specs)
table_pairs = []
table_specs.each do |table_spec|

# If it is a regexp, convert it in an according string
table_spec = table_spec.inspect if table_spec.kind_of? Regexp

Expand All @@ -44,16 +57,32 @@ def resolve(table_specs)
table_pairs << {:left => table_spec.strip, :right => table_spec.strip}
end
end
remove_duplicate_table_pairs(table_pairs)
table_pairs
end
private :expand_table_specs

# Helper for #resolve
# Takes given table_pairs and removes all tables that are excluded.
# Returns the result.
# Both the given and the returned table_pairs is an array of hashes with
# * :+left+: name of the left table
# * :+right+: name of the corresponding right table
# +excluded_table_specs+ is the array of table specifications to be excluded.
def table_pairs_without_excluded(table_pairs, excluded_table_specs)
excluded_tables = expand_table_specs(excluded_table_specs).map do |table_pair|
table_pair[:left]
end
table_pairs.select {|table_pair| not excluded_tables.include? table_pair[:left]}
end
private :table_pairs_without_excluded

# Helper for #resolve
# Takes given table_pairs and removes all duplicates.
# Returns the result.
# Both the given and the returned table_pairs is an array of hashes with
# * :+left_table+: name of the left table
# * :+right_table+: name of the corresponding right table
def remove_duplicate_table_pairs(table_pairs)
# * :+left+: name of the left table
# * :+right+: name of the corresponding right table
def table_pairs_without_duplicates(table_pairs)
processed_left_tables = {}
resulting_table_pairs = []
table_pairs.each do |table_pair|
Expand All @@ -64,6 +93,6 @@ def remove_duplicate_table_pairs(table_pairs)
end
resulting_table_pairs
end
private :remove_duplicate_table_pairs
private :table_pairs_without_duplicates
end
end
53 changes: 33 additions & 20 deletions spec/configuration_spec.rb
Expand Up @@ -31,25 +31,39 @@
merge(config.options)
end

it "tables should return the list of added table specifications" do
it "included_table_specs should return the list of included table specifications" do
config = Configuration.new
config.add_tables('a', {:bla => :blub})
config.add_tables('a, b')
config.add_tables(/a/)
config.tables.should == ['a', 'a, b', /a/]
config.include_tables('a', {:bla => :blub})
config.include_tables('a, b')
config.include_tables(/a/)
config.included_table_specs.should == ['a', 'a, b', /a/]
end

it "included_table_specs should save the options if provided" do
config = Configuration.new
config.include_tables('a', {:bla => :blub})
config.options_for_table('a')[:bla].should == :blub
end

it "excluded_table_specs should return the list of excluded table specifications" do
config = Configuration.new
config.exclude_tables('a')
config.exclude_tables('a, b')
config.exclude_tables(/a/)
config.excluded_table_specs.should == ['a', 'a, b', /a/]
end

it "options_for_table should return the general options if there are no matching table specific options" do
config = Configuration.new
config.add_tables(/a/, {:bla => :blub})
config.include_tables(/a/, {:bla => :blub})
config.options_for_table('b').should == \
Syncers::TwoWaySyncer.default_options.clone.
merge(config.options)
end

it "options_for_table should return table specific options mixed in with default options" do
config = Configuration.new
config.add_tables(/a/, {:bla => :blub})
config.include_tables(/a/, {:bla => :blub})
config.options_for_table('a').should == \
Syncers::TwoWaySyncer.default_options.clone.
merge(config.options).
Expand All @@ -58,10 +72,10 @@

it "options_for_table should return last added version of added options for matching table spec" do
config = Configuration.new
config.add_tables(/a/, {:bla => :blub})
config.add_tables('a', {:bla => :blok})
config.add_tables(/x/, {:bla => :bar})
config.add_tables('y', {:bla => :foo})
config.include_tables(/a/, {:bla => :blub})
config.include_tables('a', {:bla => :blok})
config.include_tables(/x/, {:bla => :bar})
config.include_tables('y', {:bla => :foo})
config.options_for_table('a').should == \
Syncers::TwoWaySyncer.default_options.clone.
merge(config.options).
Expand All @@ -70,44 +84,43 @@

it "options_for_table should match against table pair specs" do
config = Configuration.new
config.add_tables('a, b', {:bla => :blub})
config.add_table_options('a, b', {:bla => :blub})
config.options_for_table('a')[:bla].should == :blub
end

it "options_for_table should match against regular expression specs" do
config = Configuration.new
config.add_tables(/a/, {:bla => :blub})
config.add_table_options(/a/, {:bla => :blub})
config.options_for_table('a')[:bla].should == :blub
end

it "options_for_table should match against pure table name specs" do
config = Configuration.new
config.add_tables('a', {:bla => :blub})
config.add_table_options('a', {:bla => :blub})
config.options_for_table('a')[:bla].should == :blub
end

it "add_options_for_table should not create table_spec duplicates" do
it "add_table_options should not create table_spec duplicates" do
config = Configuration.new
config.add_tables(/a/, {:bla => :blub})
config.add_tables(/a/, {:foo => :bar})
config.add_table_options(/a/, {:bla => :blub})
config.add_table_options(/a/, {:foo => :bar})
config.options_for_table('a').should == \
Syncers::TwoWaySyncer.default_options.clone.
merge(config.options).
merge(:bla => :blub, :foo => :bar)
end

it "add_options_for_table should include default syncer options" do
it "add_table_options should include default syncer options" do
config = Configuration.new
config.options = {:syncer => :one_way}

# overwrite one syncer option
config.add_tables(/a/, {:delete => true})
config.add_table_options(/a/, {:delete => true})

options = config.options_for_table('a')
Syncers::OneWaySyncer.default_options.each do |key, value|
options[key].should == value unless key == :delete
end
options[:delete].should == true
end

end
2 changes: 1 addition & 1 deletion spec/proxied_table_scan_spec.rb
Expand Up @@ -41,7 +41,7 @@
old_table_specific_options = config.tables_with_options
begin
config.options = {:proxy_block_size => 2}
config.add_tables 'scanner_records', {:proxy_block_size => 3}
config.include_tables 'scanner_records', {:proxy_block_size => 3}
ProxiedTableScan.new(Session.new(config), 'scanner_records').block_size \
.should == 3
ensure
Expand Down
8 changes: 4 additions & 4 deletions spec/session_spec.rb
Expand Up @@ -72,17 +72,17 @@

it "initialize should assign manual primary keys to the proxy connections" do
config = deep_copy(standard_config)
config.tables_with_options.clear
config.add_tables "table_with_manual_key, extender_without_key", :primary_key_names => ['id']
config.included_table_specs.clear
config.include_tables "table_with_manual_key, extender_without_key", :primary_key_names => ['id']
session = Session.new config
session.left.manual_primary_keys.should == {'table_with_manual_key'=>['id']}
session.right.manual_primary_keys.should == {'extender_without_key'=>['id']}
end

it "manual_primary_keys should return the correct primary keys" do
config = deep_copy(standard_config)
config.tables_with_options.clear
config.add_tables "table_with_manual_key, extender_without_key", :primary_key_names => ['id']
config.included_table_specs.clear
config.include_tables "table_with_manual_key, extender_without_key", :primary_key_names => ['id']
session = Session.new config
session.manual_primary_keys(:left).should == {'table_with_manual_key'=>['id']}
session.manual_primary_keys(:right).should == {'extender_without_key'=>['id']}
Expand Down
9 changes: 9 additions & 0 deletions spec/table_spec_resolver_spec.rb
Expand Up @@ -57,4 +57,13 @@
{:left => 'scanner_records', :right => 'scanner_records'}
]
end

it "resolve should not return tables that are excluded" do
@resolver.resolve(
[/SCANNER_RECORDS|scanner_text_key/],
[/scanner_text/]
).should == [
{:left => 'scanner_records', :right => 'scanner_records'},
]
end
end
2 changes: 1 addition & 1 deletion spec/table_sync_spec.rb
Expand Up @@ -24,7 +24,7 @@
old_table_specific_options = config.tables_with_options
begin
config.options = {:syncer => :bla}
config.add_tables 'scanner_records', {:syncer => :blub}
config.include_tables 'scanner_records', {:syncer => :blub}
TableSync.new(Session.new(config), 'scanner_records').sync_options[:syncer] \
.should == :blub
ensure
Expand Down

0 comments on commit 9387d6a

Please sign in to comment.