PersistentOpenStruct
Are you inserting data in the same format into OpenStruct again and again? Wishing OpenStruct wouldn't have to define a new singleton method on each individual object? Here is your solution!
PersistentOpenStruct defines methods on the class, so as long as you keep using the same keys, no new methods will be defined. The class quickly learns the shape of your data, so you can use it with minimal overhead. (Though of course it's still not as fast as doing the work of defining a full-fledged class.)
It obeys the entire interface of OpenStruct, so you can insert it into your code
without problem! (Unless you're using OpenStruct#delete_field for some reason;
PersistentOpenStruct refuses to undefine the methods it defines.)
This gives a noticeable performance boost. Here are the results of the benchmark
found at
benchmark/benchmark.rb;
included are
results for OpenFastStruct as
well, to get a sense of alternative solutions.
require 'benchmark/ips'
require 'ostruct'
require_relative '../lib/persistent_open_struct'
Benchmark.ips do |x|
input_hash = { foo: :bar }
x.report('OpenStruct') do
OpenStruct.new(input_hash)
end
x.report('PersistentOpenStruct') do
PersistentOpenStruct.new(input_hash)
end
end
puts "\n\n"
Benchmark.ips do |x|
x.report('OpenStruct') do
OpenStruct.new.foo = :bar
end
x.report('PersistentOpenStruct') do
PersistentOpenStruct.new.foo = :bar
end
end
And the results:
$ ruby benchmark/benchmark.rb
Initialization benchmark
Calculating -------------------------------------
OpenStruct 17.082k i/100ms
PersistentOpenStruct 61.316k i/100ms
OpenFastStruct 62.589k i/100ms
RegularClass 122.018k i/100ms
-------------------------------------------------
OpenStruct 192.534k (± 3.4%) i/s - 973.674k
PersistentOpenStruct 955.081k (± 3.1%) i/s - 4.783M
OpenFastStruct 980.760k (± 4.2%) i/s - 4.945M
RegularClass 3.615M (± 4.4%) i/s - 18.059M
Assignment Benchmark
Calculating -------------------------------------
OpenStruct 119.904k i/100ms
PersistentOpenStruct 120.998k i/100ms
OpenFastStruct 67.418k i/100ms
RegularClass 152.935k i/100ms
-------------------------------------------------
OpenStruct 3.707M (± 4.7%) i/s - 18.585M
PersistentOpenStruct 3.753M (± 4.0%) i/s - 18.755M
OpenFastStruct 1.133M (± 2.6%) i/s - 5.731M
RegularClass 9.155M (± 4.8%) i/s - 45.728M
Access Benchmark
Calculating -------------------------------------
OpenStruct 134.887k i/100ms
PersistentOpenStruct 134.239k i/100ms
OpenFastStruct 120.480k i/100ms
RegularClass 134.040k i/100ms
-------------------------------------------------
OpenStruct 5.558M (± 3.2%) i/s - 27.787M
PersistentOpenStruct 5.531M (± 4.3%) i/s - 27.653M
OpenFastStruct 3.996M (± 4.7%) i/s - 20.000M
RegularClass 5.562M (± 3.5%) i/s - 27.880M
Installation
Add this line to your application's Gemfile:
gem 'persistent_open_struct'
And then execute:
$ bundle
Or install it yourself as:
$ gem install persistent_open_struct
Usage
Note: requires Ruby 2.1.0 or above.
class MyDataStructure < PersistentOpenStruct
end
datum1 = MyDataStructure.new(foo: :bar)
datum2 = MyDataStructure.new
datum2.respond_to?(:baz) #=> false
datum2.respond_to?(:foo) #=> true
Contributing
- Fork it ( https://github.com/[my-github-username]/persistent_open_struct/fork )
- 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 a new Pull Request
Pull requests will not be considered without tests of whatever you seek to change, unless you are submitting a performance improvement.