persistent_open_struct 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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