full_dup 0.0.5 → 1.0.3
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 +5 -5
- data/README.md +69 -14
- data/full_dup.gemspec +3 -3
- data/lib/full_dup.rb +4 -29
- data/lib/full_dup/array.rb +13 -10
- data/lib/full_dup/hash.rb +26 -23
- data/lib/full_dup/object.rb +23 -6
- data/lib/full_dup/struct.rb +13 -10
- data/lib/full_dup/use_dup.rb +23 -0
- data/lib/full_dup/use_self.rb +44 -0
- data/lib/full_dup/version.rb +1 -1
- data/rakefile.rb +5 -0
- data/reek.txt +1 -0
- metadata +17 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 5780661f9225891467a4552a1b065627b67c21f5619566e7038cfcbf50b8c148
|
4
|
+
data.tar.gz: ff40d94205773a95baccc9feaa4c6c9a287ed89f5b12b3b9e1f8cb5000506c17
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 779b609572b851269a75014bedadd06c8443b4041562cf05046c75717f877a6a6d7e136ac967751a7af368d37ba588993e2066e92e3742949978b5328b9fa0c6
|
7
|
+
data.tar.gz: fd1f6a258c8d494b196e6e228689a23127f7b4618fec197621d5b0b7fe8c322f635824ed37cb90b169d541992e298fbe51b3f5d1968ffd3b8572ab5d9ce17852
|
data/README.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
# FullDup
|
2
2
|
|
3
|
+
The full_dup gem adds the full_dup method to all objects descended from the
|
4
|
+
Object class. While this gem makes extensive use of monkey patching, it does
|
5
|
+
not modify the behaviour of any existing methods. This is done to minimize
|
6
|
+
the risk of breaking any existing code.
|
7
|
+
|
8
|
+
New for version 0.0.6, now 84% faster (at least in my tests).
|
9
|
+
|
3
10
|
The standard dup method creates a fresh instance of most (non-scalar) objects
|
4
11
|
but does not process internal state. This internal state remains aliased in the
|
5
12
|
duplicated copy. The full_dup method digs deep and makes copies of these
|
@@ -13,13 +20,24 @@ it does mean that the dup operation must be applied with great care.
|
|
13
20
|
|
14
21
|
Unlike the standard dup method, the full\_dup method does not throw an
|
15
22
|
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
|
17
|
-
not
|
23
|
+
simply return themselves. This is deemed correct because those types of objects
|
24
|
+
are immutable and do not need to be duped. Instead of raising an exception,
|
25
|
+
the code returns the immutable object instead.
|
18
26
|
|
19
27
|
Another issue that this gem deals with is that of data with looping reference
|
20
28
|
chains. To handle this, the code tracks object ID values and does not re-dup
|
21
29
|
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.
|
30
|
+
without any special effort on the part of the application programmer. Note though
|
31
|
+
that this also means that it is important that the object id be correctly
|
32
|
+
implemented. Fortunately, this is done by default in Ruby.
|
33
|
+
|
34
|
+
If you wish to implement your own object id for your own special classes:
|
35
|
+
1. Don't! If object_id is broken,
|
36
|
+
then full_dup (and a whole lot of other things too) will also be broken!
|
37
|
+
2. It's all on you to do as good a job as Ruby. Like the Ruby object id method,
|
38
|
+
your method must create id values that are unique to each object and are perfectly
|
39
|
+
repeatable for that object.
|
40
|
+
3. Really DON'T! I have never found a valid reason for doing so. I doubt that one exists.
|
23
41
|
|
24
42
|
## Family Overview
|
25
43
|
|
@@ -93,20 +111,46 @@ array:
|
|
93
111
|
```ruby
|
94
112
|
my_array.define_singleton_method(:full_dup_exclude) { [0, 1] }
|
95
113
|
```
|
96
|
-
<br>**Possible Red Flag** There is a catch here. The dup
|
97
|
-
duplicate singleton methods (unlike the clone
|
98
|
-
made in this manner will lose the attached full_dup_exclude
|
99
|
-
important to retain singleton methods, consider using the
|
100
|
-
instead.
|
114
|
+
<br>**Possible Red Flag** There is a catch here. The dup and full_dup methods
|
115
|
+
do not duplicate singleton methods (unlike the clone and full_clone methods).
|
116
|
+
Thus any duplicates made in this manner will lose the attached full_dup_exclude
|
117
|
+
method. If it is important to retain singleton methods, consider using the
|
118
|
+
full_clone gem instead.
|
101
119
|
|
102
|
-
|
120
|
+
### irbt
|
103
121
|
|
104
|
-
The full_dup gem
|
105
|
-
|
106
|
-
|
107
|
-
If object_id is broken, then full_dup and hashes and ... will also be
|
108
|
-
broken!
|
122
|
+
The root folder of the full_dup gem contains the file irbt.rb. This program
|
123
|
+
opens up the irb repl with the full_dup gem preloaded and is useful for trying
|
124
|
+
out the gem interactively.
|
109
125
|
|
126
|
+
By default, irbt will load the system gem version of full dup. The following
|
127
|
+
interactive session demonstrates the difference between dup and full_dup
|
128
|
+
|
129
|
+
```
|
130
|
+
C:\Sites\full_dup>ruby irbt.rb
|
131
|
+
Starting an IRB console with full_dup loaded.
|
132
|
+
full_dup loaded from gem: 0.0.5
|
133
|
+
irb(main):001:0> a = ["a", "b", "c"]
|
134
|
+
=> ["a", "b", "c"]
|
135
|
+
irb(main):002:0> b = a.dup
|
136
|
+
=> ["a", "b", "c"]
|
137
|
+
irb(main):003:0> c = a.full_dup
|
138
|
+
=> ["a", "b", "c"]
|
139
|
+
irb(main):004:0> a[0] << "foo"
|
140
|
+
=> "afoo"
|
141
|
+
irb(main):005:0> a
|
142
|
+
=> ["afoo", "b", "c"]
|
143
|
+
irb(main):006:0> b
|
144
|
+
=> ["afoo", "b", "c"]
|
145
|
+
irb(main):007:0> c
|
146
|
+
=> ["a", "b", "c"]
|
147
|
+
irb(main):008:0>
|
148
|
+
```
|
149
|
+
To load the local copy of full_dup use:
|
150
|
+
```
|
151
|
+
>ruby irbt.rb local #optional irb args go here.
|
152
|
+
#etc etc etc...
|
153
|
+
```
|
110
154
|
|
111
155
|
## Contributing
|
112
156
|
|
@@ -122,3 +166,14 @@ broken!
|
|
122
166
|
|
123
167
|
Go to the GitHub repository and raise an issue calling attention to some
|
124
168
|
aspect that could use some TLC or a suggestion or an idea.
|
169
|
+
|
170
|
+
## License
|
171
|
+
|
172
|
+
The gem is available as open source under the terms of the
|
173
|
+
[MIT License](./LICENSE.txt).
|
174
|
+
|
175
|
+
## Code of Conduct
|
176
|
+
|
177
|
+
Everyone interacting in the fully_freeze project’s codebases, issue trackers,
|
178
|
+
chat rooms and mailing lists is expected to follow the
|
179
|
+
[code of conduct](./CODE_OF_CONDUCT.md).
|
data/full_dup.gemspec
CHANGED
@@ -17,8 +17,8 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test)/}) }
|
18
18
|
spec.require_paths = ["lib"]
|
19
19
|
|
20
|
-
spec.add_development_dependency "rake", "
|
21
|
-
spec.add_development_dependency "bundler", "
|
20
|
+
spec.add_development_dependency "rake", ">= 12.3.3"
|
21
|
+
spec.add_development_dependency "bundler", ">= 2.1.0"
|
22
22
|
spec.add_development_dependency 'minitest', "~> 5.7"
|
23
|
-
spec.add_development_dependency '
|
23
|
+
spec.add_development_dependency 'reek', "~> 5.0.2"
|
24
24
|
end
|
data/lib/full_dup.rb
CHANGED
@@ -1,37 +1,12 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
|
+
# A deep, safe, recursive variant of the dup method. The full_dup method.
|
4
|
+
|
5
|
+
require_relative 'full_dup/use_self'
|
6
|
+
require_relative 'full_dup/use_dup'
|
3
7
|
require_relative 'full_dup/version'
|
4
8
|
require_relative 'full_dup/object'
|
5
9
|
require_relative 'full_dup/array'
|
6
10
|
require_relative 'full_dup/hash'
|
7
11
|
require_relative 'full_dup/struct'
|
8
12
|
|
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
|
data/lib/full_dup/array.rb
CHANGED
@@ -1,23 +1,26 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
|
+
# Add full_dup support to the array class.
|
3
4
|
class Array
|
4
5
|
|
5
|
-
#
|
6
|
-
def
|
7
|
-
progress[object_id] = result = dup
|
8
|
-
exclude = full_dup_exclude
|
9
|
-
|
6
|
+
# Do a full_dup with no exclusions
|
7
|
+
def full_dup_no_exclusions(progress)
|
10
8
|
each_index do |name|
|
9
|
+
value = self[name]
|
10
|
+
value = progress[value.object_id] || value.full_dup(progress)
|
11
|
+
self[name] = value
|
12
|
+
end
|
13
|
+
end
|
11
14
|
|
15
|
+
# Do a full_dup with exclusions
|
16
|
+
def full_dup_with_exclusions(progress, exclude)
|
17
|
+
each_index do |name|
|
12
18
|
unless exclude.include?(name)
|
13
|
-
value =
|
19
|
+
value = self[name]
|
14
20
|
value = progress[value.object_id] || value.full_dup(progress)
|
15
|
-
|
21
|
+
self[name] = value
|
16
22
|
end
|
17
|
-
|
18
23
|
end
|
19
|
-
|
20
|
-
result
|
21
24
|
end
|
22
25
|
|
23
26
|
end
|
data/lib/full_dup/hash.rb
CHANGED
@@ -1,23 +1,26 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
|
3
|
-
class
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
end
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
# Add full_dup support to the hash class.
|
4
|
+
class Hash
|
5
|
+
|
6
|
+
# Do a full_dup with no exclusions
|
7
|
+
def full_dup_no_exclusions(progress)
|
8
|
+
each_key do |name|
|
9
|
+
value = self[name]
|
10
|
+
value = progress[value.object_id] || value.full_dup(progress)
|
11
|
+
self[name] = value
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Do a full_dup with exclusions
|
16
|
+
def full_dup_with_exclusions(progress, exclude)
|
17
|
+
each_key do |name|
|
18
|
+
unless exclude.include?(name)
|
19
|
+
value = self[name]
|
20
|
+
value = progress[value.object_id] || value.full_dup(progress)
|
21
|
+
self[name] = value
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
data/lib/full_dup/object.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
|
+
# Add full_dup support to the object class.
|
3
4
|
class Object
|
4
5
|
|
5
6
|
#By default, no instance variables are excluded.
|
@@ -7,22 +8,38 @@ class Object
|
|
7
8
|
[]
|
8
9
|
end
|
9
10
|
|
10
|
-
#The
|
11
|
+
#The common part of the full_dup.
|
11
12
|
def full_dup(progress={})
|
12
13
|
progress[object_id] = result = dup
|
13
14
|
exclude = full_dup_exclude
|
14
15
|
|
16
|
+
if exclude.empty?
|
17
|
+
result.full_dup_no_exclusions(progress)
|
18
|
+
else
|
19
|
+
result.full_dup_with_exclusions(progress, exclude)
|
20
|
+
end
|
21
|
+
|
22
|
+
result
|
23
|
+
end
|
24
|
+
|
25
|
+
# Do a full_dup with no exclusions
|
26
|
+
def full_dup_no_exclusions(progress)
|
15
27
|
instance_variables.each do |name|
|
28
|
+
value = instance_variable_get(name)
|
29
|
+
value = progress[value.object_id] || value.full_dup(progress)
|
30
|
+
instance_variable_set(name, value)
|
31
|
+
end
|
32
|
+
end
|
16
33
|
|
34
|
+
# Do a full_dup with exclusions
|
35
|
+
def full_dup_with_exclusions(progress, exclude)
|
36
|
+
instance_variables.each do |name|
|
17
37
|
unless exclude.include?(name)
|
18
|
-
value =
|
38
|
+
value = instance_variable_get(name)
|
19
39
|
value = progress[value.object_id] || value.full_dup(progress)
|
20
|
-
|
40
|
+
instance_variable_set(name, value)
|
21
41
|
end
|
22
|
-
|
23
42
|
end
|
24
|
-
|
25
|
-
result
|
26
43
|
end
|
27
44
|
|
28
45
|
end
|
data/lib/full_dup/struct.rb
CHANGED
@@ -1,23 +1,26 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
|
+
# Add full_dup support to the struct class.
|
3
4
|
class Struct
|
4
5
|
|
5
|
-
#
|
6
|
-
def
|
7
|
-
progress[object_id] = result = dup
|
8
|
-
exclude = full_dup_exclude
|
9
|
-
|
6
|
+
# Do a full_dup with no exclusions
|
7
|
+
def full_dup_no_exclusions(progress)
|
10
8
|
members.each do |name|
|
9
|
+
value = self[name]
|
10
|
+
value = progress[value.object_id] || value.full_dup(progress)
|
11
|
+
self[name] = value
|
12
|
+
end
|
13
|
+
end
|
11
14
|
|
15
|
+
# Do a full_dup with exclusions
|
16
|
+
def full_dup_with_exclusions(progress, exclude)
|
17
|
+
members.each do |name|
|
12
18
|
unless exclude.include?(name)
|
13
|
-
value =
|
19
|
+
value = self[name]
|
14
20
|
value = progress[value.object_id] || value.full_dup(progress)
|
15
|
-
|
21
|
+
self[name] = value
|
16
22
|
end
|
17
|
-
|
18
23
|
end
|
19
|
-
|
20
|
-
result
|
21
24
|
end
|
22
25
|
|
23
26
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
# The mixin for classes where dup is just simple dup.
|
4
|
+
module FullDupDup
|
5
|
+
def full_dup(_progress=nil)
|
6
|
+
dup
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
# Strings answer dup.
|
11
|
+
class String
|
12
|
+
include FullDupDup
|
13
|
+
end
|
14
|
+
|
15
|
+
# Enumerators answer dup.
|
16
|
+
class Enumerator
|
17
|
+
include FullDupDup
|
18
|
+
end
|
19
|
+
|
20
|
+
# MatchData answer dup.
|
21
|
+
class MatchData
|
22
|
+
include FullDupDup
|
23
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
# A mixin for classes where the dup is the object itself.
|
4
|
+
module FullDupSelf
|
5
|
+
def full_dup(_progress=nil)
|
6
|
+
self
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
# Numerics answer self.
|
11
|
+
class Numeric
|
12
|
+
include FullDupSelf
|
13
|
+
end
|
14
|
+
|
15
|
+
# NilClass objects (nil) answer self.
|
16
|
+
class NilClass
|
17
|
+
include FullDupSelf
|
18
|
+
end
|
19
|
+
|
20
|
+
# TrueClass objects (true) answer self.
|
21
|
+
class TrueClass
|
22
|
+
include FullDupSelf
|
23
|
+
end
|
24
|
+
|
25
|
+
# FalseClass objects (false) answer self.
|
26
|
+
class FalseClass
|
27
|
+
include FullDupSelf
|
28
|
+
end
|
29
|
+
|
30
|
+
# Symbols answer self.
|
31
|
+
class Symbol
|
32
|
+
include FullDupSelf
|
33
|
+
end
|
34
|
+
|
35
|
+
# Regular expressions answer self.
|
36
|
+
class Regexp
|
37
|
+
include FullDupSelf
|
38
|
+
end
|
39
|
+
|
40
|
+
# Threads answer self.
|
41
|
+
class Thread
|
42
|
+
include FullDupSelf
|
43
|
+
end
|
44
|
+
|
data/lib/full_dup/version.rb
CHANGED
data/rakefile.rb
CHANGED
@@ -14,6 +14,11 @@ Rake::TestTask.new do |t|
|
|
14
14
|
t.warning = true
|
15
15
|
end
|
16
16
|
|
17
|
+
desc "Run a scan for smelly code!"
|
18
|
+
task :reek do |t|
|
19
|
+
`reek --no-color lib > reek.txt`
|
20
|
+
end
|
21
|
+
|
17
22
|
desc "Fire up an IRB session with full_dup preloaded."
|
18
23
|
task :console do
|
19
24
|
system "ruby irbt.rb local"
|
data/reek.txt
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0 total warnings
|
metadata
CHANGED
@@ -1,43 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: full_dup
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter Camilleri
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-05-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 12.3.3
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 12.3.3
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 2.1.0
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 2.1.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: minitest
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -53,19 +53,19 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '5.7'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: reek
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: 5.0.2
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
68
|
+
version: 5.0.2
|
69
69
|
description: A (safe/no exceptions) dup variant that performs a deep, recursive copy.
|
70
70
|
email:
|
71
71
|
- peter.c.camilleri@gmail.com
|
@@ -85,8 +85,11 @@ files:
|
|
85
85
|
- lib/full_dup/hash.rb
|
86
86
|
- lib/full_dup/object.rb
|
87
87
|
- lib/full_dup/struct.rb
|
88
|
+
- lib/full_dup/use_dup.rb
|
89
|
+
- lib/full_dup/use_self.rb
|
88
90
|
- lib/full_dup/version.rb
|
89
91
|
- rakefile.rb
|
92
|
+
- reek.txt
|
90
93
|
homepage: https://github.com/PeterCamilleri/full_dup
|
91
94
|
licenses:
|
92
95
|
- MIT
|
@@ -106,10 +109,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
106
109
|
- !ruby/object:Gem::Version
|
107
110
|
version: '0'
|
108
111
|
requirements: []
|
109
|
-
|
110
|
-
rubygems_version: 2.2.3
|
112
|
+
rubygems_version: 3.2.17
|
111
113
|
signing_key:
|
112
114
|
specification_version: 4
|
113
115
|
summary: A dup variant that performs a deep copy.
|
114
116
|
test_files: []
|
115
|
-
has_rdoc:
|