grosser-ar_merge 0.1.1
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/CHANGELOG +1 -0
 - data/README.markdown +52 -0
 - data/Rakefile +34 -0
 - data/ar_merge.gemspec +34 -0
 - data/init.rb +1 -0
 - data/lib/ar_merge.rb +55 -0
 - data/spec/ar_merge_spec.rb +98 -0
 - data/spec/setup_test_model.rb +37 -0
 - data/spec/spec_helper.rb +39 -0
 - metadata +77 -0
 
    
        data/CHANGELOG
    ADDED
    
    | 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            v 0.1.1 -- added activerecord dependency
         
     | 
    
        data/README.markdown
    ADDED
    
    | 
         @@ -0,0 +1,52 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            PROBLEM
         
     | 
| 
      
 2 
     | 
    
         
            +
            =======
         
     | 
| 
      
 3 
     | 
    
         
            +
             - Merging 2 records is often needed
         
     | 
| 
      
 4 
     | 
    
         
            +
             - Merging has many hidden & problematic aspects
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            SOLUTION
         
     | 
| 
      
 8 
     | 
    
         
            +
            ==========
         
     | 
| 
      
 9 
     | 
    
         
            +
            ActiveRecord extension that introduces a simple merging API.
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
             - Specify associations/attributes you want to merge
         
     | 
| 
      
 12 
     | 
    
         
            +
             - Protects from self-merges
         
     | 
| 
      
 13 
     | 
    
         
            +
             - Keeps counters valid
         
     | 
| 
      
 14 
     | 
    
         
            +
             - Removes merged record
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            INSTALL
         
     | 
| 
      
 18 
     | 
    
         
            +
            =======
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            Rails plugin
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                script/plugin install git://github.com/grosser/ar_merge.git
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            OR Gem
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                sudo gem install grosser-ar_merge
         
     | 
| 
      
 27 
     | 
    
         
            +
                #require ar_merge after activerecord
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
            USAGE
         
     | 
| 
      
 31 
     | 
    
         
            +
            =====
         
     | 
| 
      
 32 
     | 
    
         
            +
            Merge from outside the model:
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                user.merge!(other,:attributes=>user.attributes.keys,:associations=>%w[movies friends])`
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
            Merge from inside the model
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                User < ActiveRecord::Base
         
     | 
| 
      
 39 
     | 
    
         
            +
                  def merge!(other)
         
     | 
| 
      
 40 
     | 
    
         
            +
                    super(other,:attributes=>%w[email website])
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
            Merge duplicates
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                #merge all new users, that have the same email
         
     | 
| 
      
 47 
     | 
    
         
            +
                User.merge_duplicates!(User.find_all_by_status('new')) , :compare=>:email)
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
            AUTHOR
         
     | 
| 
      
 50 
     | 
    
         
            +
            ======
         
     | 
| 
      
 51 
     | 
    
         
            +
              Michael Grosser  
         
     | 
| 
      
 52 
     | 
    
         
            +
              grosser dot michael ät gmail dot com  
         
     | 
    
        data/Rakefile
    ADDED
    
    | 
         @@ -0,0 +1,34 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'rubygems'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'spec'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            desc 'Default: run spec.'
         
     | 
| 
      
 5 
     | 
    
         
            +
            task :default => :spec
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            desc "Run all specs in spec directory"
         
     | 
| 
      
 8 
     | 
    
         
            +
            task :spec do |t|
         
     | 
| 
      
 9 
     | 
    
         
            +
              options = "--colour --format progress --loadby --reverse"
         
     | 
| 
      
 10 
     | 
    
         
            +
              files = FileList['spec/**/*_spec.rb']
         
     | 
| 
      
 11 
     | 
    
         
            +
              system("spec #{options} #{files}")
         
     | 
| 
      
 12 
     | 
    
         
            +
            end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            require 'rubygems'
         
     | 
| 
      
 15 
     | 
    
         
            +
            require 'rake'
         
     | 
| 
      
 16 
     | 
    
         
            +
            require 'echoe'
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            Echoe.new('ar_merge', '0.1.1') do |p|
         
     | 
| 
      
 19 
     | 
    
         
            +
              p.description    = "Simply and securely merge AciveRecord`s."
         
     | 
