rubyrep 1.1.2 → 1.2.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.
- data/.gemtest +0 -0
 - data/History.txt +9 -0
 - data/bin/rubyrep +0 -0
 - data/config/hoe.rb +15 -17
 - data/lib/rubyrep.rb +1 -1
 - data/lib/rubyrep/connection_extenders/connection_extenders.rb +14 -1
 - data/lib/rubyrep/connection_extenders/jdbc_extender.rb +4 -19
 - data/lib/rubyrep/proxy_connection.rb +5 -0
 - data/lib/rubyrep/replication_difference.rb +4 -0
 - data/lib/rubyrep/replication_extenders/postgresql_replication.rb +14 -1
 - data/lib/rubyrep/replication_run.rb +37 -11
 - data/lib/rubyrep/replication_runner.rb +7 -1
 - data/lib/rubyrep/table_scan_helper.rb +9 -1
 - data/lib/rubyrep/version.rb +2 -2
 - data/sims/performance/performance.rake +3 -2
 - data/spec/connection_extenders_registration_spec.rb +5 -1
 - data/spec/proxy_connection_spec.rb +14 -2
 - data/spec/replication_run_spec.rb +62 -0
 - data/spec/table_scan_helper_spec.rb +7 -0
 - data/tasks/database.rake +1 -1
 - data/tasks/rspec.rake +3 -7
 - metadata +29 -16
 
    
        data/.gemtest
    ADDED
    
    | 
         
            File without changes
         
     | 
    
        data/History.txt
    CHANGED
    
    | 
         @@ -1,3 +1,12 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            == 1.2.0 2011-03-07
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            * Feature: compatibility with Rails 3
         
     | 
| 
      
 4 
     | 
    
         
            +
            * Feature: do not replicate record updates that didn't change any fields (props to daudo)
         
     | 
| 
      
 5 
     | 
    
         
            +
            * Bug fix: reducing deadlock problems (props to gtanzillo)
         
     | 
| 
      
 6 
     | 
    
         
            +
            * Bug fix: adding missing schema prefix in PostgreSQL triggers
         
     | 
| 
      
 7 
     | 
    
         
            +
            * Bug fix: scans / syncs fail due to incorrect handling of case sensitivity of string primary key columns
         
     | 
| 
      
 8 
     | 
    
         
            +
            * Bug fix: reducing risk of foreign key conflicts during replication (props for root cause analysis to TonyB)
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
       1 
10 
     | 
    
         
             
            == 1.1.2 2009-05-10
         
     | 
| 
       2 
11 
     | 
    
         | 
| 
       3 
12 
     | 
    
         
             
            * Bug fix: escape primary keys in replication triggers
         
     | 
    
        data/bin/rubyrep
    CHANGED
    
    | 
         
            File without changes
         
     | 
    
        data/config/hoe.rb
    CHANGED
    
    | 
         @@ -45,25 +45,23 @@ end 
     | 
|
| 
       45 
45 
     | 
    
         | 
| 
       46 
46 
     | 
    
         
             
            # Generate all the Rake tasks
         
     | 
| 
       47 
47 
     | 
    
         
             
            # Run 'rake -T' to see list of generated tasks (from gem root directory)
         
     | 
| 
       48 
     | 
    
         
            -
            hoe = Hoe. 
     | 
| 
       49 
     | 
    
         
            -
               
     | 
| 
       50 
     | 
    
         
            -
               
     | 
| 
       51 
     | 
    
         
            -
               
     | 
| 
       52 
     | 
    
         
            -
               
     | 
| 
       53 
     | 
    
         
            -
               
     | 
| 
       54 
     | 
    
         
            -
               
     | 
| 
       55 
     | 
    
         
            -
               
     | 
| 
       56 
     | 
    
         
            -
               
     | 
| 
       57 
     | 
    
         
            -
             
     | 
| 
      
 48 
     | 
    
         
            +
            hoe = Hoe.spec(GEM_NAME) do
         
     | 
| 
      
 49 
     | 
    
         
            +
              self.version = VERS
         
     | 
| 
      
 50 
     | 
    
         
            +
              developer AUTHOR, EMAIL
         
     | 
| 
      
 51 
     | 
    
         
            +
              description = DESCRIPTION
         
     | 
| 
      
 52 
     | 
    
         
            +
              summary = DESCRIPTION
         
     | 
| 
      
 53 
     | 
    
         
            +
              url = HOMEPATH
         
     | 
| 
      
 54 
     | 
    
         
            +
              rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
         
     | 
| 
      
 55 
     | 
    
         
            +
              test_globs = ["test/**/test_*.rb"]
         
     | 
| 
      
 56 
     | 
    
         
            +
              clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store']  #An array of file patterns to delete on clean.
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
       58 
58 
     | 
    
         
             
              # == Optional
         
     | 
| 
       59 
     | 
    
         
            -
               
     | 
| 
       60 
     | 
    
         
            -
              # 
     | 
| 
       61 
     | 
    
         
            -
               
     | 
| 
       62 
     | 
    
         
            -
             
     | 
| 
       63 
     | 
    
         
            -
                ['activerecord' , '>= 2.3.5']
         
     | 
| 
       64 
     | 
    
         
            -
              ]
         
     | 
| 
      
 59 
     | 
    
         
            +
              changes = paragraphs_of("History.txt", 0..1).join("\\n\\n")
         
     | 
| 
      
 60 
     | 
    
         
            +
              #extra_deps = []     # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
         
     | 
| 
      
 61 
     | 
    
         
            +
              extra_deps << ['activesupport', '>= 3.0.5']
         
     | 
| 
      
 62 
     | 
    
         
            +
              extra_deps << ['activerecord' , '>= 3.0.5']
         
     | 
| 
       65 
63 
     | 
    
         | 
| 
       66 
     | 
    
         
            -
              # 
     | 
| 
      
 64 
     | 
    
         
            +
              #spec_extras = {}    # A hash of extra values to set in the gemspec.
         
     | 
| 
       67 
65 
     | 
    
         | 
| 
       68 
66 
     | 
    
         
             
            end
         
     | 
| 
       69 
67 
     | 
    
         | 
    
        data/lib/rubyrep.rb
    CHANGED
    
    
| 
         @@ -1,3 +1,8 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class ActiveRecord::ConnectionAdapters::AbstractAdapter
         
     | 
| 
      
 2 
     | 
    
         
            +
              # The current log subscriber
         
     | 
| 
      
 3 
     | 
    
         
            +
              attr_accessor :log_subscriber
         
     | 
| 
      
 4 
     | 
    
         
            +
            end
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
       1 
6 
     | 
    
         
             
            class ActiveRecord::ConnectionAdapters::Column
         
     | 
