full_dup 0.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.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +96 -0
- data/full_dup.gemspec +25 -0
- data/lib/full_dup.rb +37 -0
- data/lib/full_dup/array.rb +23 -0
- data/lib/full_dup/hash.rb +23 -0
- data/lib/full_dup/object.rb +28 -0
- data/lib/full_dup/struct.rb +23 -0
- data/lib/full_dup/version.rb +5 -0
- data/rakefile.rb +22 -0
- metadata +113 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3a2ac1723116c886af7e561cc4acafef3d1620b6
|
4
|
+
data.tar.gz: 0517dc2b0e8be07b4711f82386fee829b94fb356
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0ebf965592c3fbf954ce912223bf7952cbda2c966e6668d15e7469e620a3f7c12cc1339ac48e28e968a9b9d0a55f59918e858bce82a3e89f4fe1a6be7f67ffa1
|
7
|
+
data.tar.gz: 35ffdbd1d46ece617723785367fec7d63d3ff00094ec1d9fa0b2c6fc3da65d7f094202a189b3125eed922e7036686c287be876ab5ed54b4afe44a11b71fc5c93
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Peter Camilleri
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
# FullDup
|
2
|
+
|
3
|
+
The standard dup method creates a fresh instance of most (non-scalar) objects
|
4
|
+
but does not process internal state. This internal state remains aliased in the
|
5
|
+
duplicated copy. The full_dup method digs deep and makes copies of these
|
6
|
+
internal variables, not just arrays and hashes. It also allows classes to
|
7
|
+
specify an exclusion list of variables that are not to be processed.
|
8
|
+
|
9
|
+
This comprehensive approach creates another issue to be resolved. In Ruby, if an
|
10
|
+
attempt is made to dup an immutable data item like a number, an error occurs.
|
11
|
+
The justification for this uncharacteristic strictness is not at all clear, but
|
12
|
+
it does mean that the dup operation must be applied with great care.
|
13
|
+
|
14
|
+
Unlike the standard dup method, the full\_dup method does not throw an
|
15
|
+
exception when it sees un-duppable value objects like 42 or true. These values
|
16
|
+
simply return themselves. This is correct because those types of objects do
|
17
|
+
not _need_ to be duped. Instead of having a fit, the code just works!
|
18
|
+
|
19
|
+
Another issue that this gem deals with is that of data with looping reference
|
20
|
+
chains. To handle this, the code tracks object ID values and does not re-dup
|
21
|
+
data that has already been duped. Thus even nasty edge cases are handled
|
22
|
+
without any special effort on the part of the application programmer.
|
23
|
+
|
24
|
+
|
25
|
+
## Installation
|
26
|
+
|
27
|
+
Add this line to your application's Gemfile:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
gem 'full_dup'
|
31
|
+
```
|
32
|
+
|
33
|
+
And then execute:
|
34
|
+
|
35
|
+
$ bundle
|
36
|
+
|
37
|
+
Or install it yourself as:
|
38
|
+
|
39
|
+
$ gem install full_dup
|
40
|
+
|
41
|
+
require 'full_dup'
|
42
|
+
|
43
|
+
then, in those places where regular dup was problematic, use:
|
44
|
+
|
45
|
+
foo = my_object.full_dup
|
46
|
+
|
47
|
+
instead of
|
48
|
+
|
49
|
+
foo = my_object.dup
|
50
|
+
|
51
|
+
To exclude some instance variables from the deep duplicating process, define a
|
52
|
+
full_dup_exclude method in the required class:
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
def full_dup_exclude
|
56
|
+
[:@bad_var1, :@bad_var2, :@bad_var_etc]
|
57
|
+
end
|
58
|
+
```
|
59
|
+
This also can be applied to arrays and hashes. In this case, it is possible to
|
60
|
+
define a singleton method on the duped data. Then the exclude method would
|
61
|
+
return an array of array indexes or hash keys to be omitted from the full dup
|
62
|
+
recursion. Here is an example that never dupes the first two elements of the
|
63
|
+
array:
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
my_array.define_singleton_method(:full_dup_exclude) { [0, 1] }
|
67
|
+
```
|
68
|
+
<br>**Possible Red Flag** There is a catch here. The dup method does _not_
|
69
|
+
duplicate singleton methods (unlike the clone method). Thus any duplicates
|
70
|
+
made in this manner will lose the attached full_dup_exclude method. If it is
|
71
|
+
important to retain singleton methods, consider using the full_clone gem
|
72
|
+
instead.
|
73
|
+
|
74
|
+
## Notes
|
75
|
+
|
76
|
+
The full_dup gem tracks its progress and handles data objects that
|
77
|
+
contain loops, cycles, and other forms of recursion. In order to do this,
|
78
|
+
it relies heavily on the object_id property of the data being copied.
|
79
|
+
If object_id is broken, then full_dup and hashes and ... will also be
|
80
|
+
broken!
|
81
|
+
|
82
|
+
|
83
|
+
## Contributing
|
84
|
+
|
85
|
+
#### Plan A
|
86
|
+
|
87
|
+
1. Fork it ( https://github.com/PeterCamilleri/full_dup/fork )
|
88
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
89
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
90
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
91
|
+
5. Create new Pull Request
|
92
|
+
|
93
|
+
#### Plan B
|
94
|
+
|
95
|
+
Go to the GitHub repository and raise an issue calling attention to some
|
96
|
+
aspect that could use some TLC or a suggestion or an idea.
|
data/full_dup.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'full_dup/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "full_dup"
|
8
|
+
spec.version = FullDup::VERSION
|
9
|
+
spec.authors = ["Peter Camilleri"]
|
10
|
+
spec.email = ["peter.c.camilleri@gmail.com"]
|
11
|
+
|
12
|
+
spec.description = "A (safe/no exceptions) dup variant that performs a deep, recursive copy."
|
13
|
+
spec.summary = "A dup variant that performs a deep copy."
|
14
|
+
spec.homepage = "http://teuthida-technologies.com/"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency 'minitest_visible', ">= 0.1.0"
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
23
|
+
spec.add_development_dependency "rake"
|
24
|
+
spec.add_development_dependency 'minitest', "~> 4.7.5"
|
25
|
+
end
|
data/lib/full_dup.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require_relative 'full_dup/version'
|
4
|
+
require_relative 'full_dup/object'
|
5
|
+
require_relative 'full_dup/array'
|
6
|
+
require_relative 'full_dup/hash'
|
7
|
+
require_relative 'full_dup/struct'
|
8
|
+
|
9
|
+
module FullDup
|
10
|
+
def full_dup(_progress={})
|
11
|
+
self
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Numeric
|
16
|
+
include FullDup
|
17
|
+
end
|
18
|
+
|
19
|
+
class NilClass
|
20
|
+
include FullDup
|
21
|
+
end
|
22
|
+
|
23
|
+
class TrueClass
|
24
|
+
include FullDup
|
25
|
+
end
|
26
|
+
|
27
|
+
class FalseClass
|
28
|
+
include FullDup
|
29
|
+
end
|
30
|
+
|
31
|
+
class Symbol
|
32
|
+
include FullDup
|
33
|
+
end
|
34
|
+
|
35
|
+
class Regexp
|
36
|
+
include FullDup
|
37
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
class Array
|
4
|
+
|
5
|
+
#The full_dup method for arrays.
|
6
|
+
def full_dup(progress={})
|
7
|
+
progress[object_id] = result = dup
|
8
|
+
exclude = full_dup_exclude
|
9
|
+
|
10
|
+
each_index do |name|
|
11
|
+
|
12
|
+
unless exclude.include?(name)
|
13
|
+
value = result[name]
|
14
|
+
value = progress[value.object_id] || value.full_dup(progress)
|
15
|
+
result[name] = value
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
result
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
class Hash
|
4
|
+
|
5
|
+
#The full_dup method for hashes.
|
6
|
+
def full_dup(progress={})
|
7
|
+
progress[object_id] = result = dup
|
8
|
+
exclude = full_dup_exclude
|
9
|
+
|
10
|
+
each_key do |name|
|
11
|
+
|
12
|
+
unless exclude.include?(name)
|
13
|
+
value = result[name]
|
14
|
+
value = progress[value.object_id] || value.full_dup(progress)
|
15
|
+
result[name] = value
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
result
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
class Object
|
4
|
+
|
5
|
+
#By default, no instance variables are excluded.
|
6
|
+
def full_dup_exclude
|
7
|
+
[]
|
8
|
+
end
|
9
|
+
|
10
|
+
#The full_dup method for most objects.
|
11
|
+
def full_dup(progress={})
|
12
|
+
progress[object_id] = result = dup
|
13
|
+
exclude = full_dup_exclude
|
14
|
+
|
15
|
+
instance_variables.each do |name|
|
16
|
+
|
17
|
+
unless exclude.include?(name)
|
18
|
+
value = result.instance_variable_get(name)
|
19
|
+
value = progress[value.object_id] || value.full_dup(progress)
|
20
|
+
result.instance_variable_set(name, value)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
result
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
class Struct
|
4
|
+
|
5
|
+
#The full_dup method for structs.
|
6
|
+
def full_dup(progress={})
|
7
|
+
progress[object_id] = result = dup
|
8
|
+
exclude = full_dup_exclude
|
9
|
+
|
10
|
+
members.each do |name|
|
11
|
+
|
12
|
+
unless exclude.include?(name)
|
13
|
+
value = result[name]
|
14
|
+
value = progress[value.object_id] || value.full_dup(progress)
|
15
|
+
result[name] = value
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
result
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
data/rakefile.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rake/testtask'
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
|
4
|
+
#Run the unit test suite.
|
5
|
+
Rake::TestTask.new do |t|
|
6
|
+
#List out all the test files.
|
7
|
+
t.test_files = ["test/full_dup_tests.rb",
|
8
|
+
"test/object_tests.rb",
|
9
|
+
"test/array_tests.rb",
|
10
|
+
"test/hash_tests.rb",
|
11
|
+
"test/struct_tests.rb"]
|
12
|
+
|
13
|
+
t.verbose = false
|
14
|
+
t.warning = true
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "What version of full_dup is this?"
|
18
|
+
task :vers do |t|
|
19
|
+
puts
|
20
|
+
puts "full_dup version = #{FullDup::VERSION}"
|
21
|
+
end
|
22
|
+
|
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: full_dup
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Peter Camilleri
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-04-14 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: minitest_visible
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.1.0
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.1.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.3'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
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: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 4.7.5
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 4.7.5
|
69
|
+
description: A (safe/no exceptions) dup variant that performs a deep, recursive copy.
|
70
|
+
email:
|
71
|
+
- peter.c.camilleri@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".gitignore"
|
77
|
+
- Gemfile
|
78
|
+
- LICENSE.txt
|
79
|
+
- README.md
|
80
|
+
- full_dup.gemspec
|
81
|
+
- lib/full_dup.rb
|
82
|
+
- lib/full_dup/array.rb
|
83
|
+
- lib/full_dup/hash.rb
|
84
|
+
- lib/full_dup/object.rb
|
85
|
+
- lib/full_dup/struct.rb
|
86
|
+
- lib/full_dup/version.rb
|
87
|
+
- rakefile.rb
|
88
|
+
homepage: http://teuthida-technologies.com/
|
89
|
+
licenses:
|
90
|
+
- MIT
|
91
|
+
metadata: {}
|
92
|
+
post_install_message:
|
93
|
+
rdoc_options: []
|
94
|
+
require_paths:
|
95
|
+
- lib
|
96
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
requirements: []
|
107
|
+
rubyforge_project:
|
108
|
+
rubygems_version: 2.2.2
|
109
|
+
signing_key:
|
110
|
+
specification_version: 4
|
111
|
+
summary: A dup variant that performs a deep copy.
|
112
|
+
test_files: []
|
113
|
+
has_rdoc:
|