persistent_open_struct 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3ef836525502250c550a58f6988dcd6285d93337
4
+ data.tar.gz: fff2627187409c4c5109de37858a644771a722d3
5
+ SHA512:
6
+ metadata.gz: 6bd65c8ea98bcbfec8b7e0fea83a8615c6a0be59c3c77ff08326d971f278ea7b97ef7ce2a932d64e1efc4d03075e6ff85c9a026a264220a6809e850e5e8f7ea2
7
+ data.tar.gz: ceabca536ad1a1015eeb8d409b072cf4386e9b270ff4548d6633f95526039aed13789ddc0f0fa1d7a419908b6e7c89bdffedd74d05b1e6c438f32e25595babcb
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.0
4
+ - 2.1.0
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'benchmark-ips'
4
+ gem 'ofstruct'
5
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 amcaplan
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,136 @@
1
+ [![Gem Version](https://badge.fury.io/rb/persistent_open_struct.svg)](http://badge.fury.io/rb/persistent_open_struct)
2
+ ![Travis badge](https://travis-ci.org/amcaplan/persistent_open_struct.svg?branch=master)
3
+
4
+ # PersistentOpenStruct
5
+
6
+ Are you inserting data in the same format into OpenStruct again and again?
7
+ Wishing OpenStruct wouldn't have to define a new singleton method on each
8
+ individual object? Here is your solution!
9
+
10
+ PersistentOpenStruct defines methods on the class, so as long as you keep using
11
+ the same keys, no new methods will be defined. The class quickly learns the
12
+ shape of your data, so you can use it with minimal overhead. (Though of course
13
+ it's still not as fast as doing the work of defining a full-fledged class.)
14
+
15
+ It obeys the entire interface of OpenStruct, so you can insert it into your code
16
+ without problem! (Unless you're using `OpenStruct#delete_field` for some reason;
17
+ PersistentOpenStruct refuses to undefine the methods it defines.)
18
+
19
+ This gives a noticeable performance boost. Here are the results of the benchmark
20
+ found at
21
+ [`benchmark/benchmark.rb`](http://github.com/amcaplan/persistent_open_struct/blob/master/benchmark/benchmark.rb);
22
+ included are
23
+ results for [`OpenFastStruct`](http://github.com/arturoherrero/ofstruct) as
24
+ well, to get a sense of alternative solutions.
25
+
26
+ ``` ruby
27
+ require 'benchmark/ips'
28
+ require 'ostruct'
29
+ require_relative '../lib/persistent_open_struct'
30
+
31
+ Benchmark.ips do |x|
32
+ input_hash = { foo: :bar }
33
+ x.report('OpenStruct') do
34
+ OpenStruct.new(input_hash)
35
+ end
36
+
37
+ x.report('PersistentOpenStruct') do
38
+ PersistentOpenStruct.new(input_hash)
39
+ end
40
+ end
41
+
42
+ puts "\n\n"
43
+
44
+ Benchmark.ips do |x|
45
+ x.report('OpenStruct') do
46
+ OpenStruct.new.foo = :bar
47
+ end
48
+
49
+ x.report('PersistentOpenStruct') do
50
+ PersistentOpenStruct.new.foo = :bar
51
+ end
52
+ end
53
+ ```
54
+ And the results:
55
+ ```
56
+ $ ruby benchmark/benchmark.rb
57
+ Initialization benchmark
58
+
59
+ Calculating -------------------------------------
60
+ OpenStruct 17.082k i/100ms
61
+ PersistentOpenStruct 61.316k i/100ms
62
+ OpenFastStruct 62.589k i/100ms
63
+ RegularClass 122.018k i/100ms
64
+ -------------------------------------------------
65
+ OpenStruct 192.534k (± 3.4%) i/s - 973.674k
66
+ PersistentOpenStruct 955.081k (± 3.1%) i/s - 4.783M
67
+ OpenFastStruct 980.760k (± 4.2%) i/s - 4.945M
68
+ RegularClass 3.615M (± 4.4%) i/s - 18.059M
69
+
70
+
71
+ Assignment Benchmark
72
+
73
+ Calculating -------------------------------------
74
+ OpenStruct 119.904k i/100ms
75
+ PersistentOpenStruct 120.998k i/100ms
76
+ OpenFastStruct 67.418k i/100ms
77
+ RegularClass 152.935k i/100ms
78
+ -------------------------------------------------
79
+ OpenStruct 3.707M (± 4.7%) i/s - 18.585M
80
+ PersistentOpenStruct 3.753M (± 4.0%) i/s - 18.755M
81
+ OpenFastStruct 1.133M (± 2.6%) i/s - 5.731M
82
+ RegularClass 9.155M (± 4.8%) i/s - 45.728M
83
+
84
+
85
+ Access Benchmark
86
+
87
+ Calculating -------------------------------------
88
+ OpenStruct 134.887k i/100ms
89
+ PersistentOpenStruct 134.239k i/100ms
90
+ OpenFastStruct 120.480k i/100ms
91
+ RegularClass 134.040k i/100ms
92
+ -------------------------------------------------
93
+ OpenStruct 5.558M (± 3.2%) i/s - 27.787M
94
+ PersistentOpenStruct 5.531M (± 4.3%) i/s - 27.653M
95
+ OpenFastStruct 3.996M (± 4.7%) i/s - 20.000M
96
+ RegularClass 5.562M (± 3.5%) i/s - 27.880M
97
+ ```
98
+
99
+ ## Installation
100
+
101
+ Add this line to your application's Gemfile:
102
+
103
+ gem 'persistent_open_struct'
104
+
105
+ And then execute:
106
+
107
+ $ bundle
108
+
109
+ Or install it yourself as:
110
+
111
+ $ gem install persistent_open_struct
112
+
113
+ ## Usage
114
+ Note: requires Ruby 2.1.0 or above.
115
+
116
+ ``` ruby
117
+ class MyDataStructure < PersistentOpenStruct
118
+ end
119
+
120
+ datum1 = MyDataStructure.new(foo: :bar)
121
+
122
+ datum2 = MyDataStructure.new
123
+ datum2.respond_to?(:baz) #=> false
124
+ datum2.respond_to?(:foo) #=> true
125
+ ```
126
+
127
+ ## Contributing
128
+
129
+ 1. Fork it ( https://github.com/[my-github-username]/persistent_open_struct/fork )
130
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
131
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
132
+ 4. Push to the branch (`git push origin my-new-feature`)
133
+ 5. Create a new Pull Request
134
+
135
+ Pull requests will not be considered without tests of whatever you seek to
136
+ change, unless you are submitting a performance improvement.
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "rake/testtask"
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << "test"
5
+ t.test_files = FileList['test/test*.rb']
6
+ t.verbose = true
7
+ end
8
+
9
+ desc "Run tests"
10
+ task :default => :test
@@ -0,0 +1,83 @@
1
+ require 'benchmark/ips'
2
+ require 'ostruct'
3
+ require 'ofstruct'
4
+ require_relative '../lib/persistent_open_struct'
5
+
6
+ class RegularClass
7
+ attr_accessor :foo
8
+
9
+ def initialize(args)
10
+ @foo = args[:foo]
11
+ end
12
+ end
13
+
14
+ puts "Initialization benchmark\n\n"
15
+
16
+ Benchmark.ips do |x|
17
+ input_hash = { foo: :bar }
18
+ x.report('OpenStruct') do
19
+ OpenStruct.new(input_hash)
20
+ end
21
+
22
+ x.report('PersistentOpenStruct') do
23
+ PersistentOpenStruct.new(input_hash)
24
+ end
25
+
26
+ x.report('OpenFastStruct') do
27
+ OpenFastStruct.new(input_hash)
28
+ end
29
+
30
+ x.report('RegularClass') do
31
+ RegularClass.new(input_hash)
32
+ end
33
+ end
34
+
35
+ puts "\n\nAssignment Benchmark\n\n"
36
+
37
+ Benchmark.ips do |x|
38
+ os = OpenStruct.new(foo: :bar)
39
+ pos = PersistentOpenStruct.new(foo: :bar)
40
+ ofs = OpenFastStruct.new(foo: :bar)
41
+ rgc = RegularClass.new(foo: :bar)
42
+
43
+ x.report('OpenStruct') do
44
+ os.foo = :bar
45
+ end
46
+
47
+ x.report('PersistentOpenStruct') do
48
+ pos.foo = :bar
49
+ end
50
+
51
+ x.report('OpenFastStruct') do
52
+ ofs.foo = :bar
53
+ end
54
+
55
+ x.report('RegularClass') do
56
+ rgc.foo = :bar
57
+ end
58
+ end
59
+
60
+ puts "\n\nAccess Benchmark\n\n"
61
+
62
+ Benchmark.ips do |x|
63
+ os = OpenStruct.new(foo: :bar)
64
+ pos = PersistentOpenStruct.new(foo: :bar)
65
+ ofs = OpenFastStruct.new(foo: :bar)
66
+ rgc = RegularClass.new(foo: :bar)
67
+
68
+ x.report('OpenStruct') do
69
+ os.foo
70
+ end
71
+
72
+ x.report('PersistentOpenStruct') do
73
+ pos.foo
74
+ end
75
+
76
+ x.report('OpenFastStruct') do
77
+ ofs.foo
78
+ end
79
+
80
+ x.report('RegularClass') do
81
+ pos.foo
82
+ end
83
+ end
@@ -0,0 +1,17 @@
1
+ require 'ostruct'
2
+
3
+ class PersistentOpenStruct < OpenStruct
4
+ def new_ostruct_member(name)
5
+ name = name.to_sym
6
+ unless respond_to?(name)
7
+ self.class.send(:define_method, name) { @table[name] }
8
+ self.class.send(:define_method, "#{name}=") { |x| modifiable[name] = x }
9
+ end
10
+ name
11
+ end
12
+ protected :new_ostruct_member
13
+
14
+ def delete_field(name)
15
+ @table.delete(name)
16
+ end
17
+ end
@@ -0,0 +1,5 @@
1
+ require 'ostruct'
2
+
3
+ class PersistentOpenStruct < OpenStruct
4
+ VERSION = "0.0.1"
5
+ end
@@ -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 'persistent_open_struct/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "persistent_open_struct"
8
+ spec.version = PersistentOpenStruct::VERSION
9
+ spec.authors = ["amcaplan"]
10
+ spec.email = ["ariel.caplan@vitals.com"]
11
+ spec.summary = %q{A variant of OpenStruct that persists defined methods}
12
+ spec.description = %q{Unlike OpenStruct, which defines singleton methods on an object, PersistentOpenStruct defines methods on the class. This is useful when storing many hashes with the same keys as OpenStructs.}
13
+ spec.homepage = "http://github.com/amcaplan/persistent_open_struct"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.required_ruby_version = '>= 2.1.0'
22
+ spec.add_development_dependency "bundler", "~> 1.6"
23
+ spec.add_development_dependency 'rake', '~> 10.4', '>= 10.4.2'
24
+ spec.add_development_dependency 'minitest', '~> 5.4', '>= 5.4.3'
25
+ end
@@ -0,0 +1,2 @@
1
+ require 'minitest/autorun'
2
+ require 'minitest/unit'
@@ -0,0 +1,150 @@
1
+ require 'test_helper'
2
+ require 'persistent_open_struct'
3
+
4
+ class PersistentOpenStructTest < Minitest::Test
5
+ def test_initialize
6
+ h = {name: "John Smith", age: 70, pension: 300}
7
+ assert_equal h, PersistentOpenStruct.new(h).to_h
8
+ assert_equal h, PersistentOpenStruct.new(PersistentOpenStruct.new(h)).to_h
9
+ assert_equal h, PersistentOpenStruct.new(Struct.new(*h.keys).new(*h.values)).to_h
10
+ end
11
+
12
+ def test_equality
13
+ o1 = PersistentOpenStruct.new
14
+ o2 = PersistentOpenStruct.new
15
+ assert_equal(o1, o2)
16
+
17
+ o1.a = 'a'
18
+ refute_equal(o1, o2)
19
+
20
+ o2.a = 'a'
21
+ assert_equal(o1, o2)
22
+
23
+ o1.a = 'b'
24
+ refute_equal(o1, o2)
25
+
26
+ o2 = Object.new
27
+ o2.instance_eval{@table = {:a => 'b'}}
28
+ refute_equal(o1, o2)
29
+ end
30
+
31
+ def test_inspect
32
+ foo = PersistentOpenStruct.new
33
+ assert_equal("#<PersistentOpenStruct>", foo.inspect)
34
+ foo.bar = 1
35
+ foo.baz = 2
36
+ assert_equal("#<PersistentOpenStruct bar=1, baz=2>", foo.inspect)
37
+
38
+ foo = PersistentOpenStruct.new
39
+ foo.bar = PersistentOpenStruct.new
40
+ assert_equal('#<PersistentOpenStruct bar=#<PersistentOpenStruct>>', foo.inspect)
41
+ foo.bar.foo = foo
42
+ assert_equal('#<PersistentOpenStruct bar=#<PersistentOpenStruct foo=#<PersistentOpenStruct ...>>>', foo.inspect)
43
+ end
44
+
45
+ def test_frozen
46
+ o = PersistentOpenStruct.new
47
+ o.a = 'a'
48
+ o.freeze
49
+ assert_raises(RuntimeError) {o.b = 'b'}
50
+ refute_respond_to(o, :b)
51
+ assert_raises(RuntimeError) {o.a = 'z'}
52
+ assert_equal('a', o.a)
53
+ o = PersistentOpenStruct.new :a => 42
54
+ def o.frozen?; nil end
55
+ o.freeze
56
+ assert_raises(RuntimeError, '[ruby-core:22559]') {o.a = 1764}
57
+ end
58
+
59
+ def test_delete_field
60
+ o = PersistentOpenStruct.new
61
+ refute_respond_to(o, :foobar)
62
+ refute_respond_to(o, :foobar=)
63
+ o.foobar = :baz
64
+ assert_equal(o.foobar, :baz)
65
+ assert_respond_to(o, :foobar)
66
+ assert_respond_to(o, :foobar=)
67
+ o.delete_field(:foobar)
68
+ assert_equal(nil, o.foobar)
69
+ assert_respond_to(o, :foobar)
70
+ assert_respond_to(o, :foobar=)
71
+ end
72
+
73
+ def test_setter
74
+ os = PersistentOpenStruct.new
75
+ os[:foo] = :bar
76
+ assert_equal :bar, os.foo
77
+ os['foo'] = :baz
78
+ assert_equal :baz, os.foo
79
+ end
80
+
81
+ def test_getter
82
+ os = PersistentOpenStruct.new
83
+ os.foo = :bar
84
+ assert_equal :bar, os[:foo]
85
+ assert_equal :bar, os['foo']
86
+ end
87
+
88
+ def test_to_h
89
+ h = {name: "John Smith", age: 70, pension: 300}
90
+ os = PersistentOpenStruct.new(h)
91
+ to_h = os.to_h
92
+ assert_equal(h, to_h)
93
+
94
+ to_h[:age] = 71
95
+ assert_equal(70, os.age)
96
+ assert_equal(70, h[:age])
97
+
98
+ assert_equal(h, PersistentOpenStruct.new("name" => "John Smith", "age" => 70, pension: 300).to_h)
99
+ end
100
+
101
+ def test_each_pair
102
+ h = {name: "John Smith", age: 70, pension: 300}
103
+ os = PersistentOpenStruct.new(h)
104
+ assert_equal '#<Enumerator: #<PersistentOpenStruct name="John Smith", age=70, pension=300>:each_pair>', os.each_pair.inspect
105
+ assert_equal [[:name, "John Smith"], [:age, 70], [:pension, 300]], os.each_pair.to_a
106
+ assert_equal 3, os.each_pair.size
107
+ end
108
+
109
+ def test_eql_and_hash
110
+ os1 = PersistentOpenStruct.new age: 70
111
+ os2 = PersistentOpenStruct.new age: 70.0
112
+ assert_equal os1, os2
113
+ assert_equal false, os1.eql?(os2)
114
+ refute_equal os1.hash, os2.hash
115
+ assert_equal true, os1.eql?(os1.dup)
116
+ assert_equal os1.hash, os1.dup.hash
117
+ end
118
+
119
+ def test_method_missing
120
+ os = PersistentOpenStruct.new
121
+ e = assert_raises(NoMethodError) { os.foobarbaz true }
122
+ assert_equal :foobarbaz, e.name
123
+ assert_equal [true], e.args
124
+ assert_match(/#{__callee__}/, e.backtrace[0])
125
+ e = assert_raises(ArgumentError) { os.send :foobarbaz=, true, true }
126
+ assert_match(/#{__callee__}/, e.backtrace[0])
127
+ end
128
+
129
+ def test_method_reuse
130
+ os = PersistentOpenStruct.new
131
+ refute_respond_to(os, :hello)
132
+ refute_respond_to(os, :hello=)
133
+ os.hello = 'world'
134
+ os2 = PersistentOpenStruct.new
135
+ assert_respond_to(os2, :hello)
136
+ assert_respond_to(os2, :hello=)
137
+ end
138
+
139
+ PersistentOpenStructClass1 = Class.new(PersistentOpenStruct)
140
+ PersistentOpenStructClass2 = Class.new(PersistentOpenStruct)
141
+
142
+ def test_method_segregation
143
+ os1a = PersistentOpenStructClass1.new
144
+ os1a.a_thing = 'value'
145
+ os1b = PersistentOpenStructClass1.new
146
+ assert_respond_to(os1b, :a_thing)
147
+ os2 = PersistentOpenStructClass2.new
148
+ refute_respond_to(os2, :a_thing)
149
+ end
150
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: persistent_open_struct
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - amcaplan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-05-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.4'
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 10.4.2
37
+ type: :development
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '10.4'
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 10.4.2
47
+ - !ruby/object:Gem::Dependency
48
+ name: minitest
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '5.4'
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: 5.4.3
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: '5.4'
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: 5.4.3
67
+ description: Unlike OpenStruct, which defines singleton methods on an object, PersistentOpenStruct
68
+ defines methods on the class. This is useful when storing many hashes with the
69
+ same keys as OpenStructs.
70
+ email:
71
+ - ariel.caplan@vitals.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".travis.yml"
78
+ - Gemfile
79
+ - LICENSE.txt
80
+ - README.md
81
+ - Rakefile
82
+ - benchmark/benchmark.rb
83
+ - lib/persistent_open_struct.rb
84
+ - lib/persistent_open_struct/version.rb
85
+ - persistent_open_struct.gemspec
86
+ - test/test_helper.rb
87
+ - test/test_persistent_open_struct.rb
88
+ homepage: http://github.com/amcaplan/persistent_open_struct
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: 2.1.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.4.5
109
+ signing_key:
110
+ specification_version: 4
111
+ summary: A variant of OpenStruct that persists defined methods
112
+ test_files:
113
+ - test/test_helper.rb
114
+ - test/test_persistent_open_struct.rb