| 
       2 
7 
     | 
    
         
             
              # Bug in ActiveRecord parsing of PostgreSQL timestamps with microseconds:
         
     | 
| 
       3 
8 
     | 
    
         
             
              # Certain values are incorrectly rounded, thus ending up with timestamps
         
     | 
| 
         @@ -107,9 +112,17 @@ module RR 
     | 
|
| 
       107 
112 
     | 
    
         
             
                    if config[:logger].respond_to?(:debug)
         
     | 
| 
       108 
113 
     | 
    
         
             
                      logger = config[:logger]
         
     | 
| 
       109 
114 
     | 
    
         
             
                    else
         
     | 
| 
       110 
     | 
    
         
            -
                      logger =  
     | 
| 
      
 115 
     | 
    
         
            +
                      logger = ActiveSupport::BufferedLogger.new(config[:logger])
         
     | 
| 
       111 
116 
     | 
    
         
             
                    end
         
     | 
| 
       112 
117 
     | 
    
         
             
                    db_connection.instance_variable_set :@logger, logger
         
     | 
| 
      
 118 
     | 
    
         
            +
                    if ActiveSupport.const_defined?(:Notifications)
         
     | 
| 
      
 119 
     | 
    
         
            +
                      connection_object_id = db_connection.object_id
         
     | 
| 
      
 120 
     | 
    
         
            +
                      db_connection.log_subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |name, start, finish, id, payload|
         
     | 
| 
      
 121 
     | 
    
         
            +
                        if payload[:connection_id] == connection_object_id and logger.debug?
         
     | 
| 
      
 122 
     | 
    
         
            +
                          logger.debug payload[:sql].squeeze(" ")
         
     | 
| 
      
 123 
     | 
    
         
            +
                        end
         
     | 
| 
      
 124 
     | 
    
         
            +
                      end
         
     | 
| 
      
 125 
     | 
    
         
            +
                    end
         
     | 
| 
       113 
126 
     | 
    
         
             
                  end
         
     | 
| 
       114 
127 
     | 
    
         
             
                end
         
     | 
| 
       115 
128 
     | 
    
         | 
| 
         @@ -58,23 +58,8 @@ module RR 
     | 
|
| 
       58 
58 
     | 
    
         
             
              end
         
     | 
| 
       59 
59 
     | 
    
         
             
            end
         
     | 
| 
       60 
60 
     | 
    
         | 
| 
       61 
     | 
    
         
            -
             
     | 
| 
       62 
     | 
    
         
            -
             
     | 
| 
       63 
     | 
    
         
            -
             
     | 
| 
       64 
     | 
    
         
            -
             
     | 
| 
       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
         
     | 
| 
       73 
     | 
    
         
            -
                  end
         
     | 
| 
       74 
     | 
    
         
            -
             
     | 
| 
       75 
     | 
    
         
            -
                  def self.binary_to_string(value)
         
     | 
| 
       76 
     | 
    
         
            -
                    value
         
     | 
| 
       77 
     | 
    
         
            -
                  end
         
     | 
| 
       78 
     | 
    
         
            -
                end
         
     | 
| 
       79 
     | 
    
         
            -
              end
         
     | 
| 
      
 61 
     | 
    
         
            +
            require 'activerecord-jdbc-adapter'
         
     | 
| 
      
 62 
     | 
    
         
            +
            if ArJdbc.const_defined?(:PostgreSQL)
         
     | 
| 
      
 63 
     | 
    
         
            +
              ArJdbc::PostgreSQL::RecordNotUnique = ActiveRecord::RecordNotUnique unless ArJdbc::PostgreSQL.const_defined?(:RecordNotUnique)
         
     | 
| 
      
 64 
     | 
    
         
            +
              ArJdbc::PostgreSQL::InvalidForeignKey = ActiveRecord::InvalidForeignKey  unless ArJdbc::PostgreSQL.const_defined?(:InvalidForeignKey)
         
     | 
| 
       80 
65 
     | 
    
         
             
            end
         
     | 
| 
         @@ -19,6 +19,10 @@ module RR 
     | 
|
| 
       19 
19 
     | 
    
         
             
                # * :+no_diff+: changes in both databases constitute no difference
         
     | 
| 
       20 
20 
     | 
    
         
             
                attr_accessor :type
         
     | 
| 
       21 
21 
     | 
    
         | 
| 
      
 22 
     | 
    
         
            +
                # Is set to +true+ if first replication attempt failed but it should be tried again later
         
     | 
| 
      
 23 
     | 
    
         
            +
                attr_accessor :second_chance
         
     | 
| 
      
 24 
     | 
    
         
            +
                alias_method :second_chance?, :second_chance
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
       22 
26 
     | 
    
         
             
                # A hash with keys :+left+ and / or :+right+.
         
     | 
| 
       23 
27 
     | 
    
         
             
                # Hash values are LoggedChange instances.
         
     | 
| 
       24 
28 
     | 
    
         
             
                def changes
         
     | 
| 
         @@ -45,13 +45,25 @@ module RR 
     | 
|
| 
       45 
45 
     | 
    
         
             
                    activity_check = ""
         
     | 
| 
       46 
46 
     | 
    
         
             
                    if params[:exclude_rr_activity] then
         
     | 
| 
       47 
47 
     | 
    
         
             
                      activity_check = <<-end_sql
         
     | 
| 
       48 
     | 
    
         
            -
                        PERFORM ACTIVE FROM #{params[:activity_table]};
         
     | 
| 
      
 48 
     | 
    
         
            +
                        PERFORM ACTIVE FROM #{schema_prefix}#{params[:activity_table]};
         
     | 
| 
       49 
49 
     | 
    
         
             
                        IF FOUND THEN
         
     | 
| 
       50 
50 
     | 
    
         
             
                          RETURN NULL;
         
     | 
| 
       51 
51 
     | 
    
         
             
                        END IF;
         
     | 
| 
       52 
52 
     | 
    
         
             
                      end_sql
         
     | 
| 
       53 
53 
     | 
    
         
             
                    end
         
     | 
| 
       54 
54 
     | 
    
         | 
| 
      
 55 
     | 
    
         
            +
                    version_string = select_value("select version();")
         
     | 
| 
      
 56 
     | 
    
         
            +
                    version = version_string.gsub(/^\s*postgresql\s*([0-9.]+).*$/i, '\1')
         
     | 
| 
      
 57 
     | 
    
         
            +
                    if version >= '8.4'
         
     | 
| 
      
 58 
     | 
    
         
            +
                      modification_check = <<-end_sql
         
     | 
| 
      
 59 
     | 
    
         
            +
                        IF NEW IS NOT DISTINCT FROM OLD THEN
         
     | 
