agnostic-duplicate 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.
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