agnostic-duplicate 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 32cdc91880b4712356cc059c8460a991cd79d39e
4
+ data.tar.gz: 79d5fb7a6d55d51ece7ee459b5fd95fdb164cacd
5
+ SHA512:
6
+ metadata.gz: 8dab11d2925bb2e145230cd2ff3cb4e341f4cfaf68a7bca55d11805d712f257979a8d0d19b61e975ec13cfffb28e47293f9a0b614bce5aed13c0bcd9a2b52e7c
7
+ data.tar.gz: 74020d056b6f1493a9788281476fa52b4e743a1aa9466be38137e8522b4cda172eac53fc67973de2a3177f716bd0a1893d3d0dee96676763cd2fbaa49af36c94
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --format doc
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ agnostic-duplicate
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.1.2
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.2
4
+ - 2.1.1
5
+ - 2.1.0
6
+ - 2.0.0
7
+ - 1.9.3
8
+ script: rspec spec
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in agnostic-duplicate.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 David Saenz Tagarro
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,108 @@
1
+ # Agnostic::Duplicate
2
+
3
+ Duplicate objects are provided with an additional method `duplicate` that
4
+ extends the method `dup` functionality, allowing deep copy or shallow copy of
5
+ specific fields.
6
+
7
+ ## When to use
8
+
9
+ The advantage of using Duplicate module reside in support for fields that
10
+ are not duplicated by default for any reason by calling `dup`. Example: when
11
+ using Rails `dup` implementation doesn't copy attributes of model that return an
12
+ ActiveRecord::Relation, it is supossed the developer to choose his strategy.
13
+
14
+ ## Installation
15
+
16
+ Add this line to your application's Gemfile:
17
+
18
+ gem 'agnostic-duplicate'
19
+
20
+ And then execute:
21
+
22
+ $ bundle
23
+
24
+ Or install it yourself as:
25
+
26
+ $ gem install agnostic-duplicate
27
+
28
+ ## Usage
29
+
30
+ When using `Duplicate` you specify a list of attributes that you want to be
31
+ copied additionaly to the object returned by `dup`. Though if `dup` returns
32
+ a value for an attribute and you mark that attribute as "duplicable" then
33
+ the value of the attribute will be overwritten with the value provided by
34
+ `duplicate` call.
35
+
36
+ Example:
37
+
38
+ ```ruby
39
+ class Story < ActiveRecord::Base
40
+ include Duplicate
41
+ # ...
42
+ attr_duplicable :seo_element, :category, :properties
43
+ # ...
44
+ attr_accessible :title
45
+ # ...
46
+ has_one :seo_element, as: :metadatable
47
+ has_one :category, through: :categorisation, source: :category
48
+ has_many :properties, :images, :headlines
49
+ # ...
50
+ end
51
+ ```
52
+
53
+ When using `duplicable` over any attribute, it verifies if the current value
54
+ value implements `Duplicate`. In that case it returns the result of calling
55
+ to `duplicate` on that object. If the attribute doesn't implement
56
+ `Duplicate` it is returned the `dup` value.
57
+
58
+ If the `duplicable` attribute is iterable then it is returned an array where
59
+ every element of the collection is duplicated following the flow defined
60
+ previously.
61
+
62
+ Also it is possible to provide **shallow copies** of attribute values,
63
+ modifying the default behaviour. In that case, just make use of the
64
+ `strategy` option.
65
+
66
+ ```ruby
67
+ attr_duplicable :images, strategy: :shallow_copy
68
+ ```
69
+
70
+ It is given support for custom behaviour after duplication process. In that
71
+ case it is only required to implement the method `hook_after_duplicate!`
72
+
73
+ Extending previous example:
74
+
75
+ ```ruby
76
+ def hook_after_duplicate!(duplicate)
77
+ duplicate.headlines = self.headlines.not_orphans.collect(&:dup)
78
+ duplicate.images.each { |img| img.attachable = duplicate }
79
+ end
80
+ ```
81
+
82
+ **ATENTION:** Observe that `model` passed as parameter is in fact the
83
+ duplicated instance that it is going to be returned
84
+
85
+ ## Configuration options
86
+
87
+ If the only attribute values you want to be duplicated are the ones you have
88
+ specified through the `attr_duplicable` method, and though removing the
89
+ additional fields duplicated because of the init call to `dup`, then you can
90
+ set this configuration through `duplicable_config` method:
91
+
92
+ ```ruby
93
+ class Image < ActiveRecord::Base
94
+ include Duplicate
95
+ duplicable_config new_instance: true
96
+ # ...
97
+ attr_duplicable :images
98
+ # ...
99
+ end
100
+ ```
101
+
102
+ ## Contributing
103
+
104
+ 1. Fork it ( https://github.com/[my-github-username]/agnostic-duplicate/fork )
105
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
106
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
107
+ 4. Push to the branch (`git push origin my-new-feature`)
108
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,34 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'agnostic/duplicate/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'agnostic-duplicate'
8
+ spec.version = Agnostic::Duplicate::VERSION
9
+ spec.authors = ['David Saenz Tagarro']
10
+ spec.email = ['david.saenz.tagarro@gmail.com']
11
+ spec.summary = <<-summary
12
+ Duplicate library provides additional support for deep copy or shallow copy of
13
+ specific fields in your models while you are `dupping` an instance.)
14
+ summary
15
+ spec.description = <<-description
16
+ The advantage of using Duplicate module reside in support for fields that
17
+ are not duplicated by default for any reason by calling `dup`.)
18
+ description
19
+ spec.homepage = 'https://github.com/dsaenztagarro/agnostic-duplicate'
20
+ spec.license = 'MIT'
21
+
22
+ spec.files = `git ls-files -z`.split("\x0")
23
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
24
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
25
+ spec.require_paths = ['lib']
26
+
27
+ spec.add_development_dependency 'bundler', '~> 1.6'
28
+ spec.add_development_dependency 'rake'
29
+ spec.add_development_dependency 'rspec'
30
+ spec.add_development_dependency 'simplecov'
31
+ spec.add_development_dependency 'rubocop'
32
+ spec.add_development_dependency 'reek'
33
+ spec.add_development_dependency 'cane'
34
+ end
@@ -0,0 +1,5 @@
1
+ module Agnostic
2
+ module Duplicate
3
+ VERSION = '1.0.0'
4
+ end
5
+ end
@@ -0,0 +1,262 @@
1
+ require 'agnostic/duplicate/version'
2
+
3
+ module Agnostic
4
+ # Duplicate objects are provided with an additional method `duplicate` that
5
+ # extends the method `dup` functionality.
6
+ #
7
+ # ## When to use
8
+ #
9
+ # The advantage of using Duplicate module reside in support for fields that
10
+ # are not duplicated by default for any reason. Example: when using Rails
11
+ # `dup` implementation doesn't copy attributes of model that return an
12
+ # ActiveRecord::Relation, it is supossed the developer to choose his strategy.
13
+ #
14
+ # ## Usage
15
+ #
16
+ # When using `Duplicate` you specify a list of attributes that you want to be
17
+ # copied additionaly to the object returned by `dup`. Though if `dup` returns
18
+ # a value for an attribute and you mark that attribute as "duplicable" then
19
+ # the value of the attribute will be overwritten with the value provided by
20
+ # `duplicate` call.
21
+ #
22
+ # Example:
23
+ #
24
+ # ```ruby
25
+ # class Story < ActiveRecord::Base
26
+ # include Duplicate
27
+ # # ...
28
+ # attr_duplicable :seo_element, :category, :properties
29
+ # # ...
30
+ # attr_accessible :title
31
+ # # ...
32
+ # has_one :seo_element, as: :metadatable
33
+ # has_one :category, through: :categorisation, source: :category
34
+ # has_many :properties, :images, :headlines
35
+ # # ...
36
+ # end
37
+ # ```
38
+ #
39
+ # When using `duplicable` over any attribute, it verifies if the current value
40
+ # value implements `Duplicate`. In that case it returns the result of calling
41
+ # to `duplicate` on that object. If the attribute doesn't implement
42
+ # `Duplicate` it is returned the `dup` value.
43
+ #
44
+ # If the `duplicable` attribute is iterable then it is returned an array where
45
+ # every element of the collection is duplicated following the flow defined
46
+ # previously.
47
+ #
48
+ # Also it is possible to provide **shallow copies** of attribute values,
49
+ # modifying the default behaviour. In that case, just make use of the
50
+ # `strategy` option.
51
+ #
52
+ # ```ruby
53
+ # attr_duplicable :images, strategy: :shallow_copy
54
+ # ```
55
+ #
56
+ # It is given support for custom behaviour after duplication process. In that
57
+ # case it is only required to implement the method `hook_after_duplicate!`
58
+ #
59
+ # Extending previous example:
60
+ #
61
+ # ```ruby
62
+ # def hook_after_duplicate!(duplicate)
63
+ # duplicate.headlines = self.headlines.not_orphans.collect(&:dup)
64
+ # duplicate.images.each { |img| img.attachable = duplicate }
65
+ # end
66
+ # ```
67
+ #
68
+ # **ATENTION:** Observe that `model` passed as parameter is in fact the
69
+ # duplicated instance that it is going to be returned
70
+ #
71
+ # ## Configuration options
72
+ #
73
+ # If the only attribute values you want to be duplicated are the ones you have
74
+ # specified through the `attr_duplicable` method, and though removing the
75
+ # additional fields duplicated because of the init call to `dup`, then you can
76
+ # set this configuration through `duplicable_config` method:
77
+ #
78
+ # ```ruby
79
+ # class Image < ActiveRecord::Base
80
+ # include Duplicate
81
+ # duplicable_config new_instance: true
82
+ # # ...
83
+ # attr_duplicable :images
84
+ # # ...
85
+ # end
86
+ # ```
87
+ module Duplicate
88
+ def self.included(base)
89
+ base.extend(ClassMethods)
90
+ base.instance_variable_set '@duplicable_changesets', []
91
+ base.instance_variable_set '@duplicable_options', {}
92
+ end
93
+
94
+ # Duplicates the object
95
+ # @return [Duplicate] the new instance object
96
+ def duplicate
97
+ dup_template.tap do |model|
98
+ apply_changesets!(model)
99
+ hook_after_duplicate!(model) if respond_to? :hook_after_duplicate!
100
+ end
101
+ end
102
+
103
+ private
104
+
105
+ # Applies to model the duplicable changesets defined in class definition
106
+ # @param model [Duplicate] the duplicated new instance object
107
+ def apply_changesets!(model)
108
+ self.class.duplicable_changesets.each do |changeset|
109
+ changeset.apply(self, model)
110
+ end
111
+ end
112
+
113
+ # Contains all kinds of changesets that can be applied to a duplicable
114
+ # object
115
+ module ChangeSet
116
+ # Base class for all changesets. Subclasses should implement method
117
+ # `apply` (see #apply)
118
+ class Base
119
+ attr_reader :attributes
120
+ def initialize(attributes)
121
+ @attributes = attributes
122
+ end
123
+ end
124
+
125
+ # Defines a changeset where a deep copy wants to be applied to all
126
+ # attributes
127
+ class DeepCopy < Base
128
+ # Applies changes needed on the duplicated new instance object
129
+ # @param parent [Duplicate] the original object to be duplicated
130
+ # @param model [Duplicate] the duplicated new instance object
131
+ def apply(parent, model)
132
+ attributes.each do |attribute|
133
+ setter_method = "#{attribute}="
134
+ if model.respond_to?(setter_method)
135
+ model.send(setter_method, dup_attribute(parent, attribute))
136
+ else
137
+ fail "Invalid duplicable attribute '#{attribute}'"
138
+ end
139
+ end
140
+ end
141
+
142
+ private
143
+
144
+ # @param parent [Duplicate] the original object to be duplicated
145
+ # @param attribute [Symbol] the attribute to be duplicated
146
+ # @return from a duplicable object the duplicated value for the
147
+ # attribute specified
148
+ def dup_attribute(parent, attribute)
149
+ value = parent.send(attribute)
150
+ klass = self.class
151
+ if value && value.respond_to?(:collect)
152
+ value.map { |item| klass.dup_item(item) }
153
+ else
154
+ value && klass.dup_item(value)
155
+ end
156
+ end
157
+
158
+ # Duplicates the object passed as parameter
159
+ # @param item [Object] object to be duplicated
160
+ # @return [Object] the duplicated new instance object
161
+ def self.dup_item(item)
162
+ if item.respond_to? :duplicate
163
+ item.duplicate
164
+ else
165
+ item.dup
166
+ end
167
+ rescue
168
+ item
169
+ end
170
+ end
171
+
172
+ # Defines a changeset where a deep copy wants to be applied to all
173
+ # attributes.
174
+ #
175
+ # Though if the field value is a memory address it copies the memory
176
+ # address, and if the field value is a primitive type it copies the value
177
+ # of the primitive type.
178
+ class ShallowCopy < Base
179
+ # Applies changes needed on the duplicated new instance object
180
+ # @param parent [Duplicate] the original object to be duplicated
181
+ # @param model [Duplicate] the duplicated new instance object
182
+ def apply(parent, model)
183
+ attributes.each do |attribute|
184
+ model.send("#{attribute}=", parent.send(attribute))
185
+ end
186
+ end
187
+ end
188
+ end
189
+
190
+ private
191
+
192
+ # @return [Duplicate] a new instance object based on global duplicable
193
+ # configuration
194
+ def dup_template
195
+ klass = self.class
196
+ if klass.duplicable_option? :new_instance
197
+ klass.new
198
+ else
199
+ dup
200
+ end
201
+ end
202
+
203
+ # Methods added to classes including Duplicate module
204
+ module ClassMethods
205
+ attr_accessor :duplicable_changesets, :duplicable_options
206
+
207
+ # Adds a new duplicable changeset for the class.
208
+ #
209
+ # By default created changesets apply a deep copy strategy over the
210
+ # attributes specified. If you want to set a shallow copy strategy then
211
+ # you can add the option `strategy: :shallow_copy`
212
+ #
213
+ # @param *args [Array<Symbol>] a list of attribute names
214
+ # @param options [Hash] options specific for the changeset
215
+ def attr_duplicable(*args)
216
+ @changeset_options = {}
217
+ @changeset_options = args.pop if args.last.is_a? Hash
218
+ duplicable_changesets << changeset_class.new(args)
219
+ end
220
+
221
+ # Sets global options for applying changesets
222
+ #
223
+ # ## Options available:
224
+ # - `new_instance`: if `true` the duplicated instance is created calling
225
+ # in first place `new` method over the class. if `false` the duplicated
226
+ # instance is created calling to `dup` method over the instance object.
227
+ #
228
+ # @param options [Hash]
229
+ def duplicable_config(options)
230
+ if options.is_a? Hash
231
+ @duplicable_options.merge! options
232
+ keep_valid_options
233
+ else
234
+ fail ArgumentError, 'Invalid options configuration'
235
+ end
236
+ end
237
+
238
+ # @param option [Symbol] global option for duplication
239
+ # @return [Boolean] the boolean value expressing if the option is
240
+ # activated
241
+ def duplicable_option?(option)
242
+ @duplicable_options ||= {}
243
+ @duplicable_options[option]
244
+ end
245
+
246
+ private
247
+
248
+ # Remove unknown options for applying changesets
249
+ def keep_valid_options
250
+ @duplicable_options.keep_if { |key, _| [:new_instance].include? key }
251
+ end
252
+
253
+ # @return [ChangeSet::Object] based on the strategy of duplication to be
254
+ # applied over the attributes
255
+ def changeset_class
256
+ strategy = @changeset_options[:strategy] || :deep_copy
257
+ class_name = strategy.to_s.split('_').map(&:capitalize).join
258
+ Duplicate.const_get('ChangeSet').const_get("#{class_name}")
259
+ end
260
+ end
261
+ end
262
+ end
@@ -0,0 +1,249 @@
1
+ require 'spec_helper'
2
+
3
+ #:nodoc
4
+ module DuplicateSpec
5
+ NO_DUP_VALUE = 1999
6
+ DUP_VALUE = 'testValue'
7
+
8
+ #:nodoc:
9
+ class Base
10
+ attr_accessor :id, :name, :images
11
+
12
+ def initialize(options = {})
13
+ @id = options[:id]
14
+ @name = options[:name]
15
+ @images = options[:images]
16
+ end
17
+ end
18
+
19
+ #:nodoc
20
+ # Dummy class duplicated through relationship. Not implementing Duplicable.
21
+ class Image
22
+ attr_accessor :id, :caption
23
+ def initialize(options = {})
24
+ @id = options[:id]
25
+ @caption = options[:caption]
26
+ end
27
+ end
28
+
29
+ #:nodoc
30
+ # Dummy class duplicated through relationship. Implementing Duplicable.
31
+ class ImageDuplicable < Image
32
+ include Agnostic::Duplicate
33
+ attr_duplicable :caption
34
+ end
35
+
36
+ # Factory for dummy image lists
37
+ def self.create_list(class_name, size)
38
+ const_name = class_name.to_s.split('_').map(&:capitalize).join
39
+ klass = DuplicateSpec.const_get(const_name)
40
+ [].tap do |list|
41
+ size.times do |n|
42
+ instance = klass.new(id: n, caption: "Cap #{n}")
43
+ instance.stub(:dup).and_return(klass.new(id: n))
44
+ list << instance
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ RSpec.shared_context 'iterable field not dup' do
51
+ before(:each) do
52
+ @images = DuplicateSpec.create_list image_type, 3
53
+ @dummy = DuplicateSpec::DeepCopy.new(id: 1, name: 'Dummy', images: @images)
54
+ @dummy.stub(:dup).and_return(
55
+ DuplicateSpec::DeepCopy.new(id: 1), name: 'Dummy'
56
+ )
57
+ @duplicate = @dummy.duplicate
58
+ end
59
+ end
60
+
61
+ RSpec.shared_context 'not iterable field not dup' do
62
+ before(:each) do
63
+ @dummy = DuplicateSpec::DeepCopy.new(id: 1, name: attribute_value)
64
+ @dummy.stub(:dup).and_return(DuplicateSpec::DeepCopy.new(id: 1))
65
+ @duplicate = @dummy.duplicate
66
+ end
67
+ end
68
+
69
+ describe Agnostic::Duplicate do
70
+
71
+ subject { @dummy }
72
+
73
+ describe '#duplicate' do
74
+ it { respond_to :duplicate }
75
+ it { respond_to :duplicable_config }
76
+ it { respond_to :duplicate_as_new_instance? }
77
+ it { respond_to :attr_duplicable }
78
+
79
+ context 'when applying changesets' do
80
+ context 'when deep copy strategy' do
81
+ before(:all) do
82
+ module DuplicateSpec
83
+ #:nodoc
84
+ class DeepCopy < Base
85
+ include Agnostic::Duplicate
86
+ attr_duplicable :name, :images
87
+ end
88
+ end
89
+ end
90
+ context 'when attribute is iterable' do
91
+ context 'when elements implement Duplicable' do
92
+ let(:image_type) { :image_duplicable }
93
+ include_context 'iterable field not dup'
94
+
95
+ it 'is duplicated the collection' do
96
+ expect(@duplicate.images.size).to be(@images.size)
97
+ end
98
+ it 'copies duplicable attributes' do
99
+ @duplicate.images.zip(@dummy.images).each do |copy, parent|
100
+ expect(copy.caption).to eq(parent.caption)
101
+ end
102
+ end
103
+ end
104
+ context "when elements don't implement Duplicable" do
105
+ let(:image_type) { :image }
106
+ include_context 'iterable field not dup'
107
+
108
+ it 'is duplicated the collection' do
109
+ expect(@duplicate.images.size).to be(@images.size)
110
+ end
111
+ it "doesn't copy no duplicable attributes" do
112
+ @duplicate.images.each do |image|
113
+ expect(image.caption).to be(nil)
114
+ end
115
+ end
116
+ end
117
+ end
118
+ context 'when attribute is not iterable' do
119
+ context "when it cann't be dupped" do
120
+ let(:attribute_value) { DuplicateSpec::NO_DUP_VALUE }
121
+ include_context 'not iterable field not dup'
122
+
123
+ it 'is duplicated' do
124
+ expect(@duplicate.name).to eq(DuplicateSpec::NO_DUP_VALUE)
125
+ end
126
+ end
127
+ context 'when it can be dupped' do
128
+ let(:attribute_value) { DuplicateSpec::DUP_VALUE }
129
+ include_context 'not iterable field not dup'
130
+
131
+ it 'is duplicated' do
132
+ expect(@duplicate.name).to eq(DuplicateSpec::DUP_VALUE)
133
+ end
134
+ end
135
+ end
136
+ end
137
+ context 'when shallow copy strategy' do
138
+ before :all do
139
+ module DuplicateSpec
140
+ #:nodoc
141
+ class ShallowCopy < Base
142
+ include Agnostic::Duplicate
143
+ attr_duplicable :images, strategy: :shallow_copy
144
+ end
145
+ end
146
+ end
147
+
148
+ before(:each) do
149
+ @images = DuplicateSpec.create_list :image_duplicable, 3
150
+ @dummy = DuplicateSpec::ShallowCopy.new(
151
+ id: 1,
152
+ name: DuplicateSpec::DUP_VALUE,
153
+ images: @images)
154
+ @duplicate = @dummy.duplicate
155
+ end
156
+
157
+ it 'copies values keeping the object reference' do
158
+ expect(@duplicate.images).to be(@images)
159
+ end
160
+ end
161
+ end
162
+ context 'when applying hook after duplicate' do
163
+ before(:all) do
164
+ module DuplicateSpec
165
+ #:nodoc
166
+ class DeepCopyWithHook < Base
167
+ include Agnostic::Duplicate
168
+ attr_duplicable :name, :images
169
+
170
+ def hook_after_duplicate!(duplicate)
171
+ duplicate.name = "#{duplicate.name} hooked"
172
+ end
173
+ end
174
+ end
175
+ end
176
+ before(:each) do
177
+ @name = DuplicateSpec::DUP_VALUE
178
+ @dummy = DuplicateSpec::DeepCopyWithHook.new(id: 1, name: @name)
179
+ @dummy.stub(:dup).and_return(
180
+ DuplicateSpec::DeepCopyWithHook.new(id: 1))
181
+ @duplicate = @dummy.duplicate
182
+ end
183
+ it 'overrides changes made during duplication' do
184
+ expect(@duplicate.name).to eq("#{@name} hooked")
185
+ end
186
+ end
187
+ context 'when duplicating with new instance option' do
188
+ before :all do
189
+ module DuplicateSpec
190
+ #:nodoc
191
+ class NewInstanceTest < Base
192
+ include Agnostic::Duplicate
193
+ duplicable_config new_instance: true
194
+ attr_duplicable :images
195
+ end
196
+ end
197
+ end
198
+
199
+ before(:each) do
200
+ @images = DuplicateSpec.create_list :image_duplicable, 3
201
+ @dummy = DuplicateSpec::NewInstanceTest.new(
202
+ id: 1,
203
+ name: DuplicateSpec::DUP_VALUE,
204
+ images: @images)
205
+ @duplicate = @dummy.duplicate
206
+ end
207
+
208
+ it 'duplicates only fields for #attr_duplicable' do
209
+ expect(@duplicate.id).to be(nil)
210
+ expect(@duplicate.name).to be(nil)
211
+ expect(@duplicate.images.size).to be(@images.size)
212
+ end
213
+ end
214
+ context 'when invalid settings' do
215
+ context "when attribute doesn't exist" do
216
+ it 'raises an exception' do
217
+ expect do
218
+ #:nodoc
219
+ module DuplicateSpec
220
+ #:nodoc
221
+ class InvalidAttributeTest
222
+ include Agnostic::Duplicate
223
+ attr_duplicable :name
224
+ end
225
+ InvalidAttributeTest.new.duplicate
226
+ end
227
+ end.to raise_error("Invalid duplicable attribute 'name'")
228
+ end
229
+ end
230
+ context 'when duplicable config is invalid' do
231
+ it 'raises an exception' do
232
+ expect do
233
+ #:nodoc
234
+ module DuplicateSpec
235
+ #:nodoc
236
+ class DummyInvalidConfig
237
+ include Agnostic::Duplicate
238
+ duplicable_config :new_instance
239
+ attr_duplicable :name
240
+ end
241
+
242
+ DummyInvalidConfig.new.duplicate
243
+ end
244
+ end.to raise_error('Invalid options configuration')
245
+ end
246
+ end
247
+ end
248
+ end
249
+ end
@@ -0,0 +1,4 @@
1
+ require 'simplecov'
2
+ SimpleCov.start
3
+
4
+ require_relative '../lib/agnostic/duplicate'
metadata ADDED
@@ -0,0 +1,161 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: agnostic-duplicate
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - David Saenz Tagarro
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-09-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: simplecov
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: reek
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: cane
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: |
112
+ The advantage of using Duplicate module reside in support for fields that
113
+ are not duplicated by default for any reason by calling `dup`.)
114
+ email:
115
+ - david.saenz.tagarro@gmail.com
116
+ executables: []
117
+ extensions: []
118
+ extra_rdoc_files: []
119
+ files:
120
+ - ".gitignore"
121
+ - ".rspec"
122
+ - ".ruby-gemset"
123
+ - ".ruby-version"
124
+ - ".travis.yml"
125
+ - Gemfile
126
+ - LICENSE.txt
127
+ - README.md
128
+ - Rakefile
129
+ - agnostic-duplicate.gemspec
130
+ - lib/agnostic/duplicate.rb
131
+ - lib/agnostic/duplicate/version.rb
132
+ - spec/agnostic_duplicate_spec.rb
133
+ - spec/spec_helper.rb
134
+ homepage: https://github.com/dsaenztagarro/agnostic-duplicate
135
+ licenses:
136
+ - MIT
137
+ metadata: {}
138
+ post_install_message:
139
+ rdoc_options: []
140
+ require_paths:
141
+ - lib
142
+ required_ruby_version: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ required_rubygems_version: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ requirements: []
153
+ rubyforge_project:
154
+ rubygems_version: 2.2.2
155
+ signing_key:
156
+ specification_version: 4
157
+ summary: Duplicate library provides additional support for deep copy or shallow copy
158
+ of specific fields in your models while you are `dupping` an instance.)
159
+ test_files:
160
+ - spec/agnostic_duplicate_spec.rb
161
+ - spec/spec_helper.rb