| 
      
 60 
     | 
    
         
            +
                          RETURN NULL;
         
     | 
| 
      
 61 
     | 
    
         
            +
                        END IF;
         
     | 
| 
      
 62 
     | 
    
         
            +
                      end_sql
         
     | 
| 
      
 63 
     | 
    
         
            +
                    else
         
     | 
| 
      
 64 
     | 
    
         
            +
                      modification_check = ""
         
     | 
| 
      
 65 
     | 
    
         
            +
                    end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
       55 
67 
     | 
    
         
             
                    # now create the trigger
         
     | 
| 
       56 
68 
     | 
    
         
             
                    execute(<<-end_sql)
         
     | 
| 
       57 
69 
     | 
    
         
             
                      CREATE OR REPLACE FUNCTION "#{params[:trigger_name]}"() RETURNS TRIGGER AS $change_trigger$
         
     | 
| 
         @@ -61,6 +73,7 @@ module RR 
     | 
|
| 
       61 
73 
     | 
    
         
             
                            INSERT INTO #{schema_prefix}#{params[:log_table]}(change_table, change_key, change_type, change_time)
         
     | 
| 
       62 
74 
     | 
    
         
             
                              SELECT '#{params[:table]}', #{key_clause('OLD', params)}, 'D', now();
         
     | 
| 
       63 
75 
     | 
    
         
             
                          ELSIF (TG_OP = 'UPDATE') THEN
         
     | 
| 
      
 76 
     | 
    
         
            +
                            #{modification_check}
         
     | 
| 
       64 
77 
     | 
    
         
             
                            INSERT INTO  #{schema_prefix}#{params[:log_table]}(change_table, change_key, change_new_key, change_type, change_time)
         
     | 
| 
       65 
78 
     | 
    
         
             
                              SELECT '#{params[:table]}', #{key_clause('OLD', params)}, #{key_clause('NEW', params)}, 'U', now();
         
     | 
| 
       66 
79 
     | 
    
         
             
                          ELSIF (TG_OP = 'INSERT') THEN
         
     | 
| 
         @@ -11,6 +11,11 @@ module RR 
     | 
|
| 
       11 
11 
     | 
    
         
             
                # The current TaskSweeper
         
     | 
| 
       12 
12 
     | 
    
         
             
                attr_accessor :sweeper
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
      
 14 
     | 
    
         
            +
                # An array of ReplicationDifference which originally failed replication but should be tried one more time
         
     | 
| 
      
 15 
     | 
    
         
            +
                def second_chancers
         
     | 
| 
      
 16 
     | 
    
         
            +
                  @second_chancers ||= []
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
       14 
19 
     | 
    
         
             
                # Returns the current ReplicationHelper; creates it if necessary
         
     | 
| 
       15 
20 
     | 
    
         
             
                def helper
         
     | 
| 
       16 
21 
     | 
    
         
             
                  @helper ||= ReplicationHelper.new(self)
         
     | 
| 
         @@ -39,6 +44,20 @@ module RR 
     | 
|
| 
       39 
44 
     | 
    
         
             
                  end
         
     | 
| 
       40 
45 
     | 
    
         
             
                end
         
     | 
| 
       41 
46 
     | 
    
         | 
| 
      
 47 
     | 
    
         
            +
                # Returns the next available ReplicationDifference.
         
     | 
| 
      
 48 
     | 
    
         
            +
                # (Either new unprocessed differences or if not available, the first available 'second chancer'.)
         
     | 
| 
      
 49 
     | 
    
         
            +
                #
         
     | 
| 
      
 50 
     | 
    
         
            +
                def load_difference
         
     | 
| 
      
 51 
     | 
    
         
            +
                  @loaders ||= LoggedChangeLoaders.new(session)
         
     | 
| 
      
 52 
     | 
    
         
            +
                  @loaders.update # ensure the cache of change log records is up-to-date
         
     | 
| 
      
 53 
     | 
    
         
            +
                  diff = ReplicationDifference.new @loaders
         
     | 
| 
      
 54 
     | 
    
         
            +
                  diff.load
         
     | 
| 
      
 55 
     | 
    
         
            +
                  unless diff.loaded? or second_chancers.empty?
         
     | 
| 
      
 56 
     | 
    
         
            +
                    diff = second_chancers.shift
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
                  diff
         
     | 
| 
      
 59 
     | 
    
         
            +
                end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
       42 
61 
     | 
    
         
             
                # Executes the replication run.
         
     | 
| 
       43 
62 
     | 
    
         
             
                def run
         
     | 
| 
       44 
63 
     | 
    
         
             
                  return unless [:left, :right].any? do |database|
         
     | 
| 
         @@ -57,29 +76,36 @@ module RR 
     | 
|
| 
       57 
76 
     | 
    
         
             
                  # Check for this and if timed out, return (silently).
         
     | 
| 
       58 
77 
     | 
    
         
             
                  return if sweeper.terminated?
         
     | 
| 
       59 
78 
     | 
    
         | 
| 
       60 
     | 
    
         
            -
                  loaders = LoggedChangeLoaders.new(session)
         
     | 
| 
       61 
     | 
    
         
            -
             
     | 
| 
       62 
79 
     | 
    
         
             
                  success = false
         
     | 
| 
       63 
80 
     | 
    
         
             
                  begin
         
     | 
| 
       64 
81 
     | 
    
         
             
                    replicator # ensure that replicator is created and has chance to validate settings
         
     | 
| 
       65 
82 
     | 
    
         | 
| 
       66 
83 
     | 
    
         
             
                    loop do
         
     | 
| 
       67 
84 
     | 
    
         
             
                      begin
         
     | 
| 
       68 
     | 
    
         
            -
                         
     | 
| 
       69 
     | 
    
         
            -
                        diff = ReplicationDifference.new loaders
         
     | 
| 
       70 
     | 
    
         
            -
                        diff.load
         
     | 
| 
      
 85 
     | 
    
         
            +
                        diff = load_difference
         
     | 
| 
       71 
86 
     | 
    
         
             
                        break unless diff.loaded?
         
     | 
| 
       72 
87 
     | 
    
         
             
                        break if sweeper.terminated?
         
     | 
| 
       73 
88 
     | 
    
         
             
                        if diff.type != :no_diff and not event_filtered?(diff)
         
     | 
| 
       74 
89 
     | 
    
         
             
                          replicator.replicate_difference diff
         
     | 
| 
       75 
90 
     | 
    
         
             
                        end
         
     | 
| 
       76 
91 
     | 
    
         
             
                      rescue Exception => e
         
     | 
| 
       77 
     | 
    
         
            -
                         
     | 