| 
      
 20 
     | 
    
         
            +
              p.url            = "http://github.com/grosser/ar_merge"
         
     | 
| 
      
 21 
     | 
    
         
            +
              p.author         = "Michael Grosser"
         
     | 
| 
      
 22 
     | 
    
         
            +
              p.email          = "grosser.michael@gmail.com"
         
     | 
| 
      
 23 
     | 
    
         
            +
              p.ignore_pattern = ["tmp/*", "script/*"]
         
     | 
| 
      
 24 
     | 
    
         
            +
              p.dependencies   = %w[activerecord]
         
     | 
| 
      
 25 
     | 
    
         
            +
              p.development_dependencies = []
         
     | 
| 
      
 26 
     | 
    
         
            +
            end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
            Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
            task :update_gemspec do
         
     | 
| 
      
 31 
     | 
    
         
            +
              puts "updating..."
         
     | 
| 
      
 32 
     | 
    
         
            +
              `rake manifest`
         
     | 
| 
      
 33 
     | 
    
         
            +
              `rake build_gemspec`
         
     | 
| 
      
 34 
     | 
    
         
            +
            end
         
     | 
    
        data/ar_merge.gemspec
    ADDED
    
    | 
         @@ -0,0 +1,34 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # -*- encoding: utf-8 -*-
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            Gem::Specification.new do |s|
         
     | 
| 
      
 4 
     | 
    
         
            +
              s.name = %q{ar_merge}
         
     | 
| 
      
 5 
     | 
    
         
            +
              s.version = "0.1.1"
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
         
     | 
| 
      
 8 
     | 
    
         
            +
              s.authors = ["Michael Grosser"]
         
     | 
| 
      
 9 
     | 
    
         
            +
              s.date = %q{2008-12-23}
         
     | 
| 
      
 10 
     | 
    
         
            +
              s.description = %q{Simply and securely merge AciveRecord`s.}
         
     | 
| 
      
 11 
     | 
    
         
            +
              s.email = %q{grosser.michael@gmail.com}
         
     | 
| 
      
 12 
     | 
    
         
            +
              s.extra_rdoc_files = ["CHANGELOG", "lib/ar_merge.rb", "README.markdown"]
         
     | 
| 
      
 13 
     | 
    
         
            +
              s.files = ["Manifest", "CHANGELOG", "lib/ar_merge.rb", "spec/setup_test_model.rb", "spec/spec_helper.rb", "spec/ar_merge_spec.rb", "init.rb", "Rakefile", "README.markdown", "ar_merge.gemspec"]
         
     | 
| 
      
 14 
     | 
    
         
            +
              s.has_rdoc = true
         
     | 
| 
      
 15 
     | 
    
         
            +
              s.homepage = %q{http://github.com/grosser/ar_merge}
         
     | 
| 
      
 16 
     | 
    
         
            +
              s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Ar_merge", "--main", "README.markdown"]
         
     | 
| 
      
 17 
     | 
    
         
            +
              s.require_paths = ["lib"]
         
     | 
| 
      
 18 
     | 
    
         
            +
              s.rubyforge_project = %q{ar_merge}
         
     | 
| 
      
 19 
     | 
    
         
            +
              s.rubygems_version = %q{1.3.1}
         
     | 
| 
      
 20 
     | 
    
         
            +
              s.summary = %q{Simply and securely merge AciveRecord`s.}
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              if s.respond_to? :specification_version then
         
     | 
| 
      
 23 
     | 
    
         
            +
                current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
         
     | 
| 
      
 24 
     | 
    
         
            +
                s.specification_version = 2
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
         
     | 
| 
      
 27 
     | 
    
         
            +
                  s.add_runtime_dependency(%q<activerecord>, [">= 0"])
         
     | 
| 
      
 28 
     | 
    
         
            +
                else
         
     | 
| 
      
 29 
     | 
    
         
            +
                  s.add_dependency(%q<activerecord>, [">= 0"])
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
              else
         
     | 
| 
      
 32 
     | 
    
         
            +
                s.add_dependency(%q<activerecord>, [">= 0"])
         
     | 
| 
      
 33 
     | 
    
         
            +
              end
         
     | 
