deep_cloneable 1.0.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/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Reinier de Lange
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,58 @@
1
+ = Deep_cloneable
2
+
3
+ This gem gives every ActiveRecord::Base object the possibility to do a deep clone. It is a rails3 upgrade of the deep_cloning plugin (http://github.com/openminds/deep_cloning).
4
+
5
+ == Requirements
6
+
7
+ * Activerecord, tested with Rails 3.
8
+
9
+ == Installation
10
+
11
+ * Install by adding the gem to your Gemfile
12
+
13
+ gem 'deep_cloneable'
14
+
15
+ * Rails 2:
16
+
17
+ gem install deep_cloneable
18
+
19
+ And include the gem in your apps config
20
+
21
+ config.gem 'deep_cloneable'
22
+
23
+ * Or install as a plugin if you must:
24
+
25
+ ./script/plugin install git://github.com/moiristo/deep_cloneable.git
26
+
27
+ == Example
28
+
29
+ === Cloning a model without an attribute
30
+ pirate.clone :except => :name
31
+
32
+ === Cloning a model without multiple attributes
33
+ pirate.clone :except => [:name, :nick_name]
34
+ === Cloning one single association
35
+ pirate.clone :include => :mateys
36
+
37
+ === Cloning multiple associations
38
+ pirate.clone :include => [:mateys, :treasures]
39
+
40
+ === Cloning really deep
41
+ pirate.clone :include => {:treasures => :gold_pieces}
42
+
43
+ === Cloning really deep with multiple associations
44
+ pirate.clone :include => [:mateys, {:treasures => :gold_pieces}]
45
+
46
+ == Note on Patches/Pull Requests
47
+
48
+ * Fork the project.
49
+ * Make your feature addition or bug fix.
50
+ * Add tests for it. This is important so I don't break it in a
51
+ future version unintentionally.
52
+ * Commit, do not mess with rakefile, version, or history.
53
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
54
+ * Send me a pull request. Bonus points for topic branches.
55
+
56
+ == Copyright
57
+
58
+ Copyright (c) 2010 Reinier de Lange. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,52 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "deep_cloneable"
8
+ gem.summary = %Q{This gem gives every ActiveRecord::Base object the possibility to do a deep clone.}
9
+ gem.description = %Q{Extends the functionality of ActiveRecord::Base#clone to perform a deep clone that includes user specified associations. }
10
+ gem.email = "r.j.delange@nedforce.nl"
11
+ gem.homepage = "http://github.com/moiristo/deep_cloneable"
12
+ gem.authors = ["Reinier de Lange"]
13
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
14
+ end
15
+ Jeweler::GemcutterTasks.new
16
+ rescue LoadError
17
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
18
+ end
19
+
20
+ require 'rake/testtask'
21
+ Rake::TestTask.new(:test) do |test|
22
+ test.libs << 'lib' << 'test'
23
+ test.pattern = 'test/**/test_*.rb'
24
+ test.verbose = true
25
+ end
26
+
27
+ begin
28
+ require 'rcov/rcovtask'
29
+ Rcov::RcovTask.new do |test|
30
+ test.libs << 'test'
31
+ test.pattern = 'test/**/test_*.rb'
32
+ test.verbose = true
33
+ end
34
+ rescue LoadError
35
+ task :rcov do
36
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
37
+ end
38
+ end
39
+
40
+ task :test => :check_dependencies
41
+
42
+ task :default => :test
43
+
44
+ require 'rake/rdoctask'
45
+ Rake::RDocTask.new do |rdoc|
46
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
47
+
48
+ rdoc.rdoc_dir = 'rdoc'
49
+ rdoc.title = "deep_cloneable #{version}"
50
+ rdoc.rdoc_files.include('README*')
51
+ rdoc.rdoc_files.include('lib/**/*.rb')
52
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,55 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{deep_cloneable}
8
+ s.version = "1.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Reinier de Lange"]
12
+ s.date = %q{2010-10-18}
13
+ s.description = %q{Extends the functionality of ActiveRecord::Base#clone to perform a deep clone that includes user specified associations. }
14
+ s.email = %q{r.j.delange@nedforce.nl}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "deep_cloneable.gemspec",
27
+ "init.rb",
28
+ "lib/deep_cloneable.rb",
29
+ "test/database.yml",
30
+ "test/schema.rb",
31
+ "test/test_deep_cloneable.rb",
32
+ "test/test_helper.rb"
33
+ ]
34
+ s.homepage = %q{http://github.com/moiristo/deep_cloneable}
35
+ s.rdoc_options = ["--charset=UTF-8"]
36
+ s.require_paths = ["lib"]
37
+ s.rubygems_version = %q{1.3.7}
38
+ s.summary = %q{This gem gives every ActiveRecord::Base object the possibility to do a deep clone.}
39
+ s.test_files = [
40
+ "test/schema.rb",
41
+ "test/test_deep_cloneable.rb",
42
+ "test/test_helper.rb"
43
+ ]
44
+
45
+ if s.respond_to? :specification_version then
46
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
47
+ s.specification_version = 3
48
+
49
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
50
+ else
51
+ end
52
+ else
53
+ end
54
+ end
55
+
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'deep_cloneable'
@@ -0,0 +1,57 @@
1
+ class ActiveRecord::Base
2
+ module DeepCloneable
3
+ # clones an ActiveRecord model.
4
+ # if passed the :include option, it will deep clone the given associations
5
+ # if passed the :except option, it won't clone the given attributes
6
+ #
7
+ # === Usage:
8
+ #
9
+ # ==== Cloning a model without an attribute
10
+ # pirate.clone :except => :name
11
+ #
12
+ # ==== Cloning a model without multiple attributes
13
+ # pirate.clone :except => [:name, :nick_name]
14
+ # ==== Cloning one single association
15
+ # pirate.clone :include => :mateys
16
+ #
17
+ # ==== Cloning multiple associations
18
+ # pirate.clone :include => [:mateys, :treasures]
19
+ #
20
+ # ==== Cloning really deep
21
+ # pirate.clone :include => {:treasures => :gold_pieces}
22
+ #
23
+ # ==== Cloning really deep with multiple associations
24
+ # pirate.clone :include => [:mateys, {:treasures => :gold_pieces}]
25
+ #
26
+ def clone(options = {})
27
+ kopy = super()
28
+
29
+ if options[:except]
30
+ Array(options[:except]).each do |attribute|
31
+ kopy.send(:write_attribute, attribute, attributes_from_column_definition[attribute.to_s])
32
+ end
33
+ end
34
+
35
+ if options[:include]
36
+ Array(options[:include]).each do |association, deep_associations|
37
+ if (association.kind_of? Hash)
38
+ deep_associations = association[association.keys.first]
39
+ association = association.keys.first
40
+ end
41
+ opts = deep_associations.blank? ? {} : {:include => deep_associations}
42
+ cloned_object = case self.class.reflect_on_association(association).macro
43
+ when :belongs_to, :has_one
44
+ self.send(association) && self.send(association).clone(opts)
45
+ when :has_many, :has_and_belongs_to_many
46
+ self.send(association).collect { |obj| obj.clone(opts) }
47
+ end
48
+ kopy.send("#{association}=", cloned_object)
49
+ end
50
+ end
51
+
52
+ return kopy
53
+ end
54
+ end
55
+
56
+ include DeepCloneable
57
+ end
data/test/database.yml ADDED
@@ -0,0 +1,6 @@
1
+ sqlite:
2
+ adapter: sqlite
3
+ database: ":memory:"
4
+ sqlite3:
5
+ adapter: sqlite3
6
+ database: ":memory:"
data/test/schema.rb ADDED
@@ -0,0 +1,26 @@
1
+ ActiveRecord::Schema.define(:version => 1) do
2
+ create_table :pirates, :force => true do |t|
3
+ t.column :name, :string
4
+ t.column :nick_name, :string, :default => 'no nickname'
5
+ t.column :age, :string
6
+ end
7
+
8
+ create_table :parrots, :force => true do |t|
9
+ t.column :name, :string
10
+ t.column :pirate_id, :integer
11
+ end
12
+
13
+ create_table :mateys, :force => true do |t|
14
+ t.column :name, :string
15
+ t.column :pirate_id, :integer
16
+ end
17
+
18
+ create_table :treasures, :force => true do |t|
19
+ t.column :found_at, :string
20
+ t.column :pirate_id, :integer
21
+ end
22
+
23
+ create_table :gold_pieces, :force => true do |t|
24
+ t.column :treasure_id, :integer
25
+ end
26
+ end
@@ -0,0 +1,71 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class TestDeepCloneable < Test::Unit::TestCase
4
+ load_schema
5
+
6
+ def setup
7
+ @jack = Pirate.create(:name => 'Jack Sparrow', :nick_name => 'Captain Jack', :age => 30)
8
+ @polly = Parrot.create(:name => 'Polly', :pirate => @jack)
9
+ @john = Matey.create(:name => 'John', :pirate => @jack)
10
+ @treasure = Treasure.create(:found_at => 'Isla del Muerte', :pirate => @jack)
11
+ @gold_piece = GoldPiece.create(:treasure => @treasure)
12
+ end
13
+
14
+ def test_single_clone_exception
15
+ clone = @jack.clone(:except => :name)
16
+ assert clone.save
17
+ assert_equal @jack.name, @jack.clone.name # Old behaviour
18
+ assert_nil clone.name
19
+ assert_equal @jack.nick_name, clone.nick_name
20
+ end
21
+
22
+ def test_multiple_clone_exception
23
+ clone = @jack.clone(:except => [:name, :nick_name])
24
+ assert clone.save
25
+ assert_nil clone.name
26
+ assert_equal 'no nickname', clone.nick_name
27
+ assert_equal @jack.age, clone.age
28
+ end
29
+
30
+ def test_single_include_association
31
+ clone = @jack.clone(:include => :mateys)
32
+ assert clone.save
33
+ assert_equal 1, clone.mateys.size
34
+ end
35
+
36
+ def test_multiple_include_association
37
+ clone = @jack.clone(:include => [:mateys, :treasures])
38
+ assert clone.save
39
+ assert_equal 1, clone.mateys.size
40
+ assert_equal 1, clone.treasures.size
41
+ end
42
+
43
+ def test_deep_include_association
44
+ clone = @jack.clone(:include => {:treasures => :gold_pieces})
45
+ assert clone.save
46
+ assert_equal 1, clone.treasures.size
47
+ assert_equal 1, clone.gold_pieces.size
48
+ end
49
+
50
+ def test_multiple_and_deep_include_association
51
+ clone = @jack.clone(:include => {:treasures => :gold_pieces, :mateys => {}})
52
+ assert clone.save
53
+ assert_equal 1, clone.treasures.size
54
+ assert_equal 1, clone.gold_pieces.size
55
+ assert_equal 1, clone.mateys.size
56
+ end
57
+
58
+ def test_multiple_and_deep_include_association_with_array
59
+ clone = @jack.clone(:include => [{:treasures => :gold_pieces}, :mateys])
60
+ assert clone.save
61
+ assert_equal 1, clone.treasures.size
62
+ assert_equal 1, clone.gold_pieces.size
63
+ assert_equal 1, clone.mateys.size
64
+ end
65
+
66
+ def test_with_belongs_to_relation
67
+ clone = @jack.clone(:include => :parrot)
68
+ assert clone.save
69
+ assert_not_equal clone.parrot, @jack.parrot
70
+ end
71
+ end
@@ -0,0 +1,51 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'pp'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+
8
+ Gem.activate 'activerecord'
9
+ require 'active_record'
10
+ require 'active_record/fixtures'
11
+ require File.dirname(__FILE__) + '/../init.rb'
12
+
13
+ class GoldPiece < ActiveRecord::Base; belongs_to :treasure end
14
+ class Matey < ActiveRecord::Base; belongs_to :pirate end
15
+ class Parrot < ActiveRecord::Base; belongs_to :pirate end
16
+
17
+ class Pirate < ActiveRecord::Base
18
+ has_many :mateys
19
+ has_many :treasures
20
+ has_many :gold_pieces, :through => :treasures
21
+ has_one :parrot
22
+ end
23
+
24
+ class Treasure < ActiveRecord::Base
25
+ belongs_to :pirate
26
+ has_many :gold_pieces
27
+ end
28
+
29
+ def load_schema
30
+ config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
31
+ ActiveRecord::Base.logger = ActiveSupport::BufferedLogger.new(File.dirname(__FILE__) + "/debug.log")
32
+ db_adapter = ENV['DB']
33
+ # no db passed, try one of these fine config-free DBs before bombing.
34
+ db_adapter ||= begin
35
+ require 'rubygems'
36
+ require 'sqlite'
37
+ 'sqlite'
38
+ rescue MissingSourceFile
39
+ begin
40
+ require 'sqlite3'
41
+ 'sqlite3'
42
+ rescue MissingSourceFile
43
+ end
44
+ end
45
+
46
+ if db_adapter.nil?
47
+ raise "No DB Adapter selected. Pass the DB= option to pick one, or install Sqlite or Sqlite3."
48
+ end
49
+ ActiveRecord::Base.establish_connection(config[db_adapter])
50
+ load(File.dirname(__FILE__) + "/schema.rb")
51
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: deep_cloneable
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Reinier de Lange
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-10-18 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: "Extends the functionality of ActiveRecord::Base#clone to perform a deep clone that includes user specified associations. "
23
+ email: r.j.delange@nedforce.nl
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files:
29
+ - LICENSE
30
+ - README.rdoc
31
+ files:
32
+ - .document
33
+ - .gitignore
34
+ - LICENSE
35
+ - README.rdoc
36
+ - Rakefile
37
+ - VERSION
38
+ - deep_cloneable.gemspec
39
+ - init.rb
40
+ - lib/deep_cloneable.rb
41
+ - test/database.yml
42
+ - test/schema.rb
43
+ - test/test_deep_cloneable.rb
44
+ - test/test_helper.rb
45
+ has_rdoc: true
46
+ homepage: http://github.com/moiristo/deep_cloneable
47
+ licenses: []
48
+
49
+ post_install_message:
50
+ rdoc_options:
51
+ - --charset=UTF-8
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ hash: 3
69
+ segments:
70
+ - 0
71
+ version: "0"
72
+ requirements: []
73
+
74
+ rubyforge_project:
75
+ rubygems_version: 1.3.7
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: This gem gives every ActiveRecord::Base object the possibility to do a deep clone.
79
+ test_files:
80
+ - test/schema.rb
81
+ - test/test_deep_cloneable.rb
82
+ - test/test_helper.rb