| 
       78 
     | 
    
         
            -
                           
     | 
| 
       79 
     | 
    
         
            -
             
     | 
| 
       80 
     | 
    
         
            -
             
     | 
| 
       81 
     | 
    
         
            -
                          #  
     | 
| 
       82 
     | 
    
         
            -
                           
     | 
| 
      
 92 
     | 
    
         
            +
                        if e.message =~ /violates foreign key constraint|foreign key constraint fails/i and !diff.second_chance?
         
     | 
| 
      
 93 
     | 
    
         
            +
                          # Note:
         
     | 
| 
      
 94 
     | 
    
         
            +
                          # Identifying the foreign key constraint violation via regular expression is
         
     | 
| 
      
 95 
     | 
    
         
            +
                          # database dependent and *dirty*.
         
     | 
| 
      
 96 
     | 
    
         
            +
                          # It would be better to use the ActiveRecord #translate_exception mechanism.
         
     | 
| 
      
 97 
     | 
    
         
            +
                          # However as per version 3.0.5 this doesn't work yet properly.
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                          diff.second_chance = true
         
     | 
| 
      
 100 
     | 
    
         
            +
                          second_chancers << diff
         
     | 
| 
      
 101 
     | 
    
         
            +
                        else
         
     | 
| 
      
 102 
     | 
    
         
            +
                          begin
         
     | 
| 
      
 103 
     | 
    
         
            +
                            helper.log_replication_outcome diff, e.message,
         
     | 
| 
      
 104 
     | 
    
         
            +
                              e.class.to_s + "\n" + e.backtrace.join("\n")
         
     | 
| 
      
 105 
     | 
    
         
            +
                          rescue Exception => _
         
     | 
| 
      
 106 
     | 
    
         
            +
                            # if logging to database itself fails, re-raise the original exception
         
     | 
| 
      
 107 
     | 
    
         
            +
                            raise e
         
     | 
| 
      
 108 
     | 
    
         
            +
                          end
         
     | 
| 
       83 
109 
     | 
    
         
             
                        end
         
     | 
| 
       84 
110 
     | 
    
         
             
                      end
         
     | 
| 
       85 
111 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -2,6 +2,12 @@ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib' 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            require 'optparse'
         
     | 
| 
       4 
4 
     | 
    
         
             
            require 'thread'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'monitor'
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            class Monitor
         
     | 
| 
      
 8 
     | 
    
         
            +
              alias lock mon_enter
         
     | 
| 
      
 9 
     | 
    
         
            +
              alias unlock mon_exit
         
     | 
| 
      
 10 
     | 
    
         
            +
            end
         
     | 
| 
       5 
11 
     | 
    
         | 
| 
       6 
12 
     | 
    
         
             
            module RR
         
     | 
| 
       7 
13 
     | 
    
         
             
              # This class implements the functionality of the 'replicate' command.
         
     | 
| 
         @@ -94,7 +100,7 @@ EOS 
     | 
|
| 
       94 
100 
     | 
    
         
             
                # Initializes the waiter thread used for replication pauses and processing
         
     | 
| 
       95 
101 
     | 
    
         
             
                # the process TERM signal.
         
     | 
| 
       96 
102 
     | 
    
         
             
                def init_waiter
         
     | 
| 
       97 
     | 
    
         
            -
                  @termination_mutex =  
     | 
| 
      
 103 
     | 
    
         
            +
                  @termination_mutex = Monitor.new
         
     | 
| 
       98 
104 
     | 
    
         
             
                  @termination_mutex.lock
         
     | 
| 
       99 
105 
     | 
    
         
             
                  @waiter_thread ||= Thread.new {@termination_mutex.lock; self.termination_requested = true}
         
     | 
| 
       100 
106 
     | 
    
         
             
                  %w(TERM INT).each do |signal|
         
     | 
| 
         @@ -19,7 +19,15 @@ module RR 
     | 
|
| 
       19 
19 
     | 
    
         
             
                  return 1 unless left_row 
         
     | 
| 
       20 
20 
     | 
    
         
             
                  rank = 0
         
     | 
| 
       21 
21 
     | 
    
         
             
                  primary_key_names.any? do |key|
         
     | 
| 
       22 
     | 
    
         
            -
                     
     | 
| 
      
 22 
     | 
    
         
            +
                    if left_row[key].kind_of?(String)
         
     | 
| 
      
 23 
     | 
    
         
            +
                      # When databases order strings, then 'a' < 'A' while for Ruby 'A' < 'a'
         
     | 
| 
      
 24 
     | 
    
         
            +
                      # ==> Use a combination of case sensitive and case insensitive comparing to
         
     | 
| 
      
 25 
     | 
    
         
            +
                      #     reproduce the database behaviour.
         
     | 
| 
      
 26 
     | 
    
         
            +
                      rank = left_row[key].casecmp(right_row[key]) # deal with 'a' to 'B' comparisons
         
     | 
| 
      
 27 
     | 
    
         
            +
                      rank = -(left_row[key] <=> right_row[key]) if rank == 0 # deal with 'a' to 'A' comparisons
         
     | 
| 
      
 28 
     | 
    
         
            +
                    else
         
     | 
| 
      
 29 
     | 
    
         
            +
                      rank = left_row[key] <=> right_row[key]
         
     | 
| 
      
 30 
     | 
    
         
            +
                    end
         
     | 
| 
       23 
31 
     | 
    
         
             
                    rank != 0
         
     | 
| 
       24 
32 
     | 
    
         
             
                  end
         
     | 
| 
       25 
33 
     | 
    
         
             
                  rank
         
     | 
    
        data/lib/rubyrep/version.rb
    CHANGED
    
    
| 
         @@ -1,3 +1,4 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
             
     | 
| 
       1 
2 
     | 
    
         
             
            require 'rake'
         
     | 
| 
       2 
3 
     | 
    
         
             
            require 'benchmark'
         
     | 
| 
       3 
4 
     | 
    
         | 
| 
         @@ -136,7 +137,7 @@ def populate_rep_data 
     | 
|
| 
       136 
137 
     | 
    
         
             
                # Updating progress bar
         
     | 
| 
       137 
138 
     | 
    
         
             
                progress_bar.step
         
     | 
| 
       138 
139 
     | 
    
         | 
| 
       139 
     | 
    
         
            -
                database = [:left, :right] 
     | 
| 
      
 140 
     | 
    
         
            +
                database = [:left, :right][rand(2)]
         
     | 
| 
       140 
141 
     | 
    
         | 
| 
       141 
142 
     | 
    
         
             
                case rand(100)
         
     | 
| 
       142 
143 
     | 
    
         
             
                when 0...BIG_REP_INSERT
         
     | 