| 
      
 34 
     | 
    
         
            +
            end
         
     | 
    
        data/init.rb
    ADDED
    
    | 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'ar_merge'
         
     | 
    
        data/lib/ar_merge.rb
    ADDED
    
    | 
         @@ -0,0 +1,55 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module AR_Merge
         
     | 
| 
      
 2 
     | 
    
         
            +
              def self.included(base) #:nodoc:
         
     | 
| 
      
 3 
     | 
    
         
            +
                base.extend ClassMethods
         
     | 
| 
      
 4 
     | 
    
         
            +
                base.send(:include,InstanceMethods)
         
     | 
| 
      
 5 
     | 
    
         
            +
              end
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              module InstanceMethods
         
     | 
| 
      
 8 
     | 
    
         
            +
                def merge!(other,options={})
         
     | 
| 
      
 9 
     | 
    
         
            +
                  raise "cannot merge wit a new record" if other.new_record?
         
     | 
| 
      
 10 
     | 
    
         
            +
                  raise "cannot merge with myself" if other == self
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  #merge associations
         
     | 
| 
      
 13 
     | 
    
         
            +
                  (options[:associations]||[]).each do |association_name|
         
     | 
| 
      
 14 
     | 
    
         
            +
                    other.send(association_name).each do |object|
         
     | 
| 
      
 15 
     | 
    
         
            +
                      send(association_name).concat object
         
     | 
| 
      
 16 
     | 
    
         
            +
                    end
         
     | 
| 
      
 17 
     | 
    
         
            +
                    
         
     | 
| 
      
 18 
     | 
    
         
            +
                    #update counters, this is very basic/hacky/not secure for customized counters...
         
     | 
| 
      
 19 
     | 
    
         
            +
                    counter = "#{association_name}_count"
         
     | 
| 
      
 20 
     | 
    
         
            +
                    if other.respond_to?(counter)
         
     | 
| 
      
 21 
     | 
    
         
            +
                      other.send(counter).times{self.class.increment_counter(counter, id)}
         
     | 
| 
      
 22 
     | 
    
         
            +
                    end
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  #merge attributes
         
     | 
| 
      
 26 
     | 
    
         
            +
                  (options[:attributes]||[]).each do |attr|
         
     | 
| 
      
 27 
     | 
    
         
            +
                    send("#{attr}=",other.send(attr)) if send(attr).blank?
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  #cleanup
         
     | 
| 
      
 31 
     | 
    
         
            +
                  other.reload.destroy
         
     | 
| 
      
 32 
     | 
    
         
            +
                  save!
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
              end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
              module ClassMethods
         
     | 
| 
      
 37 
     | 
    
         
            +
                def merge_duplicates!(records,options)
         
     | 
| 
      
 38 
     | 
    
         
            +
                  records.each do |record|
         
     | 
| 
      
 39 
     | 
    
         
            +
                    next if record.nil?
         
     | 
| 
      
 40 
     | 
    
         
            +
                    records.each do |other|
         
     | 
| 
      
 41 
     | 
    
         
            +
                      next if other.nil?
         
     | 
| 
      
 42 
     | 
    
         
            +
                      next if other == record
         
     | 
| 
      
 43 
     | 
    
         
            +
                      is_comparable = other.send(options[:compare]) == record.send(options[:compare])
         
     | 
| 
      
 44 
     | 
    
         
            +
                      next unless is_comparable
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                      #merge and remove the other
         
     | 
| 
      
 47 
     | 
    
         
            +
                      records[records.index(other)]=nil
         
     | 
| 
      
 48 
     | 
    
         
            +
                      record.merge!(other)
         
     | 
| 
      
 49 
     | 
    
         
            +
                    end
         
     | 
| 
      
 50 
     | 
    
         
            +
                  end.reject(&:nil?)
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
              end
         
     | 
| 
      
 53 
     | 
    
         
            +
            end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
            ActiveRecord::Base.send(:include,AR_Merge)
         
     | 
| 
         @@ -0,0 +1,98 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.expand_path("spec_helper", File.dirname(__FILE__))
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe "AR_Merge" do
         
     | 
| 
      
 4 
     | 
    
         
            +
              describe :merge! do
         
     | 
| 
      
 5 
     | 
    
         
            +
                before do
         
     | 
| 
      
 6 
     | 
    
         
            +
                  @user = User.create!(:name=>'x')
         
     | 
