agnostic-duplicate 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +8 -5
- data/CHANGELOG.md +7 -0
- data/README.md +24 -1
- data/Rakefile.rb +13 -0
- data/agnostic-duplicate.gemspec +4 -2
- data/lib/agnostic/duplicate.rb +72 -21
- data/lib/agnostic/duplicate/version.rb +2 -1
- data/spec/agnostic_duplicate_spec.rb +42 -2
- data/spec/spec_helper.rb +8 -0
- metadata +32 -3
- data/Rakefile +0 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0f1eab323490625f666f5be5cb411b804541d471
|
4
|
+
data.tar.gz: e51eb8409624bdb8ac8819b20056fe06686ddecf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 61853c9158efa07ca9a8ff3d8f5791655a6269d692f1bbb4c6ecd823ca1bdd6ac569443fde5813420566a1d7ee679c5d41490094ea6e07508fbb2d47e3cf6993
|
7
|
+
data.tar.gz: 0d073b672d0f70242e7b24ef5f0f9802efded6ca751ec49fb92a9cde4772f734f9eb4b2b676eb045b9e546287e5139df839020d7d4748edb591ecbab10491a4d
|
data/.travis.yml
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
- 2.1.2
|
4
|
+
- 2.1.1
|
5
|
+
- 2.1.0
|
6
|
+
- 2.0.0
|
7
|
+
- 1.9.3
|
8
8
|
script: rspec spec
|
9
|
+
addons:
|
10
|
+
code_climate:
|
11
|
+
repo_token: 11404e3572fdb6bae4c9e45cb6effc8c294a8f9d7ec3f5d581e189eae6008912
|
data/CHANGELOG.md
ADDED
data/README.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
[![Gem Version](https://badge.fury.io/rb/agnostic-duplicate.svg)](http://badge.fury.io/rb/agnostic-duplicate)
|
2
|
+
[![Build Status](https://travis-ci.org/dsaenztagarro/agnostic-duplicate.png)](https://travis-ci.org/dsaenztagarro/agnostic-duplicate)
|
3
|
+
[![Code Climate](https://codeclimate.com/github/dsaenztagarro/agnostic-duplicate/badges/gpa.svg)](https://codeclimate.com/github/dsaenztagarro/agnostic-duplicate)
|
4
|
+
[![Coverage Status](https://coveralls.io/repos/dsaenztagarro/agnostic-duplicate/badge.png?branch=master)](https://coveralls.io/r/dsaenztagarro/agnostic-duplicate?branch=master)
|
5
|
+
[![Dependency Status](https://gemnasium.com/dsaenztagarro/agnostic-duplicate.svg)](https://gemnasium.com/dsaenztagarro/agnostic-duplicate)
|
6
|
+
|
1
7
|
# Agnostic::Duplicate
|
2
8
|
|
3
9
|
Duplicate objects are provided with an additional method `duplicate` that
|
@@ -99,9 +105,26 @@ set this configuration through `duplicable_config` method:
|
|
99
105
|
end
|
100
106
|
```
|
101
107
|
|
108
|
+
If you want to apply the `duplicate` over a custom instance object instead
|
109
|
+
of the default template for the current configuration, then you can pass a
|
110
|
+
`dup_template` option on the method call
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
otherobject # => Object sharing duplicable attributes with 'myobject'
|
114
|
+
myobject.duplicate dup_template: otherobject
|
115
|
+
```
|
116
|
+
|
117
|
+
As the object passed to dup_template should be compliant with the duplicable
|
118
|
+
attribute list, if there is an error during the process an exception will
|
119
|
+
be raise according to the type of error:
|
120
|
+
|
121
|
+
- Agnostic::Duplicate::ChangeSet::AttributeNotFound
|
122
|
+
- Agnostic::Duplicate::ChangeSet::CopyError
|
123
|
+
|
124
|
+
|
102
125
|
## Contributing
|
103
126
|
|
104
|
-
1. Fork it ( https://github.com/
|
127
|
+
1. Fork it ( https://github.com/dsaenztagarro/agnostic-duplicate/fork )
|
105
128
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
106
129
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
107
130
|
4. Push to the branch (`git push origin my-new-feature`)
|
data/Rakefile.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rubocop/rake_task'
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
|
5
|
+
RuboCop::RakeTask.new do |task|
|
6
|
+
task.requires << 'rubocop-rspec'
|
7
|
+
end
|
8
|
+
|
9
|
+
RSpec::Core::RakeTask.new :specs do |task|
|
10
|
+
task.pattern = Dir['spec/**/*_spec.rb']
|
11
|
+
end
|
12
|
+
|
13
|
+
task default: ['specs']
|
data/agnostic-duplicate.gemspec
CHANGED
@@ -12,10 +12,10 @@ Gem::Specification.new do |spec|
|
|
12
12
|
Duplicate library provides additional support for deep copy or shallow copy of
|
13
13
|
specific fields in your models while you are `dupping` an instance.)
|
14
14
|
summary
|
15
|
-
spec.description = <<-
|
15
|
+
spec.description = <<-desc
|
16
16
|
The advantage of using Duplicate module reside in support for fields that
|
17
17
|
are not duplicated by default for any reason by calling `dup`.)
|
18
|
-
|
18
|
+
desc
|
19
19
|
spec.homepage = 'https://github.com/dsaenztagarro/agnostic-duplicate'
|
20
20
|
spec.license = 'MIT'
|
21
21
|
|
@@ -27,7 +27,9 @@ description
|
|
27
27
|
spec.add_development_dependency 'bundler', '~> 1.6'
|
28
28
|
spec.add_development_dependency 'rake'
|
29
29
|
spec.add_development_dependency 'rspec'
|
30
|
+
spec.add_development_dependency 'codeclimate-test-reporter'
|
30
31
|
spec.add_development_dependency 'simplecov'
|
32
|
+
spec.add_development_dependency 'coveralls'
|
31
33
|
spec.add_development_dependency 'rubocop'
|
32
34
|
spec.add_development_dependency 'reek'
|
33
35
|
spec.add_development_dependency 'cane'
|
data/lib/agnostic/duplicate.rb
CHANGED
@@ -84,6 +84,22 @@ module Agnostic
|
|
84
84
|
# # ...
|
85
85
|
# end
|
86
86
|
# ```
|
87
|
+
#
|
88
|
+
# If you want to apply the `duplicate` over a custom instance object instead
|
89
|
+
# of the default template for the current configuration, then you can pass a
|
90
|
+
# `dup_template` option on the method call
|
91
|
+
#
|
92
|
+
# ```ruby
|
93
|
+
# otherobject # => Object sharing duplicable attributes with 'myobject'
|
94
|
+
# myobject.duplicate dup_template: otherobject
|
95
|
+
# ```
|
96
|
+
#
|
97
|
+
# As the object passed to dup_template should be compliant with the duplicable
|
98
|
+
# attribute list, if there is an error during the process an exception will
|
99
|
+
# be raise according to the type of error:
|
100
|
+
# - Agnostic::Duplicate::ChangeSet::AttributeNotFound
|
101
|
+
# - Agnostic::Duplicate::ChangeSet::CopyError
|
102
|
+
#
|
87
103
|
module Duplicate
|
88
104
|
def self.included(base)
|
89
105
|
base.extend(ClassMethods)
|
@@ -93,8 +109,11 @@ module Agnostic
|
|
93
109
|
|
94
110
|
# Duplicates the object
|
95
111
|
# @return [Duplicate] the new instance object
|
96
|
-
|
97
|
-
|
112
|
+
# @return opts [Hash] the options for duplicating
|
113
|
+
# @option [Object] dup_template The object over attributes are going to be
|
114
|
+
# copied
|
115
|
+
def duplicate(opts = {})
|
116
|
+
(opts[:dup_template] || dup_template).tap do |model|
|
98
117
|
apply_changesets!(model)
|
99
118
|
hook_after_duplicate!(model) if respond_to? :hook_after_duplicate!
|
100
119
|
end
|
@@ -113,6 +132,13 @@ module Agnostic
|
|
113
132
|
# Contains all kinds of changesets that can be applied to a duplicable
|
114
133
|
# object
|
115
134
|
module ChangeSet
|
135
|
+
# Raised when there is an error while trying to copy an attribute
|
136
|
+
class CopyError < StandardError
|
137
|
+
end
|
138
|
+
# Raised when a non existing attribute is tried to be duplicated
|
139
|
+
class AttributeNotFound < StandardError
|
140
|
+
end
|
141
|
+
|
116
142
|
# Base class for all changesets. Subclasses should implement method
|
117
143
|
# `apply` (see #apply)
|
118
144
|
class Base
|
@@ -120,6 +146,13 @@ module Agnostic
|
|
120
146
|
def initialize(attributes)
|
121
147
|
@attributes = attributes
|
122
148
|
end
|
149
|
+
|
150
|
+
private
|
151
|
+
|
152
|
+
def raise_copy_error_for(attribute)
|
153
|
+
msg = "It wasn't possible to copy attribute '#{attribute}'"
|
154
|
+
fail CopyError, msg, caller
|
155
|
+
end
|
123
156
|
end
|
124
157
|
|
125
158
|
# Defines a changeset where a deep copy wants to be applied to all
|
@@ -130,17 +163,25 @@ module Agnostic
|
|
130
163
|
# @param model [Duplicate] the duplicated new instance object
|
131
164
|
def apply(parent, model)
|
132
165
|
attributes.each do |attribute|
|
133
|
-
|
134
|
-
|
135
|
-
model.send(setter_method, dup_attribute(parent, attribute))
|
136
|
-
else
|
137
|
-
fail "Invalid duplicable attribute '#{attribute}'"
|
166
|
+
unless model.respond_to? "#{attribute}="
|
167
|
+
fail AttributeNotFound, "Attribute: '#{attribute}'", caller
|
138
168
|
end
|
169
|
+
deep_copy = dup_attribute(parent, attribute)
|
170
|
+
copy_attribute(attribute, model, deep_copy)
|
139
171
|
end
|
140
172
|
end
|
141
173
|
|
142
174
|
private
|
143
175
|
|
176
|
+
# @param attribute [Symbol] attribute to be copied
|
177
|
+
# @param parent [Duplicable] the original object to be duplicated
|
178
|
+
# @param model [Duplicable] the duplicated new instance object
|
179
|
+
def copy_attribute(attribute, model, deep_copy)
|
180
|
+
model.send("#{attribute}=", deep_copy)
|
181
|
+
rescue
|
182
|
+
raise_copy_error_for(attribute)
|
183
|
+
end
|
184
|
+
|
144
185
|
# @param parent [Duplicate] the original object to be duplicated
|
145
186
|
# @param attribute [Symbol] the attribute to be duplicated
|
146
187
|
# @return from a duplicable object the duplicated value for the
|
@@ -177,19 +218,30 @@ module Agnostic
|
|
177
218
|
# of the primitive type.
|
178
219
|
class ShallowCopy < Base
|
179
220
|
# Applies changes needed on the duplicated new instance object
|
180
|
-
# @param parent [
|
181
|
-
# @param model [
|
221
|
+
# @param parent [Duplicable] the original object to be duplicated
|
222
|
+
# @param model [Duplicable] the duplicated new instance object
|
182
223
|
def apply(parent, model)
|
183
224
|
attributes.each do |attribute|
|
184
|
-
|
225
|
+
copy_attribute(attribute, parent, model)
|
185
226
|
end
|
186
227
|
end
|
228
|
+
|
229
|
+
private
|
230
|
+
|
231
|
+
# @param attribute [Symbol] attribute to be copied
|
232
|
+
# @param parent [Duplicable] the original object to be duplicated
|
233
|
+
# @param model [Duplicable] the duplicated new instance object
|
234
|
+
def copy_attribute(attribute, parent, model)
|
235
|
+
model.send("#{attribute}=", parent.send(attribute))
|
236
|
+
rescue
|
237
|
+
raise_copy_error_for(attribute)
|
238
|
+
end
|
187
239
|
end
|
188
240
|
end
|
189
241
|
|
190
242
|
private
|
191
243
|
|
192
|
-
# @return [
|
244
|
+
# @return [Duplicable] a new instance object based on global duplicable
|
193
245
|
# configuration
|
194
246
|
def dup_template
|
195
247
|
klass = self.class
|
@@ -200,7 +252,7 @@ module Agnostic
|
|
200
252
|
end
|
201
253
|
end
|
202
254
|
|
203
|
-
# Methods added to classes including
|
255
|
+
# Methods added to classes including Duplicable module
|
204
256
|
module ClassMethods
|
205
257
|
attr_accessor :duplicable_changesets, :duplicable_options
|
206
258
|
|
@@ -220,15 +272,14 @@ module Agnostic
|
|
220
272
|
|
221
273
|
# Sets global options for applying changesets
|
222
274
|
#
|
223
|
-
#
|
224
|
-
#
|
225
|
-
# in first place `new` method over the class. if
|
226
|
-
# instance is created calling to `dup` method
|
227
|
-
#
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
@duplicable_options.merge! options
|
275
|
+
# @param opts [Hash] The options for duplicable configuration
|
276
|
+
# @option opts [Boolean] :new_instance If `true` the duplicated instance
|
277
|
+
# is created calling in first place `new` method over the class. if
|
278
|
+
# `false` the duplicated instance is created calling to `dup` method
|
279
|
+
# over the instance object.
|
280
|
+
def duplicable_config(opts)
|
281
|
+
if opts.is_a? Hash
|
282
|
+
@duplicable_options.merge! opts
|
232
283
|
keep_valid_options
|
233
284
|
else
|
234
285
|
fail ArgumentError, 'Invalid options configuration'
|
@@ -213,7 +213,7 @@ describe Agnostic::Duplicate do
|
|
213
213
|
end
|
214
214
|
context 'when invalid settings' do
|
215
215
|
context "when attribute doesn't exist" do
|
216
|
-
it 'raises an
|
216
|
+
it 'raises an attribute not found error' do
|
217
217
|
expect do
|
218
218
|
#:nodoc
|
219
219
|
module DuplicateSpec
|
@@ -224,7 +224,47 @@ describe Agnostic::Duplicate do
|
|
224
224
|
end
|
225
225
|
InvalidAttributeTest.new.duplicate
|
226
226
|
end
|
227
|
-
end.to raise_error
|
227
|
+
end.to raise_error Agnostic::Duplicate::ChangeSet::AttributeNotFound
|
228
|
+
end
|
229
|
+
end
|
230
|
+
context 'when custom dup_template lacks of duplicable attribute' do
|
231
|
+
it 'raises a copy error on deep copy' do
|
232
|
+
expect do
|
233
|
+
#:nodoc
|
234
|
+
module DuplicateSpec
|
235
|
+
#:nodoc
|
236
|
+
class InvalidDupTemplateTest < Base
|
237
|
+
include Agnostic::Duplicate
|
238
|
+
attr_duplicable :name
|
239
|
+
end
|
240
|
+
|
241
|
+
obj = Object.new
|
242
|
+
obj.define_singleton_method('name=') do
|
243
|
+
fail 'Internal error'
|
244
|
+
end
|
245
|
+
|
246
|
+
InvalidDupTemplateTest.new.duplicate dup_template: obj
|
247
|
+
end
|
248
|
+
end.to raise_error Agnostic::Duplicate::ChangeSet::CopyError
|
249
|
+
end
|
250
|
+
it 'raises a copy error on shallow copy' do
|
251
|
+
expect do
|
252
|
+
#:nodoc
|
253
|
+
module DuplicateSpec
|
254
|
+
#:nodoc
|
255
|
+
class InvalidDupTemplateTest < Base
|
256
|
+
include Agnostic::Duplicate
|
257
|
+
attr_duplicable :name, strategy: :shallow_copy
|
258
|
+
end
|
259
|
+
|
260
|
+
obj = Object.new
|
261
|
+
obj.define_singleton_method('name=') do
|
262
|
+
fail 'Internal error'
|
263
|
+
end
|
264
|
+
|
265
|
+
InvalidDupTemplateTest.new.duplicate dup_template: obj
|
266
|
+
end
|
267
|
+
end.to raise_error Agnostic::Duplicate::ChangeSet::CopyError
|
228
268
|
end
|
229
269
|
end
|
230
270
|
context 'when duplicable config is invalid' do
|
data/spec/spec_helper.rb
CHANGED
@@ -1,4 +1,12 @@
|
|
1
|
+
require 'codeclimate-test-reporter'
|
2
|
+
CodeClimate::TestReporter.start
|
3
|
+
|
1
4
|
require 'simplecov'
|
5
|
+
require 'coveralls'
|
6
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
7
|
+
SimpleCov::Formatter::HTMLFormatter,
|
8
|
+
Coveralls::SimpleCov::Formatter
|
9
|
+
]
|
2
10
|
SimpleCov.start
|
3
11
|
|
4
12
|
require_relative '../lib/agnostic/duplicate'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: agnostic-duplicate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Saenz Tagarro
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-12-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: codeclimate-test-reporter
|
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'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: simplecov
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,6 +80,20 @@ dependencies:
|
|
66
80
|
- - ">="
|
67
81
|
- !ruby/object:Gem::Version
|
68
82
|
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: coveralls
|
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'
|
69
97
|
- !ruby/object:Gem::Dependency
|
70
98
|
name: rubocop
|
71
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -122,10 +150,11 @@ files:
|
|
122
150
|
- ".ruby-gemset"
|
123
151
|
- ".ruby-version"
|
124
152
|
- ".travis.yml"
|
153
|
+
- CHANGELOG.md
|
125
154
|
- Gemfile
|
126
155
|
- LICENSE.txt
|
127
156
|
- README.md
|
128
|
-
- Rakefile
|
157
|
+
- Rakefile.rb
|
129
158
|
- agnostic-duplicate.gemspec
|
130
159
|
- lib/agnostic/duplicate.rb
|
131
160
|
- lib/agnostic/duplicate/version.rb
|
data/Rakefile
DELETED