| 
         @@ -147,7 +148,7 @@ def populate_rep_data 
     | 
|
| 
       147 
148 
     | 
    
         
             
                  next_id += 1
         
     | 
| 
       148 
149 
     | 
    
         
             
                  session.send(database).insert_record 'big_rep', attributes
         
     | 
| 
       149 
150 
     | 
    
         
             
                when BIG_REP_INSERT...BIG_REP_UPDATE
         
     | 
| 
       150 
     | 
    
         
            -
                  id = all_ids[database]. 
     | 
| 
      
 151 
     | 
    
         
            +
                  id = all_ids[database][rand(all_ids[database].size)]
         
     | 
| 
       151 
152 
     | 
    
         
             
                  attributes = session.send(database).select_one("select * from big_rep where id = '#{id}'")
         
     | 
| 
       152 
153 
     | 
    
         
             
                  column = number_columns[rand(number_columns.size)]
         
     | 
| 
       153 
154 
     | 
    
         
             
                  attributes[column] = rand(1000)
         
     | 
| 
         @@ -10,9 +10,13 @@ describe ConnectionExtenders do 
     | 
|
| 
       10 
10 
     | 
    
         
             
              it "db_connect should install the already created logger" do
         
     | 
| 
       11 
11 
     | 
    
         
             
                configuration = deep_copy(Initializer.configuration)
         
     | 
| 
       12 
12 
     | 
    
         
             
                io = StringIO.new
         
     | 
| 
       13 
     | 
    
         
            -
                logger =  
     | 
| 
      
 13 
     | 
    
         
            +
                logger = ActiveSupport::BufferedLogger.new(io)
         
     | 
| 
       14 
14 
     | 
    
         
             
                configuration.left[:logger] = logger
         
     | 
| 
       15 
15 
     | 
    
         
             
                session = Session.new configuration
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                session.left.connection.instance_eval {@logger}.should == logger
         
     | 
| 
      
 18 
     | 
    
         
            +
                session.right.connection.instance_eval {@logger}.should_not == logger
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
       16 
20 
     | 
    
         
             
                session.left.select_one "select 'left_query'"
         
     | 
| 
       17 
21 
     | 
    
         
             
                session.right.select_one "select 'right_query'"
         
     | 
| 
       18 
22 
     | 
    
         | 
| 
         @@ -9,7 +9,7 @@ describe ProxyConnection do 
     | 
|
| 
       9 
9 
     | 
    
         
             
              end
         
     | 
| 
       10 
10 
     | 
    
         | 
| 
       11 
11 
     | 
    
         
             
              it "initialize should connect to the database" do
         
     | 
| 
       12 
     | 
    
         
            -
                 
     | 
| 
      
 12 
     | 
    
         
            +
                (!!@connection.connection.active?).should == true
         
     | 
| 
       13 
13 
     | 
    
         
             
              end
         
     | 
| 
       14 
14 
     | 
    
         | 
| 
       15 
15 
     | 
    
         
             
              it "initialize should store the configuratin" do
         
     | 
| 
         @@ -17,9 +17,21 @@ describe ProxyConnection do 
     | 
|
| 
       17 
17 
     | 
    
         
             
              end
         
     | 
| 
       18 
18 
     | 
    
         | 
| 
       19 
19 
     | 
    
         
             
              it "destroy should disconnect from the database" do
         
     | 
| 
      
 20 
     | 
    
         
            +
                if ActiveSupport.const_defined?(:Notifications)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  ConnectionExtenders::install_logger @connection.connection, :logger => StringIO.new
         
     | 
| 
      
 22 
     | 
    
         
            +
                  log_subscriber = @connection.connection.log_subscriber
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  ActiveSupport::Notifications.notifier.listeners_for("sql.active_record").should include(log_subscriber)
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
       20 
27 
     | 
    
         
             
                @connection.destroy
         
     | 
| 
       21 
28 
     | 
    
         | 
| 
       22 
     | 
    
         
            -
                 
     | 
| 
      
 29 
     | 
    
         
            +
                if ActiveSupport.const_defined?(:Notifications)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  ActiveSupport::Notifications.notifier.listeners_for("sql.active_record").should_not include(log_subscriber)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  @connection.connection.log_subscriber.should be_nil
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                (!!@connection.connection.active?).should == false
         
     | 
| 
       23 
35 
     | 
    
         
             
              end
         
     | 
| 
       24 
36 
     | 
    
         | 
| 
       25 
37 
     | 
    
         
             
              it "cursors should return the current cursor hash or an empty hash if nil" do
         
     | 
| 
         @@ -135,6 +135,68 @@ describe ReplicationRun do 
     | 
|
| 
       135 
135 
     | 
    
         
             
                end
         
     | 
| 
       136 
136 
     | 
    
         
             
              end
         
     | 
| 
       137 
137 
     | 
    
         | 
| 
      
 138 
     | 
    
         
            +
              it "run should replication records with foreign key constraints" do
         
     | 
| 
      
 139 
     | 
    
         
            +
                begin
         
     | 
| 
      
 140 
     | 
    
         
            +
                  config = deep_copy(standard_config)
         
     | 
| 
      
 141 
     | 
    
         
            +
                  config.options[:committer] = :never_commit
         
     | 
| 
      
 142 
     | 
    
         
            +
             
     | 
| 
      
 143 
     | 
    
         
            +
                  session = Session.new(config)
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
                  session.left.insert_record 'referencing_table', {
         
     | 
| 
      
 146 
     | 
    
         
            +
                    'id' => '5',
         
     | 
| 
      
 147 
     | 
    
         
            +
                  }
         
     | 
| 
      
 148 
     | 
    
         
            +
                  session.left.insert_record 'rr_pending_changes', {
         
     | 
| 
      
 149 
     | 
    
         
            +
                    'change_table' => 'referencing_table',
         
     | 
| 
      
 150 
     | 
    
         
            +
                    'change_key' => 'id|5',
         
     | 
| 
      
 151 
     | 
    
         
            +
                    'change_type' => 'I',
         
     | 
| 
      
 152 
     | 
    
         
            +
                    'change_time' => Time.now
         
     | 
| 
      
 153 
     | 
    
         
            +
                  }
         
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
      
 155 
     | 
    
         
            +
                  session.left.insert_record 'referenced_table2', {
         
     | 
| 
      
 156 
     | 
    
         
            +
                    'id' => '6',
         
     | 
| 
      
 157 
     | 
    
         
            +
                  }
         
     | 