| 
      
 7 
     | 
    
         
            +
                  @u2 = User.create!(:name=>'y')
         
     | 
| 
      
 8 
     | 
    
         
            +
                end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                describe :removal do
         
     | 
| 
      
 11 
     | 
    
         
            +
                  it "removes the merged user" do
         
     | 
| 
      
 12 
     | 
    
         
            +
                    @user.merge!(@u2)
         
     | 
| 
      
 13 
     | 
    
         
            +
                    lambda{@u2.reload}.should raise_error(ActiveRecord::RecordNotFound)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  it "saves the merging user" do
         
     | 
| 
      
 17 
     | 
    
         
            +
                    @user.expects(:save!)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    @user.merge!(@u2)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  end
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                describe :attributes do
         
     | 
| 
      
 23 
     | 
    
         
            +
                  it "merges and overtakes attributes" do
         
     | 
| 
      
 24 
     | 
    
         
            +
                    @user.name = ''
         
     | 
| 
      
 25 
     | 
    
         
            +
                    @user.merge!(@u2,:attributes=>['name'])
         
     | 
| 
      
 26 
     | 
    
         
            +
                    @user.name.should == 'y'
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  it "does not overtake attributes when current is not blank" do
         
     | 
| 
      
 30 
     | 
    
         
            +
                    @user.merge!(@u2,:attributes=>['name'])
         
     | 
| 
      
 31 
     | 
    
         
            +
                    @user.name.should == 'x'
         
     | 
| 
      
 32 
     | 
    
         
            +
                  end
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                describe :associations do
         
     | 
| 
      
 36 
     | 
    
         
            +
                  before do
         
     | 
| 
      
 37 
     | 
    
         
            +
                    Movie.delete_all
         
     | 
| 
      
 38 
     | 
    
         
            +
                    @user.movies << Movie.new
         
     | 
| 
      
 39 
     | 
    
         
            +
                    @user.save!
         
     | 
| 
      
 40 
     | 
    
         
            +
                    @user.movies.size.should == Movie.count
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  it "overtakes asociated objects" do
         
     | 
| 
      
 44 
     | 
    
         
            +
                    @u2.merge!(@user,:associations=>[:movies])
         
     | 
| 
      
 45 
     | 
    
         
            +
                    @u2.reload.should have(Movie.count).movies
         
     | 
| 
      
 46 
     | 
    
         
            +
                  end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                  it "does not create new objects" do
         
     | 
| 
      
 49 
     | 
    
         
            +
                    lambda{@u2.merge!(@user,:associations=>['movies'])}.should_not change(Movie,:count)
         
     | 
| 
      
 50 
     | 
    
         
            +
                  end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                  it "keeps counters in sync" do
         
     | 
| 
      
 53 
     | 
    
         
            +
                    user, merged_user = CountingUser.create!, CountingUser.create!
         
     | 
| 
      
 54 
     | 
    
         
            +
                    user.movies_count.should == 0
         
     | 
| 
      
 55 
     | 
    
         
            +
                    merged_user.movies << Movie.new
         
     | 
| 
      
 56 
     | 
    
         
            +
                    merged_user.save!
         
     | 
| 
      
 57 
     | 
    
         
            +
                    merged_user.reload.movies_count.should == 1
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                    user.merge!(merged_user,:associations=>[:movies])
         
     | 
| 
      
 60 
     | 
    
         
            +
                    user.reload.movies_count.should == 1
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
                end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                it "does no merge with new" do
         
     | 
| 
      
 65 
     | 
    
         
            +
                  lambda{@user.merge!(User.new)}.should raise_error
         
     | 
| 
      
 66 
     | 
    
         
            +
                end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                it "does no merge with self" do
         
     | 
| 
      
 69 
     | 
    
         
            +
                  lambda{@user.merge!(User.find(@user.id))}.should raise_error
         
     | 
| 
      
 70 
     | 
    
         
            +
                end
         
     | 
| 
      
 71 
     | 
    
         
            +
              end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
              describe :merge_duplicates! do
         
     | 
| 
      
 74 
     | 
    
         
            +
                before do
         
     | 
| 
      
 75 
     | 
    
         
            +
                  @u1 = User.create!(:name=>'a')
         
     | 
