SafeClone
This tiny gem implements a version of clone called safe_clone. Unlike the standard clone method, the safe_clone method does not throw an exception when sent to un-clonable value objects like 42 or true. These values simply return themselves. This is correct because those types of objects do not need to be cloned. Instead of having a fit, the code just works!
Installation
Add this line to your application's Gemfile:
gem 'safe_clone'
And then execute:
$ bundle
Or install it yourself as:
$ gem install safe_clone
Usage
require 'safe_clone'
then, in those places where regular clone was problematic, use:
foo = my_object.safe_clone
instead of
begin
foo = my_object.clone
rescue TypeError
foo = my_object
end
Performance
A reasonable question to raise is "How does safe clone compare with just catching the exception and handling it?" The benchmark sets a a realistic scenario where an array (whose contents may be varied) is having its contents cloned. The benchmarking code follows:
require "benchmark/ips"
require 'safe_clone'
class Array
def use_clone
self.map do |element|
begin
element.clone
rescue TypeError
element
end
end
end
def use_safe_clone
self.map {|element| element.safe_clone }
end
end
X = ["Test", :test, 43, true, nil, false]
Benchmark.ips do |x|
x.report("Clone with standard clone method") { X.use_clone }
x.report("Clone with the safe clone method") { X.use_safe_clone }
x.compare!
end
Ruby Version:
ruby 1.9.3p484 (2013-11-22) [i386-mingw32]
Results:
C:\Sites\safe_clone>ruby bench\bench.rb
Warming up --------------------------------------
Clone with standard clone method
1.247k i/100ms
Clone with the safe clone method
35.027k i/100ms
Calculating -------------------------------------
Clone with standard clone method
12.957k (± 5.8%) i/s - 64.844k
Clone with the safe clone method
534.740k (± 8.9%) i/s - 2.662M
Comparison:
Clone with the safe clone method: 534740.1 i/s
Clone with standard clone method: 12956.6 i/s - 41.27x slower
Ruby Version:
ruby 2.1.6p336 (2015-04-13 revision 50298) [i386-mingw32]
Results:
C:\Sites\safe_clone>ruby bench\bench.rb
Warming up --------------------------------------
Clone with standard clone method
4.945k i/100ms
Clone with the safe clone method
38.109k i/100ms
Calculating -------------------------------------
Clone with standard clone method
54.491k (± 7.3%) i/s - 271.975k
Clone with the safe clone method
569.236k (±10.2%) i/s - 2.820M
Comparison:
Clone with the safe clone method: 569236.4 i/s
Clone with standard clone method: 54491.3 i/s - 10.45x slower
Ruby Version:
ruby 2.2.3p173 (2015-08-18 revision 51636) [i386-cygwin]
Results:
Peter Camilleri@NCC1701G /cygdrive/c/sites/safe_clone
$ ruby bench/bench.rb
Warming up --------------------------------------
Clone with standard clone method
3.698k i/100ms
Clone with the safe clone method
28.999k i/100ms
Calculating -------------------------------------
Clone with standard clone method
40.076k (± 5.1%) i/s - 203.390k
Clone with the safe clone method
481.524k (±10.0%) i/s - 2.407M
Comparison:
Clone with the safe clone method: 481524.1 i/s
Clone with standard clone method: 40075.6 i/s - 12.02x slower
Overall: Shorter code and faster. Winner, winner, chicken dinner!
Contributing
- Fork it
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create new Pull Request