| 
      
 158 
     | 
    
         
            +
                  session.left.insert_record 'rr_pending_changes', {
         
     | 
| 
      
 159 
     | 
    
         
            +
                    'change_table' => 'referenced_table2',
         
     | 
| 
      
 160 
     | 
    
         
            +
                    'change_key' => 'id|6',
         
     | 
| 
      
 161 
     | 
    
         
            +
                    'change_type' => 'I',
         
     | 
| 
      
 162 
     | 
    
         
            +
                    'change_time' => Time.now
         
     | 
| 
      
 163 
     | 
    
         
            +
                  }
         
     | 
| 
      
 164 
     | 
    
         
            +
             
     | 
| 
      
 165 
     | 
    
         
            +
                  session.left.update_record 'referencing_table', {
         
     | 
| 
      
 166 
     | 
    
         
            +
                    'id' => 5,
         
     | 
| 
      
 167 
     | 
    
         
            +
                    'third_fk' => '6'
         
     | 
| 
      
 168 
     | 
    
         
            +
                  }
         
     | 
| 
      
 169 
     | 
    
         
            +
                  session.left.insert_record 'rr_pending_changes', {
         
     | 
| 
      
 170 
     | 
    
         
            +
                    'change_table' => 'referencing_table',
         
     | 
| 
      
 171 
     | 
    
         
            +
                    'change_key' => 'id|5',
         
     | 
| 
      
 172 
     | 
    
         
            +
                    'change_new_key' => 'id|5',
         
     | 
| 
      
 173 
     | 
    
         
            +
                    'change_type' => 'U',
         
     | 
| 
      
 174 
     | 
    
         
            +
                    'change_time' => Time.now
         
     | 
| 
      
 175 
     | 
    
         
            +
                  }
         
     | 
| 
      
 176 
     | 
    
         
            +
             
     | 
| 
      
 177 
     | 
    
         
            +
                  run = ReplicationRun.new session, TaskSweeper.new(1)
         
     | 
| 
      
 178 
     | 
    
         
            +
                  run.run
         
     | 
| 
      
 179 
     | 
    
         
            +
             
     | 
| 
      
 180 
     | 
    
         
            +
                  session.right.select_record(:table => "referencing_table", :from => {'id' => 5}).should == {
         
     | 
| 
      
 181 
     | 
    
         
            +
                    'id' => 5,
         
     | 
| 
      
 182 
     | 
    
         
            +
                    'first_fk' => nil,
         
     | 
| 
      
 183 
     | 
    
         
            +
                    'second_fk' => nil,
         
     | 
| 
      
 184 
     | 
    
         
            +
                    'third_fk' => 6
         
     | 
| 
      
 185 
     | 
    
         
            +
                  }
         
     | 
| 
      
 186 
     | 
    
         
            +
                ensure
         
     | 
| 
      
 187 
     | 
    
         
            +
                  Committers::NeverCommitter.rollback_current_session
         
     | 
| 
      
 188 
     | 
    
         
            +
                  if session
         
     | 
| 
      
 189 
     | 
    
         
            +
                    session.left.execute "delete from referencing_table where id = 5"
         
     | 
| 
      
 190 
     | 
    
         
            +
                    session.left.execute "delete from referenced_table2 where id = 6"
         
     | 
| 
      
 191 
     | 
    
         
            +
             
     | 
| 
      
 192 
     | 
    
         
            +
                    session.right.execute "delete from referencing_table where id = 5"
         
     | 
| 
      
 193 
     | 
    
         
            +
                    session.right.execute "delete from referenced_table2 where id = 6"
         
     | 
| 
      
 194 
     | 
    
         
            +
                    
         
     | 
| 
      
 195 
     | 
    
         
            +
                    session.left.execute "delete from rr_pending_changes"
         
     | 
| 
      
 196 
     | 
    
         
            +
                  end
         
     | 
| 
      
 197 
     | 
    
         
            +
                end
         
     | 
| 
      
 198 
     | 
    
         
            +
              end
         
     | 
| 
      
 199 
     | 
    
         
            +
             
     | 
| 
       138 
200 
     | 
    
         
             
              it "run should not replicate filtered changes" do
         
     | 
| 
       139 
201 
     | 
    
         
             
                begin
         
     | 
| 
       140 
202 
     | 
    
         
             
                  config = deep_copy(standard_config)
         
     | 
| 
         @@ -13,6 +13,13 @@ describe TableScanHelper do 
     | 
|
| 
       13 
13 
     | 
    
         
             
                @scan.rank_rows({'first_id' => 1, 'second_id' => 1}, {'first_id' => 1, 'second_id' => 1}).should == 0
         
     | 
| 
       14 
14 
     | 
    
         
             
                @scan.rank_rows({'first_id' => 1, 'second_id' => 1}, {'first_id' => 1, 'second_id' => 2}).should == -1
         
     | 
| 
       15 
15 
     | 
    
         
             
                @scan.rank_rows({'first_id' => 2, 'second_id' => 1}, {'first_id' => 1, 'second_id' => 1}).should == 1
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                # should rank strings according to database logic ('a' < 'A')
         
     | 
| 
      
 18 
     | 
    
         
            +
                # instead of the Ruby logic (which is the other way round)
         
     | 
| 
      
 19 
     | 
    
         
            +
                @scan.rank_rows({'first_id' => 'a', 'second_id' => 1}, {'first_id' => 'B', 'second_id' => 1}).should == -1
         
     | 
| 
      
 20 
     | 
    
         
            +
                @scan.rank_rows({'first_id' => 'a', 'second_id' => 1}, {'first_id' => 'A', 'second_id' => 1}).should == -1
         
     | 
| 
      
 21 
     | 
    
         
            +
                @scan.rank_rows({'first_id' => 'a', 'second_id' => 1}, {'first_id' => 'a', 'second_id' => 1}).should == 0
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
       16 
23 
     | 
    
         
             
                lambda {@scan.rank_rows(nil,nil)}.should raise_error(RuntimeError, 'At least one of left_row and right_row must not be nil!')
         
     | 
| 
       17 
24 
     | 
    
         
             
                @scan.rank_rows(nil, {'first_id' => 1, 'second_id' => 1}).should == 1
         
     | 
| 
       18 
25 
     | 
    
         
             
                @scan.rank_rows({'first_id' => 1, 'second_id' => 1}, nil).should == -1
         
     | 
    
        data/tasks/database.rake
    CHANGED
    
    | 
         @@ -18,7 +18,7 @@ def create_database(config) 
     | 
|
| 
       18 
18 
     | 
    
         
             
                  @charset   = ENV['CHARSET']   || 'utf8'
         
     | 
| 
       19 
19 
     | 
    
         
             
                  @collation = ENV['COLLATION'] || 'utf8_general_ci'
         
     | 
| 
       20 
20 
     | 
    
         
             
                  begin
         
     | 