| 
      
 76 
     | 
    
         
            +
                  @u2 = User.create!(:name=>'b')
         
     | 
| 
      
 77 
     | 
    
         
            +
                  @u3 = User.create!(:name=>'a')
         
     | 
| 
      
 78 
     | 
    
         
            +
                  @u4 = User.create!(:name=>'a')
         
     | 
| 
      
 79 
     | 
    
         
            +
                  @users=[@u1,@u2,@u3]
         
     | 
| 
      
 80 
     | 
    
         
            +
                end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                it "merges all records that have the same attributes" do
         
     | 
| 
      
 83 
     | 
    
         
            +
                  User.merge_duplicates!(@users,:compare=>:name).should == [@u1,@u2]
         
     | 
| 
      
 84 
     | 
    
         
            +
                end
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                it "destroys the duplicates" do
         
     | 
| 
      
 87 
     | 
    
         
            +
                  User.merge_duplicates!(@users,:compare=>:name)
         
     | 
| 
      
 88 
     | 
    
         
            +
                  lambda{@u3.reload}.should raise_error(ActiveRecord::RecordNotFound)
         
     | 
| 
      
 89 
     | 
    
         
            +
                end
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                it "merges the first with each of its duplicates" do
         
     | 
| 
      
 92 
     | 
    
         
            +
                  @users = [@u2,@u3,@u1,@u4]
         
     | 
| 
      
 93 
     | 
    
         
            +
                  @u3.expects(:merge!).with(@u1)
         
     | 
| 
      
 94 
     | 
    
         
            +
                  @u3.expects(:merge!).with(@u4)
         
     | 
| 
      
 95 
     | 
    
         
            +
                  User.merge_duplicates!(@users,:compare=>:name).should == [@u2,@u3]
         
     | 
| 
      
 96 
     | 
    
         
            +
                end
         
     | 
| 
      
 97 
     | 
    
         
            +
              end
         
     | 
| 
      
 98 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,37 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'rubygems'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'active_record'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'active_record/fixtures'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            #create model table
         
     | 
| 
      
 6 
     | 
    
         
            +
            ActiveRecord::Schema.define(:version => 1) do
         
     | 
| 
      
 7 
     | 
    
         
            +
              create_table :users do |t|
         
     | 
| 
      
 8 
     | 
    
         
            +
                t.string :name
         
     | 
| 
      
 9 
     | 
    
         
            +
                t.timestamps
         
     | 
| 
      
 10 
     | 
    
         
            +
              end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              create_table :counting_users do |t|
         
     | 
| 
      
 13 
     | 
    
         
            +
                t.integer :movies_count, :default=>0, :null=>false
         
     | 
| 
      
 14 
     | 
    
         
            +
                t.string :name
         
     | 
| 
      
 15 
     | 
    
         
            +
                t.timestamps
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              create_table :movies do |t|
         
     | 
| 
      
 19 
     | 
    
         
            +
                t.integer :user_id
         
     | 
| 
      
 20 
     | 
    
         
            +
                t.integer :counting_user_id
         
     | 
| 
      
 21 
     | 
    
         
            +
                t.timestamps
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
            end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
            #create model
         
     | 
| 
      
 26 
     | 
    
         
            +
            class User < ActiveRecord::Base
         
     | 
| 
      
 27 
     | 
    
         
            +
              has_many :movies
         
     | 
| 
      
 28 
     | 
    
         
            +
            end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
            class CountingUser < ActiveRecord::Base
         
     | 
| 
      
 31 
     | 
    
         
            +
              has_many :movies
         
     | 
| 
      
 32 
     | 
    
         
            +
            end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
            class Movie < ActiveRecord::Base
         
     | 
| 
      
 35 
     | 
    
         
            +
              belongs_to :user
         
     | 
| 
      
 36 
     | 
    
         
            +
              belongs_to :counting_user, :counter_cache => true
         
     | 
| 
      
 37 
     | 
    
         
            +
            end
         
     | 
    
        data/spec/spec_helper.rb
    ADDED
    
    | 
         @@ -0,0 +1,39 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # ---- requirements
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'rubygems'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'spec'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'mocha'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'activerecord'
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            $LOAD_PATH << File.expand_path("../lib", File.dirname(__FILE__))
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            # ---- rspec
         
     | 
