dm-deep_cloning 1.0.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/.travis.yml +7 -0
- data/Gemfile +78 -0
- data/LICENSE +13 -0
- data/README.md +43 -0
- data/Rakefile +32 -0
- data/VERSION +1 -0
- data/dm-deep_cloning.gemspec +87 -0
- data/lib/data_mapper/deep_cloning/utilities.rb +38 -0
- data/lib/dm-deep_cloning.rb +82 -0
- data/spec/dm-deep_cloning_spec.rb +112 -0
- data/spec/rcov.opts +6 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +57 -0
- data/tasks/changelog.rake +20 -0
- data/tasks/ci.rake +1 -0
- data/tasks/metrics.rake +36 -0
- data/tasks/spec.rake +22 -0
- data/tasks/yard.rake +9 -0
- data/tasks/yardstick.rake +19 -0
- metadata +253 -0
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# Copyright 2011 innoQ Deutschland GmbH
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
SOURCE = ENV.fetch('SOURCE', :git).to_sym
|
18
|
+
|
19
|
+
DM_VERSION = '~> 1.2.0'
|
20
|
+
|
21
|
+
DO_VERSION = '~> 0.10.6'
|
22
|
+
DM_DO_ADAPTERS = %w[ sqlite postgres mysql oracle sqlserver ]
|
23
|
+
|
24
|
+
def dm(module_name)
|
25
|
+
if SOURCE == :rubygems
|
26
|
+
gem "dm-module_name", DM_VERSION
|
27
|
+
elsif SOURCE == :path
|
28
|
+
gem "dm-#{module_name}", DM_VERSION, :path => Pathname(__FILE__).dirname.parent
|
29
|
+
else
|
30
|
+
gem "dm-#{module_name}", DM_VERSION, :git => "http://github.com/datamapper/dm-#{module_name}.git", :branch => ENV.fetch('BRANCH', 'master')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
source 'http://rubygems.org'
|
35
|
+
|
36
|
+
dm('core')
|
37
|
+
dm('transactions')
|
38
|
+
|
39
|
+
group :development, :test do
|
40
|
+
gem 'rake'
|
41
|
+
gem 'jeweler', '~> 1.4'
|
42
|
+
gem 'rspec', '~> 1.3'
|
43
|
+
|
44
|
+
group :datamapper do
|
45
|
+
|
46
|
+
adapters = ENV['ADAPTER'] || ENV['ADAPTERS']
|
47
|
+
adapters = adapters.to_s.tr(',', ' ').split.uniq - %w[ in_memory ]
|
48
|
+
|
49
|
+
if (do_adapters = DM_DO_ADAPTERS & adapters).any?
|
50
|
+
do_options = {}
|
51
|
+
do_options[:git] = "#{DATAMAPPER}/do#{REPO_POSTFIX}" if ENV['DO_GIT'] == 'true'
|
52
|
+
|
53
|
+
gem 'data_objects', DO_VERSION, do_options.dup
|
54
|
+
|
55
|
+
do_adapters.each do |adapter|
|
56
|
+
adapter = 'sqlite3' if adapter == 'sqlite'
|
57
|
+
gem "do_#{adapter}", DO_VERSION, do_options.dup
|
58
|
+
end
|
59
|
+
|
60
|
+
dm('do-adapter')
|
61
|
+
end
|
62
|
+
|
63
|
+
adapters.each do |adapter|
|
64
|
+
gem 'ruby-oci8', :platform => :ruby if adapter == 'oracle'
|
65
|
+
dm("#{adapter}-adapter")
|
66
|
+
end
|
67
|
+
|
68
|
+
plugins = ENV['PLUGINS'] || ENV['PLUGIN']
|
69
|
+
plugins = plugins.to_s.tr(',', ' ').split.push('migrations', 'timestamps').uniq
|
70
|
+
|
71
|
+
plugins.each do |plugin|
|
72
|
+
dm(plugin.gsub(/^dm-/, ""))
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
data/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright 2011 innoQ Deutschland GmbH
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# Datamapper deep cloning extention
|
2
|
+
|
3
|
+
This [DataMapper](http://github.com/datamapper) extension adds deep cloning functionality to your resource objects.
|
4
|
+
You can use it to recursively clone objects following spefic relations which need to be specified explicitly.
|
5
|
+
|
6
|
+
## Usage
|
7
|
+
|
8
|
+
Given the following models:
|
9
|
+
|
10
|
+
class Blog
|
11
|
+
has n, :posts
|
12
|
+
end
|
13
|
+
|
14
|
+
class Post
|
15
|
+
belongs_to :blog
|
16
|
+
end
|
17
|
+
|
18
|
+
Cloning a blog including all its posts would look like:
|
19
|
+
|
20
|
+
new_blog = blog.deep_clone(:posts)
|
21
|
+
|
22
|
+
This would initialize a new Blog object with all attrbutes taken from the original `blog`. `new_blog.posts` would
|
23
|
+
consist of (unsaved) clones of the original entrys from `blog.posts`.
|
24
|
+
|
25
|
+
If you don't want just to initialize the clones but to create them to, use `:create` as first parameter:
|
26
|
+
|
27
|
+
new_blog = blog.deep_clone(:create, :posts)
|
28
|
+
|
29
|
+
### Nested recursion
|
30
|
+
|
31
|
+
Given a further model `Comment`:
|
32
|
+
|
33
|
+
class Comment
|
34
|
+
belongs_to :post
|
35
|
+
end
|
36
|
+
|
37
|
+
Post.has n, :comments
|
38
|
+
|
39
|
+
You can also specify nested parameters like:
|
40
|
+
|
41
|
+
blog.deep_clone(:posts => :comments)
|
42
|
+
|
43
|
+
It's also possible to spcify Arrays of relation names.
|
data/Rakefile
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'rake'
|
2
|
+
|
3
|
+
begin
|
4
|
+
|
5
|
+
require 'jeweler'
|
6
|
+
|
7
|
+
FileList['tasks/**/*.rake'].each { |task| load task }
|
8
|
+
|
9
|
+
Jeweler::Tasks.new do |gem|
|
10
|
+
|
11
|
+
gem.name = "dm-deep_cloning"
|
12
|
+
gem.summary = %Q{A deep cloning extension for datamapper}
|
13
|
+
gem.description = %Q{A library that lets you clone objects and object graphs}
|
14
|
+
gem.email = "till.schulte-coerne@innoq.com"
|
15
|
+
gem.homepage = "http://github.com/innoq/dm-deep_cloning"
|
16
|
+
gem.authors = ["Till Schulte-Coerne"]
|
17
|
+
|
18
|
+
gem.add_dependency 'dm-core', '~> 1.0'
|
19
|
+
gem.add_dependency 'dm-transactions', '~> 1.0'
|
20
|
+
|
21
|
+
gem.add_development_dependency 'rspec', '~> 1.2.9'
|
22
|
+
gem.add_development_dependency 'dm-migrations', '~> 1.2.9'
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
Jeweler::GemcutterTasks.new
|
27
|
+
|
28
|
+
rescue LoadError
|
29
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
30
|
+
end
|
31
|
+
|
32
|
+
task :default => :spec
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.1
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "dm-deep_cloning"
|
8
|
+
s.version = "1.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Till Schulte-Coerne"]
|
12
|
+
s.date = "2012-08-14"
|
13
|
+
s.description = "A library that lets you clone objects and object graphs"
|
14
|
+
s.email = "till.schulte-coerne@innoq.com"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.md"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".travis.yml",
|
21
|
+
"Gemfile",
|
22
|
+
"LICENSE",
|
23
|
+
"README.md",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"dm-deep_cloning.gemspec",
|
27
|
+
"lib/data_mapper/deep_cloning/utilities.rb",
|
28
|
+
"lib/dm-deep_cloning.rb",
|
29
|
+
"spec/dm-deep_cloning_spec.rb",
|
30
|
+
"spec/rcov.opts",
|
31
|
+
"spec/spec.opts",
|
32
|
+
"spec/spec_helper.rb",
|
33
|
+
"tasks/changelog.rake",
|
34
|
+
"tasks/ci.rake",
|
35
|
+
"tasks/metrics.rake",
|
36
|
+
"tasks/spec.rake",
|
37
|
+
"tasks/yard.rake",
|
38
|
+
"tasks/yardstick.rake"
|
39
|
+
]
|
40
|
+
s.homepage = "http://github.com/innoq/dm-deep_cloning"
|
41
|
+
s.require_paths = ["lib"]
|
42
|
+
s.rubygems_version = "1.8.10"
|
43
|
+
s.summary = "A deep cloning extension for datamapper"
|
44
|
+
|
45
|
+
if s.respond_to? :specification_version then
|
46
|
+
s.specification_version = 3
|
47
|
+
|
48
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
49
|
+
s.add_runtime_dependency(%q<dm-core>, ["~> 1.2.0"])
|
50
|
+
s.add_runtime_dependency(%q<dm-transactions>, ["~> 1.2.0"])
|
51
|
+
s.add_development_dependency(%q<rake>, [">= 0"])
|
52
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.4"])
|
53
|
+
s.add_development_dependency(%q<rspec>, ["~> 1.3"])
|
54
|
+
s.add_development_dependency(%q<dm-migrations>, ["~> 1.2.0"])
|
55
|
+
s.add_development_dependency(%q<dm-timestamps>, ["~> 1.2.0"])
|
56
|
+
s.add_runtime_dependency(%q<dm-core>, ["~> 1.0"])
|
57
|
+
s.add_runtime_dependency(%q<dm-transactions>, ["~> 1.0"])
|
58
|
+
s.add_development_dependency(%q<rspec>, ["~> 1.2.9"])
|
59
|
+
s.add_development_dependency(%q<dm-migrations>, ["~> 1.2.9"])
|
60
|
+
else
|
61
|
+
s.add_dependency(%q<dm-core>, ["~> 1.2.0"])
|
62
|
+
s.add_dependency(%q<dm-transactions>, ["~> 1.2.0"])
|
63
|
+
s.add_dependency(%q<rake>, [">= 0"])
|
64
|
+
s.add_dependency(%q<jeweler>, ["~> 1.4"])
|
65
|
+
s.add_dependency(%q<rspec>, ["~> 1.3"])
|
66
|
+
s.add_dependency(%q<dm-migrations>, ["~> 1.2.0"])
|
67
|
+
s.add_dependency(%q<dm-timestamps>, ["~> 1.2.0"])
|
68
|
+
s.add_dependency(%q<dm-core>, ["~> 1.0"])
|
69
|
+
s.add_dependency(%q<dm-transactions>, ["~> 1.0"])
|
70
|
+
s.add_dependency(%q<rspec>, ["~> 1.2.9"])
|
71
|
+
s.add_dependency(%q<dm-migrations>, ["~> 1.2.9"])
|
72
|
+
end
|
73
|
+
else
|
74
|
+
s.add_dependency(%q<dm-core>, ["~> 1.2.0"])
|
75
|
+
s.add_dependency(%q<dm-transactions>, ["~> 1.2.0"])
|
76
|
+
s.add_dependency(%q<rake>, [">= 0"])
|
77
|
+
s.add_dependency(%q<jeweler>, ["~> 1.4"])
|
78
|
+
s.add_dependency(%q<rspec>, ["~> 1.3"])
|
79
|
+
s.add_dependency(%q<dm-migrations>, ["~> 1.2.0"])
|
80
|
+
s.add_dependency(%q<dm-timestamps>, ["~> 1.2.0"])
|
81
|
+
s.add_dependency(%q<dm-core>, ["~> 1.0"])
|
82
|
+
s.add_dependency(%q<dm-transactions>, ["~> 1.0"])
|
83
|
+
s.add_dependency(%q<rspec>, ["~> 1.2.9"])
|
84
|
+
s.add_dependency(%q<dm-migrations>, ["~> 1.2.9"])
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# Copyright 2011 innoQ Deutschland GmbH
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
require 'dm-core'
|
18
|
+
require 'dm-transactions'
|
19
|
+
|
20
|
+
module DataMapper
|
21
|
+
module DeepCloning
|
22
|
+
module Utilities
|
23
|
+
module Hash
|
24
|
+
|
25
|
+
def self.recursive_merge!(h1, h2)
|
26
|
+
h1.merge!(h2) do |key, _old, _new|
|
27
|
+
if _old.class == Hash
|
28
|
+
recursive_merge!(_old, _new)
|
29
|
+
else
|
30
|
+
_new
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# Copyright 2011 innoQ Deutschland GmbH
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
require 'dm-core'
|
18
|
+
require 'dm-transactions'
|
19
|
+
require 'data_mapper/deep_cloning/utilities'
|
20
|
+
|
21
|
+
module DataMapper
|
22
|
+
module DeepCloning
|
23
|
+
|
24
|
+
DEFAULT_MODE = :new
|
25
|
+
|
26
|
+
def deep_clone(*args)
|
27
|
+
args_size = args.size # For error messages
|
28
|
+
|
29
|
+
mode = args.shift if [:new, :create].include?(args.first)
|
30
|
+
mode ||= DEFAULT_MODE
|
31
|
+
|
32
|
+
clone_relations = {}
|
33
|
+
args.each do |arg|
|
34
|
+
case arg
|
35
|
+
when Hash
|
36
|
+
Utilities::Hash.recursive_merge!(clone_relations, arg)
|
37
|
+
when Array
|
38
|
+
arg.each do |el|
|
39
|
+
clone_relations[el] = {}
|
40
|
+
end
|
41
|
+
when Symbol
|
42
|
+
clone_relations[arg] = {}
|
43
|
+
else
|
44
|
+
raise ArgumentError, "deep_clone only accepts Symbols, Hashes or Arrays"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
attributes = self.attributes.reject{ |(k, v)|
|
49
|
+
self.class.properties[k].key? || k.to_s =~ /^(updated|created)_(at|on)$/
|
50
|
+
}
|
51
|
+
|
52
|
+
self.class.relationships.each do |relationship|
|
53
|
+
if clone_relations.keys.include?(relationship.name)
|
54
|
+
case relationship
|
55
|
+
when DataMapper::Associations::OneToMany::Relationship, DataMapper::Associations::ManyToMany::Relationship
|
56
|
+
attributes[relationship.name] = self.send(relationship.name).map do |related_object|
|
57
|
+
related_object.deep_clone(mode, clone_relations[relationship.name])
|
58
|
+
end
|
59
|
+
if attributes[relationship.name].empty?
|
60
|
+
# Delete the atrribute if no objects need to be assigned to the relation.
|
61
|
+
# dm-core seems to have a problem with Foo.new(:bars => []). Sadly
|
62
|
+
# this was not reproduceable in the specs.
|
63
|
+
attributes.delete(relationship.name)
|
64
|
+
end
|
65
|
+
when DataMapper::Associations::ManyToOne::Relationship
|
66
|
+
attributes[relationship.name] = self.send(relationship.name).deep_clone(mode, clone_relations[relationship.name])
|
67
|
+
else
|
68
|
+
raise "Deep cloning failed: Unknown relationship '#{relationship.class}' for relation '#{relationship.name}' in '#{self.class}'"
|
69
|
+
end
|
70
|
+
elsif relationship.is_a?(DataMapper::Associations::ManyToMany::Relationship) # We always need to clone many to many relationship entrys (but not targets of the relations)
|
71
|
+
attributes[relationship.name] = self.send(relationship.name)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
self.class.send(mode, attributes)
|
76
|
+
end
|
77
|
+
|
78
|
+
end # mod DeepCloning
|
79
|
+
|
80
|
+
DataMapper::Resource.send(:include, DeepCloning)
|
81
|
+
|
82
|
+
end # mod DM
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# Copyright 2011 innoQ Deutschland GmbH
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
require 'spec_helper'
|
18
|
+
|
19
|
+
describe DataMapper::DeepCloning do
|
20
|
+
|
21
|
+
before(:all) do
|
22
|
+
DataMapper.auto_migrate!
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "non recursive cloning" do
|
26
|
+
|
27
|
+
before(:all) do
|
28
|
+
@blog = Blog.create(:name => 'Test Blog')
|
29
|
+
@post1 = Post.create(:title => 'First post', :text => "Sun is shining...", :blog => @blog)
|
30
|
+
@post2 = Post.create(:title => 'Second post', :text => "Now the moon is shining", :blog => @blog, :related_posts => [@post1])
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should clone single objects unsaved" do
|
34
|
+
cloned_post = @post1.deep_clone()
|
35
|
+
|
36
|
+
cloned_post.title.should == @post1.title
|
37
|
+
cloned_post.id.should be_nil
|
38
|
+
cloned_post.saved?.should == false
|
39
|
+
|
40
|
+
cloned_post.save.should == true
|
41
|
+
cloned_post.id.should_not be(@post1.id)
|
42
|
+
|
43
|
+
cloned_post.blog.id.should be(@post1.blog.id)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should clone single objects saved" do
|
47
|
+
cloned_post = @post1.deep_clone(:create)
|
48
|
+
|
49
|
+
cloned_post.title.should == @post1.title
|
50
|
+
cloned_post.saved?.should == true
|
51
|
+
cloned_post.id.should_not be(@post1.id)
|
52
|
+
|
53
|
+
cloned_post.blog.id.should be(@post1.blog.id)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should handle many to many relations" do
|
57
|
+
cloned_post = @post2.deep_clone(:create)
|
58
|
+
|
59
|
+
cloned_post.related_posts.map(&:id).should =~ [@post1.id]
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should not clone timestamps" do
|
63
|
+
cloned_post = @post2.deep_clone(:create)
|
64
|
+
|
65
|
+
cloned_post.created_at.should_not be_nil
|
66
|
+
cloned_post.created_at.should_not be(@post2.created_at)
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "recursive cloning" do
|
72
|
+
|
73
|
+
before(:all) do
|
74
|
+
@blog = Blog.create(:name => 'Test Blog')
|
75
|
+
@post1 = Post.create(:title => 'First post', :text => "Sun is shining...", :blog => @blog)
|
76
|
+
@post2 = Post.create(:title => 'Second post', :text => "Now the moon is shining", :blog => @blog, :related_posts => [@post1])
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should handle many to many relations" do
|
80
|
+
cloned_post = @post2.deep_clone(:create, :related_posts)
|
81
|
+
|
82
|
+
cloned_post.related_posts.map(&:id).should_not include(@post1.id)
|
83
|
+
cloned_post.related_posts.map(&:title).should include(@post1.title)
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should handle nested recursive relations" do
|
87
|
+
cloned_post = @post1.deep_clone(:create, :blog => :posts)
|
88
|
+
|
89
|
+
cloned_post.blog.posts.map(&:id).should_not include(@post1.id, @post2.id)
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should be able to handle empty relations" do
|
93
|
+
emtpy_blog = Blog.create(:name => 'An empty blog')
|
94
|
+
cloned_blog = emtpy_blog.deep_clone(:posts)
|
95
|
+
|
96
|
+
cloned_blog.posts.should be_empty
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should not save new objects if not specified" do
|
100
|
+
*old_counts = Blog.count, Post.count
|
101
|
+
|
102
|
+
cloned_blog = @blog.deep_clone(:posts)
|
103
|
+
|
104
|
+
cloned_blog.saved?.should be(false)
|
105
|
+
cloned_blog.posts.map(&:saved?).should_not include(true)
|
106
|
+
cloned_blog.posts.map(&:id).compact.should be_empty
|
107
|
+
Blog.count.should be(old_counts[0])
|
108
|
+
Post.count.should be(old_counts[1])
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
data/spec/rcov.opts
ADDED
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# Copyright 2011 innoQ Deutschland GmbH
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
require 'dm-deep_cloning'
|
18
|
+
|
19
|
+
require 'dm-core/spec/setup'
|
20
|
+
DataMapper::Spec.setup
|
21
|
+
|
22
|
+
require 'dm-migrations'
|
23
|
+
require 'dm-timestamps'
|
24
|
+
|
25
|
+
# classes/vars for tests
|
26
|
+
class Post
|
27
|
+
include DataMapper::Resource
|
28
|
+
|
29
|
+
property :id, Serial
|
30
|
+
property :author, String
|
31
|
+
property :title, String
|
32
|
+
property :text, Text
|
33
|
+
|
34
|
+
timestamps :at
|
35
|
+
|
36
|
+
belongs_to :blog
|
37
|
+
has n, :comments
|
38
|
+
has n, :related_posts, "Post", :through => Resource
|
39
|
+
end
|
40
|
+
|
41
|
+
class Blog
|
42
|
+
include DataMapper::Resource
|
43
|
+
property :id, Serial
|
44
|
+
property :name, String
|
45
|
+
|
46
|
+
has n, :posts
|
47
|
+
end
|
48
|
+
|
49
|
+
class Comment
|
50
|
+
include DataMapper::Resource
|
51
|
+
|
52
|
+
property :id, Serial
|
53
|
+
property :author, String
|
54
|
+
property :text, Text
|
55
|
+
|
56
|
+
belongs_to :blog
|
57
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
desc 'update changelog'
|
4
|
+
task :changelog do
|
5
|
+
File.open('CHANGELOG', 'w+') do |changelog|
|
6
|
+
`git log -z --abbrev-commit`.split("\0").each do |commit|
|
7
|
+
next if commit =~ /^Merge: \d*/
|
8
|
+
ref, author, time, _, title, _, message = commit.split("\n", 7)
|
9
|
+
ref = ref[/commit ([0-9a-f]+)/, 1]
|
10
|
+
author = author[/Author: (.*)/, 1].strip
|
11
|
+
time = Time.parse(time[/Date: (.*)/, 1]).utc
|
12
|
+
title.strip!
|
13
|
+
|
14
|
+
changelog.puts "[#{ref} | #{time}] #{author}"
|
15
|
+
changelog.puts '', " * #{title}"
|
16
|
+
changelog.puts '', message.rstrip if message
|
17
|
+
changelog.puts
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/tasks/ci.rake
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
task :ci => [ :verify_measurements, 'metrics:all' ]
|
data/tasks/metrics.rake
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
begin
|
2
|
+
require 'metric_fu'
|
3
|
+
rescue LoadError
|
4
|
+
namespace :metrics do
|
5
|
+
task :all do
|
6
|
+
abort 'metric_fu is not available. In order to run metrics:all, you must: gem install metric_fu'
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
begin
|
12
|
+
require 'reek/adapters/rake_task'
|
13
|
+
|
14
|
+
Reek::RakeTask.new do |t|
|
15
|
+
t.fail_on_error = true
|
16
|
+
t.verbose = false
|
17
|
+
t.source_files = 'lib/**/*.rb'
|
18
|
+
end
|
19
|
+
rescue LoadError
|
20
|
+
task :reek do
|
21
|
+
abort 'Reek is not available. In order to run reek, you must: gem install reek'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
begin
|
26
|
+
require 'roodi'
|
27
|
+
require 'roodi_task'
|
28
|
+
|
29
|
+
RoodiTask.new do |t|
|
30
|
+
t.verbose = false
|
31
|
+
end
|
32
|
+
rescue LoadError
|
33
|
+
task :roodi do
|
34
|
+
abort 'Roodi is not available. In order to run roodi, you must: gem install roodi'
|
35
|
+
end
|
36
|
+
end
|
data/tasks/spec.rake
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec/rake/spectask'
|
2
|
+
require 'spec/rake/verify_rcov'
|
3
|
+
|
4
|
+
spec_defaults = lambda do |spec|
|
5
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
6
|
+
spec.libs << 'lib' << 'spec'
|
7
|
+
spec.spec_opts << '--options' << 'spec/spec.opts'
|
8
|
+
end
|
9
|
+
|
10
|
+
Spec::Rake::SpecTask.new(:spec, &spec_defaults)
|
11
|
+
|
12
|
+
Spec::Rake::SpecTask.new(:rcov) do |rcov|
|
13
|
+
spec_defaults.call(rcov)
|
14
|
+
rcov.rcov = true
|
15
|
+
rcov.rcov_opts = File.read('spec/rcov.opts').split(/\s+/)
|
16
|
+
end
|
17
|
+
|
18
|
+
RCov::VerifyTask.new(:verify_rcov => :rcov) do |rcov|
|
19
|
+
rcov.threshold = 100
|
20
|
+
end
|
21
|
+
|
22
|
+
task :default => :spec
|
data/tasks/yard.rake
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
begin
|
2
|
+
require 'pathname'
|
3
|
+
require 'yardstick/rake/measurement'
|
4
|
+
require 'yardstick/rake/verify'
|
5
|
+
|
6
|
+
# yardstick_measure task
|
7
|
+
Yardstick::Rake::Measurement.new
|
8
|
+
|
9
|
+
# verify_measurements task
|
10
|
+
Yardstick::Rake::Verify.new do |verify|
|
11
|
+
verify.threshold = 100
|
12
|
+
end
|
13
|
+
rescue LoadError
|
14
|
+
%w[ yardstick_measure verify_measurements ].each do |name|
|
15
|
+
task name.to_s do
|
16
|
+
abort "Yardstick is not available. In order to run #{name}, you must: gem install yardstick"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,253 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dm-deep_cloning
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 21
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 1.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Till Schulte-Coerne
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-08-14 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
22
|
+
none: false
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
hash: 31
|
27
|
+
segments:
|
28
|
+
- 1
|
29
|
+
- 2
|
30
|
+
- 0
|
31
|
+
version: 1.2.0
|
32
|
+
requirement: *id001
|
33
|
+
type: :runtime
|
34
|
+
name: dm-core
|
35
|
+
prerelease: false
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ~>
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
hash: 31
|
43
|
+
segments:
|
44
|
+
- 1
|
45
|
+
- 2
|
46
|
+
- 0
|
47
|
+
version: 1.2.0
|
48
|
+
requirement: *id002
|
49
|
+
type: :runtime
|
50
|
+
name: dm-transactions
|
51
|
+
prerelease: false
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
hash: 3
|
59
|
+
segments:
|
60
|
+
- 0
|
61
|
+
version: "0"
|
62
|
+
requirement: *id003
|
63
|
+
type: :development
|
64
|
+
name: rake
|
65
|
+
prerelease: false
|
66
|
+
- !ruby/object:Gem::Dependency
|
67
|
+
version_requirements: &id004 !ruby/object:Gem::Requirement
|
68
|
+
none: false
|
69
|
+
requirements:
|
70
|
+
- - ~>
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
hash: 7
|
73
|
+
segments:
|
74
|
+
- 1
|
75
|
+
- 4
|
76
|
+
version: "1.4"
|
77
|
+
requirement: *id004
|
78
|
+
type: :development
|
79
|
+
name: jeweler
|
80
|
+
prerelease: false
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
version_requirements: &id005 !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ~>
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
hash: 9
|
88
|
+
segments:
|
89
|
+
- 1
|
90
|
+
- 3
|
91
|
+
version: "1.3"
|
92
|
+
requirement: *id005
|
93
|
+
type: :development
|
94
|
+
name: rspec
|
95
|
+
prerelease: false
|
96
|
+
- !ruby/object:Gem::Dependency
|
97
|
+
version_requirements: &id006 !ruby/object:Gem::Requirement
|
98
|
+
none: false
|
99
|
+
requirements:
|
100
|
+
- - ~>
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
hash: 31
|
103
|
+
segments:
|
104
|
+
- 1
|
105
|
+
- 2
|
106
|
+
- 0
|
107
|
+
version: 1.2.0
|
108
|
+
requirement: *id006
|
109
|
+
type: :development
|
110
|
+
name: dm-migrations
|
111
|
+
prerelease: false
|
112
|
+
- !ruby/object:Gem::Dependency
|
113
|
+
version_requirements: &id007 !ruby/object:Gem::Requirement
|
114
|
+
none: false
|
115
|
+
requirements:
|
116
|
+
- - ~>
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
hash: 31
|
119
|
+
segments:
|
120
|
+
- 1
|
121
|
+
- 2
|
122
|
+
- 0
|
123
|
+
version: 1.2.0
|
124
|
+
requirement: *id007
|
125
|
+
type: :development
|
126
|
+
name: dm-timestamps
|
127
|
+
prerelease: false
|
128
|
+
- !ruby/object:Gem::Dependency
|
129
|
+
version_requirements: &id008 !ruby/object:Gem::Requirement
|
130
|
+
none: false
|
131
|
+
requirements:
|
132
|
+
- - ~>
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
hash: 15
|
135
|
+
segments:
|
136
|
+
- 1
|
137
|
+
- 0
|
138
|
+
version: "1.0"
|
139
|
+
requirement: *id008
|
140
|
+
type: :runtime
|
141
|
+
name: dm-core
|
142
|
+
prerelease: false
|
143
|
+
- !ruby/object:Gem::Dependency
|
144
|
+
version_requirements: &id009 !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ~>
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
hash: 15
|
150
|
+
segments:
|
151
|
+
- 1
|
152
|
+
- 0
|
153
|
+
version: "1.0"
|
154
|
+
requirement: *id009
|
155
|
+
type: :runtime
|
156
|
+
name: dm-transactions
|
157
|
+
prerelease: false
|
158
|
+
- !ruby/object:Gem::Dependency
|
159
|
+
version_requirements: &id010 !ruby/object:Gem::Requirement
|
160
|
+
none: false
|
161
|
+
requirements:
|
162
|
+
- - ~>
|
163
|
+
- !ruby/object:Gem::Version
|
164
|
+
hash: 13
|
165
|
+
segments:
|
166
|
+
- 1
|
167
|
+
- 2
|
168
|
+
- 9
|
169
|
+
version: 1.2.9
|
170
|
+
requirement: *id010
|
171
|
+
type: :development
|
172
|
+
name: rspec
|
173
|
+
prerelease: false
|
174
|
+
- !ruby/object:Gem::Dependency
|
175
|
+
version_requirements: &id011 !ruby/object:Gem::Requirement
|
176
|
+
none: false
|
177
|
+
requirements:
|
178
|
+
- - ~>
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
hash: 13
|
181
|
+
segments:
|
182
|
+
- 1
|
183
|
+
- 2
|
184
|
+
- 9
|
185
|
+
version: 1.2.9
|
186
|
+
requirement: *id011
|
187
|
+
type: :development
|
188
|
+
name: dm-migrations
|
189
|
+
prerelease: false
|
190
|
+
description: A library that lets you clone objects and object graphs
|
191
|
+
email: till.schulte-coerne@innoq.com
|
192
|
+
executables: []
|
193
|
+
|
194
|
+
extensions: []
|
195
|
+
|
196
|
+
extra_rdoc_files:
|
197
|
+
- LICENSE
|
198
|
+
- README.md
|
199
|
+
files:
|
200
|
+
- .travis.yml
|
201
|
+
- Gemfile
|
202
|
+
- LICENSE
|
203
|
+
- README.md
|
204
|
+
- Rakefile
|
205
|
+
- VERSION
|
206
|
+
- dm-deep_cloning.gemspec
|
207
|
+
- lib/data_mapper/deep_cloning/utilities.rb
|
208
|
+
- lib/dm-deep_cloning.rb
|
209
|
+
- spec/dm-deep_cloning_spec.rb
|
210
|
+
- spec/rcov.opts
|
211
|
+
- spec/spec.opts
|
212
|
+
- spec/spec_helper.rb
|
213
|
+
- tasks/changelog.rake
|
214
|
+
- tasks/ci.rake
|
215
|
+
- tasks/metrics.rake
|
216
|
+
- tasks/spec.rake
|
217
|
+
- tasks/yard.rake
|
218
|
+
- tasks/yardstick.rake
|
219
|
+
homepage: http://github.com/innoq/dm-deep_cloning
|
220
|
+
licenses: []
|
221
|
+
|
222
|
+
post_install_message:
|
223
|
+
rdoc_options: []
|
224
|
+
|
225
|
+
require_paths:
|
226
|
+
- lib
|
227
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
228
|
+
none: false
|
229
|
+
requirements:
|
230
|
+
- - ">="
|
231
|
+
- !ruby/object:Gem::Version
|
232
|
+
hash: 3
|
233
|
+
segments:
|
234
|
+
- 0
|
235
|
+
version: "0"
|
236
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
237
|
+
none: false
|
238
|
+
requirements:
|
239
|
+
- - ">="
|
240
|
+
- !ruby/object:Gem::Version
|
241
|
+
hash: 3
|
242
|
+
segments:
|
243
|
+
- 0
|
244
|
+
version: "0"
|
245
|
+
requirements: []
|
246
|
+
|
247
|
+
rubyforge_project:
|
248
|
+
rubygems_version: 1.8.10
|
249
|
+
signing_key:
|
250
|
+
specification_version: 3
|
251
|
+
summary: A deep cloning extension for datamapper
|
252
|
+
test_files: []
|
253
|
+
|