| 
       21 
     | 
    
         
            -
                    connection = RR::ConnectionExtenders.db_connect(config.merge({ 
     | 
| 
      
 21 
     | 
    
         
            +
                    connection = RR::ConnectionExtenders.db_connect(config.merge({:database => nil}))
         
     | 
| 
       22 
22 
     | 
    
         
             
                    connection.create_database(config[:database], {:charset => @charset, :collation => @collation})
         
     | 
| 
       23 
23 
     | 
    
         
             
                    RR::ConnectionExtenders.db_connect(config)
         
     | 
| 
       24 
24 
     | 
    
         
             
                  rescue
         
     | 
    
        data/tasks/rspec.rake
    CHANGED
    
    | 
         @@ -41,18 +41,14 @@ namespace :spec do 
     | 
|
| 
       41 
41 
     | 
    
         
             
              desc "Run the specs for all supported databases"
         
     | 
| 
       42 
42 
     | 
    
         
             
              task :all_dbs do
         
     | 
| 
       43 
43 
     | 
    
         
             
                [:postgres, :mysql].each do |test_db|
         
     | 
| 
       44 
     | 
    
         
            -
                  puts "Running specs for #{test_db 
     | 
| 
       45 
     | 
    
         
            -
                   
     | 
| 
       46 
     | 
    
         
            -
                  system "spec spec"
         
     | 
| 
      
 44 
     | 
    
         
            +
                  puts "Running specs for #{test_db}"
         
     | 
| 
      
 45 
     | 
    
         
            +
                  system "bash -c 'RR_TEST_DB=#{test_db} spec spec'"
         
     | 
| 
       47 
46 
     | 
    
         
             
                end
         
     | 
| 
       48 
47 
     | 
    
         
             
              end
         
     | 
| 
       49 
48 
     | 
    
         | 
| 
       50 
49 
     | 
    
         
             
              desc "Run the specs for all supported databases and ruby platforms" 
         
     | 
| 
       51 
50 
     | 
    
         
             
              task :all_rubies do
         
     | 
| 
       52 
     | 
    
         
            -
                 
     | 
| 
       53 
     | 
    
         
            -
                system "rake spec:all_dbs"
         
     | 
| 
       54 
     | 
    
         
            -
                puts "Running spec:all_dbs in jruby"
         
     | 
| 
       55 
     | 
    
         
            -
                system "export PATH=#{JRUBY_HOME}/bin:$PATH; rake spec:all_dbs"
         
     | 
| 
      
 51 
     | 
    
         
            +
                system %(rvm exec bash -c 'for db in postgres mysql; do echo "`rvm current` - $db:"; RR_TEST_DB=$db spec spec; done')
         
     | 
| 
       56 
52 
     | 
    
         
             
              end
         
     | 
| 
       57 
53 
     | 
    
         | 
| 
       58 
54 
     | 
    
         
             
              begin
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,12 +1,13 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification 
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: rubyrep
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version 
         
     | 
| 
       4 
     | 
    
         
            -
               
     | 
| 
      
 4 
     | 
    
         
            +
              hash: 31
         
     | 
| 
      
 5 
     | 
    
         
            +
              prerelease: 
         
     | 
| 
       5 
6 
     | 
    
         
             
              segments: 
         
     | 
| 
       6 
7 
     | 
    
         
             
              - 1
         
     | 
| 
       7 
     | 
    
         
            -
              - 1
         
     | 
| 
       8 
8 
     | 
    
         
             
              - 2
         
     | 
| 
       9 
     | 
    
         
            -
               
     | 
| 
      
 9 
     | 
    
         
            +
              - 0
         
     | 
| 
      
 10 
     | 
    
         
            +
              version: 1.2.0
         
     | 
| 
       10 
11 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       11 
12 
     | 
    
         
             
            authors: 
         
     | 
| 
       12 
13 
     | 
    
         
             
            - Arndt Lehmann
         
     | 
| 
         @@ -14,53 +15,60 @@ autorequire: 
     | 
|
| 
       14 
15 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       15 
16 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       16 
17 
     | 
    
         | 
| 
       17 
     | 
    
         
            -
            date:  
     | 
| 
      
 18 
     | 
    
         
            +
            date: 2011-03-07 00:00:00 +09:00
         
     | 
| 
       18 
19 
     | 
    
         
             
            default_executable: 
         
     | 
| 
       19 
20 
     | 
    
         
             
            dependencies: 
         
     | 
| 
       20 
21 
     | 
    
         
             
            - !ruby/object:Gem::Dependency 
         
     | 
| 
       21 
22 
     | 
    
         
             
              name: activesupport
         
     | 
| 
       22 
23 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       23 
24 
     | 
    
         
             
              requirement: &id001 !ruby/object:Gem::Requirement 
         
     | 
| 
      
 25 
     | 
    
         
            +
                none: false
         
     | 
| 
       24 
26 
     | 
    
         
             
                requirements: 
         
     | 
| 
       25 
27 
     | 
    
         
             
                - - ">="
         
     | 
| 
       26 
28 
     | 
    
         
             
                  - !ruby/object:Gem::Version 
         
     | 
| 
      
 29 
     | 
    
         
            +
                    hash: 13
         
     | 
| 
       27 
30 
     | 
    
         
             
                    segments: 
         
     | 
| 
       28 
     | 
    
         
            -
                    - 2
         
     | 
| 
       29 
31 
     | 
    
         
             
                    - 3
         
     | 
| 
      
 32 
     | 
    
         
            +
                    - 0
         
     | 
| 
       30 
33 
     | 
    
         
             
                    - 5
         
     | 
| 
       31 
     | 
    
         
            -
                    version:  
     | 
| 
      
 34 
     | 
    
         
            +
                    version: 3.0.5
         
     | 
| 
       32 
35 
     | 
    
         
             
              type: :runtime
         
     | 
| 
       33 
36 
     | 
    
         
             
              version_requirements: *id001
         
     | 
| 
       34 
37 
     | 
    
         
             
            - !ruby/object:Gem::Dependency 
         
     | 
| 
       35 
38 
     | 
    
         
             
              name: activerecord
         
     | 
| 
       36 
39 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       37 
40 
     | 
    
         
             
              requirement: &id002 !ruby/object:Gem::Requirement 
         
     | 
| 
      
 41 
     | 
    
         
            +
                none: false
         
     | 
| 
       38 
42 
     | 
    
         
             
                requirements: 
         
     | 
| 
       39 
43 
     | 
    
         
             
                - - ">="
         
     | 
| 
       40 
44 
     | 
    
         
             
                  - !ruby/object:Gem::Version 
         
     | 
| 
      
 45 
     | 
    
         
            +
                    hash: 13
         
     | 
| 
       41 
46 
     | 
    
         
             
                    segments: 
         
     | 
| 
       42 
     | 
    
         
            -
                    - 2
         
     | 
| 
       43 
47 
     | 
    
         
             
                    - 3
         
     | 
| 
      
 48 
     | 
    
         
            +
                    - 0
         
     | 
| 
       44 
49 
     | 
    
         
             
                    - 5
         
     | 
| 
       45 
     | 
    
         
            -
                    version:  
     | 
| 
      
 50 
     | 
    
         
            +
                    version: 3.0.5
         
     | 
| 
       46 
51 
     | 
    
         
             
              type: :runtime
         
     | 
| 
       47 
52 
     | 
    
         
             
              version_requirements: *id002
         
     | 
| 
       48 
53 
     | 
    
         
             
            - !ruby/object:Gem::Dependency 
         
     | 
| 
       49 
54 
     | 
    
         
             
              name: hoe
         
     | 
| 
       50 
55 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       51 
56 
     | 
    
         
             
              requirement: &id003 !ruby/object:Gem::Requirement 
         
     | 
| 
      
 57 
     | 
    
         
            +
                none: false
         
     | 
| 
       52 
58 
     | 
    
         
             
                requirements: 
         
     | 
| 
       53 
59 
     | 
    
         
             
                - - ">="
         
     | 
| 
       54 
60 
     | 
    
         
             
                  - !ruby/object:Gem::Version 
         
     | 
| 
      
 61 
     | 
    
         
            +
                    hash: 41
         
     | 
| 
       55 
62 
     | 
    
         
             
                    segments: 
         
     | 
| 
       56 
63 
     | 
    
         
             
                    - 2
         
     | 
| 
       57 
     | 
    
         
            -
                    -  
     | 
| 
       58 
     | 
    
         
            -
                    -  
     | 
| 
       59 
     | 
    
         
            -
                    version: 2. 
     | 
| 
      
 64 
     | 
    
         
            +
                    - 9
         
     | 
| 
      
 65 
     | 
    
         
            +
                    - 1
         
     | 
| 
      
 66 
     | 
    
         
            +
                    version: 2.9.1
         
     | 
| 
       60 
67 
     | 
    
         
             
              type: :development
         
     | 
| 
       61 
68 
     | 
    
         
             
              version_requirements: *id003
         
     | 
| 
       62 
     | 
    
         
            -
            description:  
     | 
| 
       63 
     | 
    
         
            -
            email:  
     | 
| 
      
 69 
     | 
    
         
            +
            description: ""
         
     | 
| 
      
 70 
     | 
    
         
            +
            email: 
         
     | 
| 
      
 71 
     | 
    
         
            +
            - mail@arndtlehman.com
         
     | 
| 
       64 
72 
     | 
    
         
             
            executables: 
         
     | 
| 
       65 
73 
     | 
    
         
             
            - rubyrep
         
     | 
| 
       66 
74 
     | 
    
         
             
            extensions: []
         
     | 
| 
         @@ -222,8 +230,9 @@ files: 
     | 
|
| 
       222 
230 
     | 
    
         
             
            - tasks/stats.rake
         
     | 
| 
       223 
231 
     | 
    
         
             
            - tasks/task_helper.rb
         
     | 
| 
       224 
232 
     | 
    
         
             
            - tasks/website.rake
         
     | 
| 
      
 233 
     | 
    
         
            +
            - .gemtest
         
     | 
| 
       225 
234 
     | 
    
         
             
            has_rdoc: true
         
     | 
| 
       226 
     | 
    
         
            -
            homepage:  
     | 
| 
      
 235 
     | 
    
         
            +
            homepage: 
         
     | 
| 
       227 
236 
     | 
    
         
             
            licenses: []
         
     | 
| 
       228 
237 
     | 
    
         | 
| 
       229 
238 
     | 
    
         
             
            post_install_message: 
         
     | 
| 
         @@ -233,25 +242,29 @@ rdoc_options: 
     | 
|
| 
       233 
242 
     | 
    
         
             
            require_paths: 
         
     | 
| 
       234 
243 
     | 
    
         
             
            - lib
         
     | 
| 
       235 
244 
     | 
    
         
             
            required_ruby_version: !ruby/object:Gem::Requirement 
         
     | 
| 
      
 245 
     | 
    
         
            +
              none: false
         
     | 
| 
       236 
246 
     | 
    
         
             
              requirements: 
         
     | 
| 
       237 
247 
     | 
    
         
             
              - - ">="
         
     | 
| 
       238 
248 
     | 
    
         
             
                - !ruby/object:Gem::Version 
         
     | 
| 
      
 249 
     | 
    
         
            +
                  hash: 3
         
     | 
| 
       239 
250 
     | 
    
         
             
                  segments: 
         
     | 
| 
       240 
251 
     | 
    
         
             
                  - 0
         
     | 
| 
       241 
252 
     | 
    
         
             
                  version: "0"
         
     | 
| 
       242 
253 
     | 
    
         
             
            required_rubygems_version: !ruby/object:Gem::Requirement 
         
     | 
| 
      
 254 
     | 
    
         
            +
              none: false
         
     | 
| 
       243 
255 
     | 
    
         
             
              requirements: 
         
     | 
| 
       244 
256 
     | 
    
         
             
              - - ">="
         
     | 
| 
       245 
257 
     | 
    
         
             
                - !ruby/object:Gem::Version 
         
     | 
| 
      
 258 
     | 
    
         
            +
                  hash: 3
         
     | 
| 
       246 
259 
     | 
    
         
             
                  segments: 
         
     | 
| 
       247 
260 
     | 
    
         
             
                  - 0
         
     | 
| 
       248 
261 
     | 
    
         
             
                  version: "0"
         
     | 
| 
       249 
262 
     | 
    
         
             
            requirements: []
         
     | 
| 
       250 
263 
     | 
    
         | 
| 
       251 
264 
     | 
    
         
             
            rubyforge_project: rubyrep
         
     | 
| 
       252 
     | 
    
         
            -
            rubygems_version: 1. 
     | 
| 
      
 265 
     | 
    
         
            +
            rubygems_version: 1.5.2
         
     | 
| 
       253 
266 
     | 
    
         
             
            signing_key: 
         
     | 
| 
       254 
267 
     | 
    
         
             
            specification_version: 3
         
     | 
| 
       255 
     | 
    
         
            -
            summary:  
     | 
| 
      
 268 
     | 
    
         
            +
            summary: ""
         
     | 
| 
       256 
269 
     | 
    
         
             
            test_files: []
         
     | 
| 
       257 
270 
     | 
    
         |