Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/petercamilleri/safe_dup

A safe, fast, exception free dup method that demonstrates the use of inheritance and safe monkey patching.
https://github.com/petercamilleri/safe_dup

gem ruby rubygem safe utility

Last synced: 10 days ago
JSON representation

A safe, fast, exception free dup method that demonstrates the use of inheritance and safe monkey patching.

Awesome Lists containing this project

README

        

# SafeDup

This tiny gem implements a version of dup called safe_dup. In Ruby, if an
attempt is made to dup an immutable data item like a number, an error occurs.
The justification for this uncharacteristic strictness is not at all clear, but
it does mean that the dup operation must be applied with great care.

Unlike the standard dup method, the safe\_dup method does not throw an
exception when sent to un-clonable value objects like 42 or true. These values
simply return themselves. This is deemed correct because those types of objects
are immutable and do not need to be duped. Instead of raising an exception,
the code returns the immutable object instead.

On a note about performance, this gem does not just rescue the exceptions
normally generated by dup, it prevents them from occurring and wasting time
in the first place.

Finally, this gem does not monkey patch the behavior of the dup method.
Modifying such a crucial method was considered too risky. Instead, the
safe_dup method is introduced. This is done to reduce the possibility of
breaking existing code that often occurs when monkey patching goes too far.

## Family Overview

This gem is a member of a family of four gems that all provide data copying
services in a safe, easy to use format. The following outlines the available
gems and how to chose from among them.

Depth / Action | Need to copy all. | Need to copy data only.
---------------|------------------------------|------------
Need a shallow copy | require 'safe\_clone' | require 'safe\_dup'
Need a full copy | require 'full\_clone' | require 'full\_dup'


**Notes**
* Since none of these gems override the default clone and dup
methods, the default behaviors remain available. Further, if multiple,
differing requirements exists, more than one family member gem may be
employed in the same project without fear of conflict.
* If multiple family gems are employed, they will each need to be installed and
required into the application. See below for details.
* Meta-data attributes include the frozen status and singleton methods. However
the tainted status is always copied.

## Installation

Add this line to your application's Gemfile:

gem 'safe_dup'

And then execute:

$ bundle

Or install it yourself as:

$ gem install safe_dup

The safe_dup gem is at: ( https://rubygems.org/gems/safe_dup )

The safe_clone gem is at: ( https://rubygems.org/gems/safe_clone )

The full_dup gem is at: ( https://rubygems.org/gems/full_dup )

The full_clone gem is at: ( https://rubygems.org/gems/full_clone )

## Usage

require 'safe_dup'

then, in those places where regular dup was problematic, use:

foo = my_object.safe_dup

instead of

begin
foo = my_object.dup
rescue TypeError
foo = my_object
end

It is actually pretty easy to determine where safe_dup needs to be used. It's
those places where the dup method is generating unwanted exceptions.

## Demo

A test bed for experimenting with the safe_dup gem is available as a rake task:

$ rake console

## Performance
A reasonable question to raise is "How does safe\_dup 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_ duplicated. The benchmarking code follows:

```ruby
require "benchmark/ips"
require 'safe_dup'

class Array
def use_dup
self.map do |element|
begin
element.dup
rescue TypeError
element
end
end
end

def use_safe_dup
self.map {|element| element.safe_dup }
end
end

X = ["Test", :test, 43, true, nil, false]

Benchmark.ips do |x|
x.report("Dup with standard dup method") { X.use_dup }
x.report("Dup with the safe dup method") { X.use_safe_dup }
x.compare!
end
```

#### Results: ruby 1.9.3p484 (2013-11-22) [i386-mingw32]
C:\Sites\safe_dup>ruby bench\bench.rb
Warming up --------------------------------------
Dup with standard dup method
1.245k i/100ms
Dup with the safe dup method
34.118k i/100ms
Calculating -------------------------------------
Dup with standard dup method
12.862k (± 6.2%) i/s - 64.740k
Dup with the safe dup method
533.927k (± 6.5%) i/s - 2.661M

Comparison:
Dup with the safe dup method: 533926.5 i/s
Dup with standard dup method: 12861.7 i/s - 41.51x slower

#### Results: ruby 2.1.6p336 (2015-04-13 revision 50298) [i386-mingw32]
C:\Sites\safe_dup>ruby bench\bench.rb
Warming up --------------------------------------
Dup with standard dup method
4.969k i/100ms
Dup with the safe dup method
38.025k i/100ms
Calculating -------------------------------------
Dup with standard dup method
54.786k (± 7.1%) i/s - 273.295k
Dup with the safe dup method
567.814k (±10.1%) i/s - 2.814M

Comparison:
Dup with the safe dup method: 567814.3 i/s
Dup with standard dup method: 54785.8 i/s - 10.36x slower

#### Results: ruby 2.2.3p173 (2015-08-18 revision 51636) [i386-cygwin]
$ ruby bench/bench.rb
Warming up --------------------------------------
Dup with standard dup method
3.662k i/100ms
Dup with the safe dup method
28.310k i/100ms
Calculating -------------------------------------
Dup with standard dup method
39.437k (± 6.1%) i/s - 197.748k
Dup with the safe dup method
450.558k (± 3.9%) i/s - 2.265M

Comparison:
Dup with the safe dup method: 450557.6 i/s
Dup with standard dup method: 39436.6 i/s - 11.42x slower

#### Results: ruby 2.3.3p222 (2016-11-21 revision 56859) [i386-mingw32]
Warming up --------------------------------------
Dup with standard clone method
5.866k i/100ms
Dup with the safe clone method
23.721k i/100ms
Calculating -------------------------------------
Dup with standard clone method
70.492k (± 0.4%) i/s - 357.826k in 5.076221s
Dup with the safe clone method
523.478k (± 1.1%) i/s - 2.633M in 5.030544s

Comparison:
Dup with the safe clone method: 523477.8 i/s
Dup with standard clone method: 70491.9 i/s - 7.43x slower

Overall: Shorter code _and_ faster. Winner, winner, chicken dinner!

## Contributing

#### Plan A

1. Fork it ( https://github.com/PeterCamilleri/safe_dup/fork )
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create a new Pull Request

#### Plan B

Go to the GitHub repository and raise an issue calling attention to some
aspect that could use some TLC or a suggestion or an idea.

## License

The gem is available as open source under the terms of the
[MIT License](./LICENSE.txt).

## Code of Conduct

Everyone interacting in the fully_freeze project’s codebases, issue trackers,
chat rooms and mailing lists is expected to follow the
[code of conduct](./CODE_OF_CONDUCT.md).