| 
      
 11 
     | 
    
         
            +
            Spec::Runner.configure do |config|
         
     | 
| 
      
 12 
     | 
    
         
            +
              config.mock_with :mocha
         
     | 
| 
      
 13 
     | 
    
         
            +
            end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            # ---- load database
         
     | 
| 
      
 17 
     | 
    
         
            +
            RAILS_ENV = "test"
         
     | 
| 
      
 18 
     | 
    
         
            +
            ActiveRecord::Base.configurations = {"test" => {
         
     | 
| 
      
 19 
     | 
    
         
            +
              :adapter => "sqlite3",
         
     | 
| 
      
 20 
     | 
    
         
            +
              :database => ":memory:",
         
     | 
| 
      
 21 
     | 
    
         
            +
            }.with_indifferent_access}
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
            ActiveRecord::Base.logger = Logger.new(File.directory?("log") ? "log/#{RAILS_ENV}.log" : "/dev/null")
         
     | 
| 
      
 24 
     | 
    
         
            +
            ActiveRecord::Base.establish_connection(:test)
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
            # ---- setup environment/plugin
         
     | 
| 
      
 28 
     | 
    
         
            +
            require 'ar_merge'
         
     | 
| 
      
 29 
     | 
    
         
            +
            require File.expand_path("../init", File.dirname(__FILE__))
         
     | 
| 
      
 30 
     | 
    
         
            +
            load File.expand_path("setup_test_model.rb", File.dirname(__FILE__))
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
            # ---- fixtures
         
     | 
| 
      
 34 
     | 
    
         
            +
            Spec::Example::ExampleGroupMethods.module_eval do
         
     | 
| 
      
 35 
     | 
    
         
            +
              def fixtures(*tables)
         
     | 
| 
      
 36 
     | 
    
         
            +
                dir = File.expand_path("fixtures", File.dirname(__FILE__))
         
     | 
| 
      
 37 
     | 
    
         
            +
                tables.each{|table| Fixtures.create_fixtures(dir, table.to_s) }
         
     | 
| 
      
 38 
     | 
    
         
            +
              end
         
     | 
| 
      
 39 
     | 
    
         
            +
            end
         
     | 
    
        metadata
    ADDED
    
    | 
         @@ -0,0 +1,77 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            --- !ruby/object:Gem::Specification 
         
     | 
| 
      
 2 
     | 
    
         
            +
            name: grosser-ar_merge
         
     | 
| 
      
 3 
     | 
    
         
            +
            version: !ruby/object:Gem::Version 
         
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.1.1
         
     | 
| 
      
 5 
     | 
    
         
            +
            platform: ruby
         
     | 
| 
      
 6 
     | 
    
         
            +
            authors: 
         
     | 
| 
      
 7 
     | 
    
         
            +
            - Michael Grosser
         
     | 
| 
      
 8 
     | 
    
         
            +
            autorequire: 
         
     | 
| 
      
 9 
     | 
    
         
            +
            bindir: bin
         
     | 
| 
      
 10 
     | 
    
         
            +
            cert_chain: []
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            date: 2008-12-23 00:00:00 -08:00
         
     | 
| 
      
 13 
     | 
    
         
            +
            default_executable: 
         
     | 
| 
      
 14 
     | 
    
         
            +
            dependencies: 
         
     | 
| 
      
 15 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency 
         
     | 
| 
      
 16 
     | 
    
         
            +
              name: activerecord
         
     | 
| 
      
 17 
     | 
    
         
            +
              version_requirement: 
         
     | 
| 
      
 18 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement 
         
     | 
| 
      
 19 
     | 
    
         
            +
                requirements: 
         
     | 
| 
      
 20 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 21 
     | 
    
         
            +
                  - !ruby/object:Gem::Version 
         
     | 
| 
      
 22 
     | 
    
         
            +
                    version: "0"
         
     | 
| 
      
 23 
     | 
    
         
            +
                version: 
         
     | 
| 
      
 24 
     | 
    
         
            +
            description: Simply and securely merge AciveRecord`s.
         
     | 
| 
      
 25 
     | 
    
         
            +
            email: grosser.michael@gmail.com
         
     | 
| 
      
 26 
     | 
    
         
            +
            executables: []
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
            extensions: []
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
            extra_rdoc_files: 
         
     | 
| 
      
 31 
     | 
    
         
            +
            - CHANGELOG
         
     | 
| 
      
 32 
     | 
    
         
            +
            - lib/ar_merge.rb
         
     | 
| 
      
 33 
     | 
    
         
            +
            - README.markdown
         
     | 
| 
      
 34 
     | 
    
         
            +
            files: 
         
     | 
| 
      
 35 
     | 
    
         
            +
            - Manifest
         
     | 
| 
      
 36 
     | 
    
         
            +
            - CHANGELOG
         
     | 
| 
      
 37 
     | 
    
         
            +
            - lib/ar_merge.rb
         
     | 
| 
      
 38 
     | 
    
         
            +
            - spec/setup_test_model.rb
         
     | 
| 
      
 39 
     | 
    
         
            +
            - spec/spec_helper.rb
         
     | 
| 
      
 40 
     | 
    
         
            +
            - spec/ar_merge_spec.rb
         
     | 
| 
      
 41 
     | 
    
         
            +
            - init.rb
         
     | 
| 
      
 42 
     | 
    
         
            +
            - Rakefile
         
     | 
| 
      
 43 
     | 
    
         
            +
            - README.markdown
         
     | 
| 
      
 44 
     | 
    
         
            +
            - ar_merge.gemspec
         
     | 
| 
      
 45 
     | 
    
         
            +
            has_rdoc: true
         
     | 
| 
      
 46 
     | 
    
         
            +
            homepage: http://github.com/grosser/ar_merge
         
     | 
| 
      
 47 
     | 
    
         
            +
            post_install_message: 
         
     | 
| 
      
 48 
     | 
    
         
            +
            rdoc_options: 
         
     | 
| 
      
 49 
     | 
    
         
            +
            - --line-numbers
         
     | 
| 
      
 50 
     | 
    
         
            +
            - --inline-source
         
     | 
| 
      
 51 
     | 
    
         
            +
            - --title
         
     | 
| 
      
 52 
     | 
    
         
            +
            - Ar_merge
         
     | 
| 
      
 53 
     | 
    
         
            +
            - --main
         
     | 
| 
      
 54 
     | 
    
         
            +
            - README.markdown
         
     | 
| 
      
 55 
     | 
    
         
            +
            require_paths: 
         
     | 
| 
      
 56 
     | 
    
         
            +
            - lib
         
     | 
| 
      
 57 
     | 
    
         
            +
            required_ruby_version: !ruby/object:Gem::Requirement 
         
     | 
| 
      
 58 
     | 
    
         
            +
              requirements: 
         
     | 
| 
      
 59 
     | 
    
         
            +
              - - ">="
         
     | 
| 
      
 60 
     | 
    
         
            +
                - !ruby/object:Gem::Version 
         
     | 
| 
      
 61 
     | 
    
         
            +
                  version: "0"
         
     | 
| 
      
 62 
     | 
    
         
            +
              version: 
         
     | 
| 
      
 63 
     | 
    
         
            +
            required_rubygems_version: !ruby/object:Gem::Requirement 
         
     | 
| 
      
 64 
     | 
    
         
            +
              requirements: 
         
     | 
| 
      
 65 
     | 
    
         
            +
              - - ">="
         
     | 
| 
      
 66 
     | 
    
         
            +
                - !ruby/object:Gem::Version 
         
     | 
| 
      
 67 
     | 
    
         
            +
                  version: "1.2"
         
     | 
| 
      
 68 
     | 
    
         
            +
              version: 
         
     | 
| 
      
 69 
     | 
    
         
            +
            requirements: []
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
            rubyforge_project: ar_merge
         
     | 
| 
      
 72 
     | 
    
         
            +
            rubygems_version: 1.2.0
         
     | 
| 
      
 73 
     | 
    
         
            +
            signing_key: 
         
     | 
| 
      
 74 
     | 
    
         
            +
            specification_version: 2
         
     | 
| 
      
 75 
     | 
    
         
            +
            summary: Simply and securely merge AciveRecord`s.
         
     | 
| 
      
 76 
     | 
    
         
            +
            test_files: []
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     |