persistent_open_struct 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3ef836525502250c550a58f6988dcd6285d93337
4
- data.tar.gz: fff2627187409c4c5109de37858a644771a722d3
3
+ metadata.gz: c56926b84113f349bff3d7626376aa08f3bacc67
4
+ data.tar.gz: 52d2e51a904300c09ddff7cd66500a3986772c1a
5
5
  SHA512:
6
- metadata.gz: 6bd65c8ea98bcbfec8b7e0fea83a8615c6a0be59c3c77ff08326d971f278ea7b97ef7ce2a932d64e1efc4d03075e6ff85c9a026a264220a6809e850e5e8f7ea2
7
- data.tar.gz: ceabca536ad1a1015eeb8d409b072cf4386e9b270ff4548d6633f95526039aed13789ddc0f0fa1d7a419908b6e7c89bdffedd74d05b1e6c438f32e25595babcb
6
+ metadata.gz: c572c33cf67151da6da095d47c55d758bc49ffc21db1bdda311518629f7ac2e12eabc9383e3a0f309b059d603a9371ef748d16acbe0de4bebd9af96238a97c61
7
+ data.tar.gz: bb0dc99765a194859a37ed1e8e220250a9891013257a64d93b8eb1a7b64b2a033410f220d7725b709e8b409a935810706ffce356b0cf2a7bc4abe6575e7c4f4b
data/Gemfile CHANGED
@@ -1,5 +1,4 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem 'benchmark-ips'
4
3
  gem 'ofstruct'
5
4
  gemspec
data/README.md CHANGED
@@ -3,97 +3,113 @@
3
3
 
4
4
  # PersistentOpenStruct
5
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
6
+ Are you inserting data in the same format into `OpenStruct` again and again?
7
+ Wish `OpenStruct` wouldn't have to define a new singleton method on each
8
8
  individual object? Here is your solution!
9
9
 
10
- PersistentOpenStruct defines methods on the class, so as long as you keep using
10
+ `PersistentOpenStruct` defines methods on the class, so as long as you keep using
11
11
  the same keys, no new methods will be defined. The class quickly learns the
12
12
  shape of your data, so you can use it with minimal overhead. (Though of course
13
13
  it's still not as fast as doing the work of defining a full-fledged class.)
14
14
 
15
- It obeys the entire interface of OpenStruct, so you can insert it into your code
15
+ It obeys the entire interface of `OpenStruct`, so you can insert it into your code
16
16
  without problem! (Unless you're using `OpenStruct#delete_field` for some reason;
17
- PersistentOpenStruct refuses to undefine the methods it defines.)
17
+ `PersistentOpenStruct` refuses to undefine the methods it defines.)
18
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.
19
+ This gives a noticeable performance boost. Here are the results of the
20
+ benchmark found at
21
+ [`benchmark/benchmark.rb`](http://github.com/amcaplan/persistent_open_struct/blob/master/benchmark/benchmark.rb)
22
+ run on Ruby 2.3.1; the final benchmark is most representative of the average case.
25
23
 
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
24
+ Also included are results for
25
+ [`OpenFastStruct`](http://github.com/arturoherrero/ofstruct) as well, to get a
26
+ sense of alternative solutions.
41
27
 
42
- puts "\n\n"
28
+ More is better.
43
29
 
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
30
  ```
54
- And the results:
55
- ```
56
- $ ruby benchmark/benchmark.rb
31
+ $ ruby benchmark/benchmark.rb
57
32
  Initialization benchmark
58
33
 
34
+ Warming up --------------------------------------
35
+ OpenStruct 88.289k i/100ms
36
+ PersistentOpenStruct 78.440k i/100ms
37
+ OpenFastStruct 81.306k i/100ms
38
+ RegularClass 200.536k i/100ms
59
39
  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
40
+ OpenStruct 981.150k (± 7.8%) i/s - 4.944M in 5.069950s
41
+ PersistentOpenStruct 898.432k (± 9.5%) i/s - 4.471M in 5.022044s
42
+ OpenFastStruct 1.059M (± 5.6%) i/s - 5.366M in 5.086061s
43
+ RegularClass 3.860M (± 9.6%) i/s - 19.251M in 5.034804s
44
+
45
+ Comparison:
46
+ RegularClass: 3859650.0 i/s
47
+ OpenFastStruct: 1058578.4 i/s - 3.65x slower
48
+ OpenStruct: 981149.5 i/s - 3.93x slower
49
+ PersistentOpenStruct: 898431.6 i/s - 4.30x slower
50
+
69
51
 
70
52
 
71
53
  Assignment Benchmark
72
54
 
55
+ Warming up --------------------------------------
56
+ OpenStruct 199.451k i/100ms
57
+ PersistentOpenStruct 214.181k i/100ms
58
+ OpenFastStruct 99.324k i/100ms
59
+ RegularClass 312.190k i/100ms
73
60
  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
61
+ OpenStruct 4.505M (± 5.4%) i/s - 22.538M in 5.019146s
62
+ PersistentOpenStruct 4.375M (± 4.2%) i/s - 21.846M in 5.002085s
63
+ OpenFastStruct 1.405M (± 5.0%) i/s - 7.052M in 5.033620s
64
+ RegularClass 11.113M (± 5.2%) i/s - 55.570M in 5.015664s
65
+
66
+ Comparison:
67
+ RegularClass: 11112511.1 i/s
68
+ OpenStruct: 4504735.3 i/s - 2.47x slower
69
+ PersistentOpenStruct: 4375412.9 i/s - 2.54x slower
70
+ OpenFastStruct: 1404724.1 i/s - 7.91x slower
71
+
83
72
 
84
73
 
85
74
  Access Benchmark
86
75
 
76
+ Warming up --------------------------------------
77
+ OpenStruct 256.277k i/100ms
78
+ PersistentOpenStruct 259.536k i/100ms
79
+ OpenFastStruct 227.602k i/100ms
80
+ RegularClass 260.242k i/100ms
81
+ Calculating -------------------------------------
82
+ OpenStruct 6.798M (± 5.0%) i/s - 34.085M in 5.027391s
83
+ PersistentOpenStruct 6.539M (± 6.0%) i/s - 32.702M in 5.019393s
84
+ OpenFastStruct 4.875M (± 4.0%) i/s - 24.353M in 5.004194s
85
+ RegularClass 6.654M (± 4.7%) i/s - 33.311M in 5.018183s
86
+
87
+ Comparison:
88
+ OpenStruct: 6797834.1 i/s
89
+ RegularClass: 6653907.4 i/s - same-ish: difference falls within error
90
+ PersistentOpenStruct: 6538883.0 i/s - same-ish: difference falls within error
91
+ OpenFastStruct: 4875059.7 i/s - 1.39x slower
92
+
93
+
94
+
95
+ All-Together benchmark
96
+
97
+ Warming up --------------------------------------
98
+ OpenStruct 14.490k i/100ms
99
+ PersistentOpenStruct 63.043k i/100ms
100
+ OpenFastStruct 47.777k i/100ms
101
+ RegularClass 197.293k i/100ms
87
102
  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
103
+ OpenStruct 155.130k (± 4.5%) i/s - 782.460k in 5.054693s
104
+ PersistentOpenStruct 764.359k (± 6.6%) i/s - 3.846M in 5.053760s
105
+ OpenFastStruct 546.809k (± 4.7%) i/s - 2.771M in 5.079275s
106
+ RegularClass 3.674M (± 9.1%) i/s - 18.348M in 5.054680s
107
+
108
+ Comparison:
109
+ RegularClass: 3673779.6 i/s
110
+ PersistentOpenStruct: 764359.4 i/s - 4.81x slower
111
+ OpenFastStruct: 546808.8 i/s - 6.72x slower
112
+ OpenStruct: 155130.1 i/s - 23.68x slower
97
113
  ```
98
114
 
99
115
  ## Installation
@@ -126,11 +142,12 @@ datum2.respond_to?(:foo) #=> true
126
142
 
127
143
  ## Contributing
128
144
 
129
- 1. Fork it ( https://github.com/[my-github-username]/persistent_open_struct/fork )
145
+ 1. Fork it ( https://github.com/amcaplan/persistent_open_struct/fork )
130
146
  2. Create your feature branch (`git checkout -b my-new-feature`)
131
147
  3. Commit your changes (`git commit -am 'Add some feature'`)
132
148
  4. Push to the branch (`git push origin my-new-feature`)
133
149
  5. Create a new Pull Request
134
150
 
135
- Pull requests will not be considered without tests of whatever you seek to
136
- change, unless you are submitting a performance improvement.
151
+ Changes to functionality require testing. Performance improvements should
152
+ include before/after benchmarks (or ideally just update the results in the
153
+ README above).
@@ -30,6 +30,8 @@ Benchmark.ips do |x|
30
30
  x.report('RegularClass') do
31
31
  RegularClass.new(input_hash)
32
32
  end
33
+
34
+ x.compare!
33
35
  end
34
36
 
35
37
  puts "\n\nAssignment Benchmark\n\n"
@@ -55,6 +57,8 @@ Benchmark.ips do |x|
55
57
  x.report('RegularClass') do
56
58
  rgc.foo = :bar
57
59
  end
60
+
61
+ x.compare!
58
62
  end
59
63
 
60
64
  puts "\n\nAccess Benchmark\n\n"
@@ -80,4 +84,37 @@ Benchmark.ips do |x|
80
84
  x.report('RegularClass') do
81
85
  pos.foo
82
86
  end
87
+
88
+ x.compare!
89
+ end
90
+
91
+ puts "\n\nAll-Together benchmark\n\n"
92
+
93
+ Benchmark.ips do |x|
94
+ input_hash = { foo: :bar }
95
+ x.report('OpenStruct') do
96
+ os = OpenStruct.new(input_hash)
97
+ os.foo = :bar
98
+ os.foo
99
+ end
100
+
101
+ x.report('PersistentOpenStruct') do
102
+ pos = PersistentOpenStruct.new(input_hash)
103
+ pos.foo = :bar
104
+ pos.foo
105
+ end
106
+
107
+ x.report('OpenFastStruct') do
108
+ ofs = OpenFastStruct.new(input_hash)
109
+ ofs.foo = :bar
110
+ ofs.foo
111
+ end
112
+
113
+ x.report('RegularClass') do
114
+ rgc = RegularClass.new(input_hash)
115
+ rgc.foo = :bar
116
+ rgc.foo
117
+ end
118
+
119
+ x.compare!
83
120
  end
@@ -1,6 +1,8 @@
1
- require 'ostruct'
1
+ class PersistentOpenStruct
2
+ # The following 2 methods are altered from the original OpenStruct.
3
+ # Everything else is copied from OpenStruct on Ruby 2.2. (The performance
4
+ # enhancements in 2.3 don't make sense for this implementation.)
2
5
 
3
- class PersistentOpenStruct < OpenStruct
4
6
  def new_ostruct_member(name)
5
7
  name = name.to_sym
6
8
  unless respond_to?(name)
@@ -14,4 +16,119 @@ class PersistentOpenStruct < OpenStruct
14
16
  def delete_field(name)
15
17
  @table.delete(name)
16
18
  end
19
+
20
+ # From here on was copied from https://github.com/ruby/ruby/blob/3c7a96bfa2d21336d985ceda544c4ccabafebed5/lib/ostruct.rb
21
+ # to avoid changes to internals negatively affecting performance.
22
+
23
+ def initialize(hash=nil)
24
+ @table = {}
25
+ if hash
26
+ hash.each_pair do |k, v|
27
+ k = k.to_sym
28
+ @table[k] = v
29
+ new_ostruct_member(k)
30
+ end
31
+ end
32
+ end
33
+
34
+ def initialize_copy(orig)
35
+ super
36
+ @table = @table.dup
37
+ @table.each_key{|key| new_ostruct_member(key)}
38
+ end
39
+
40
+ def to_h
41
+ @table.dup
42
+ end
43
+
44
+ def each_pair
45
+ return to_enum(__method__) { @table.size } unless block_given?
46
+ @table.each_pair{|p| yield p}
47
+ end
48
+
49
+ def marshal_dump
50
+ @table
51
+ end
52
+
53
+ def marshal_load(x)
54
+ @table = x
55
+ @table.each_key{|key| new_ostruct_member(key)}
56
+ end
57
+
58
+ def modifiable
59
+ begin
60
+ @modifiable = true
61
+ rescue
62
+ raise RuntimeError, "can't modify frozen #{self.class}", caller(3)
63
+ end
64
+ @table
65
+ end
66
+ protected :modifiable
67
+
68
+ def method_missing(mid, *args) # :nodoc:
69
+ mname = mid.id2name
70
+ len = args.length
71
+ if mname.chomp!('=')
72
+ if len != 1
73
+ raise ArgumentError, "wrong number of arguments (#{len} for 1)", caller(1)
74
+ end
75
+ modifiable[new_ostruct_member(mname)] = args[0]
76
+ elsif len == 0
77
+ @table[mid]
78
+ else
79
+ err = NoMethodError.new "undefined method `#{mid}' for #{self}", mid, args
80
+ err.set_backtrace caller(1)
81
+ raise err
82
+ end
83
+ end
84
+
85
+ def [](name)
86
+ @table[name.to_sym]
87
+ end
88
+
89
+ def []=(name, value)
90
+ modifiable[new_ostruct_member(name)] = value
91
+ end
92
+
93
+ InspectKey = :__inspect_key__ # :nodoc:
94
+
95
+ def inspect
96
+ str = "#<#{self.class}"
97
+
98
+ ids = (Thread.current[InspectKey] ||= [])
99
+ if ids.include?(object_id)
100
+ return str << ' ...>'
101
+ end
102
+
103
+ ids << object_id
104
+ begin
105
+ first = true
106
+ for k,v in @table
107
+ str << "," unless first
108
+ first = false
109
+ str << " #{k}=#{v.inspect}"
110
+ end
111
+ return str << '>'
112
+ ensure
113
+ ids.pop
114
+ end
115
+ end
116
+ alias :to_s :inspect
117
+
118
+ attr_reader :table # :nodoc:
119
+ protected :table
120
+
121
+ def ==(other)
122
+ return false unless other.kind_of?(OpenStruct)
123
+ @table == other.table
124
+ end
125
+
126
+ def eql?(other)
127
+ return false unless other.kind_of?(OpenStruct)
128
+ @table.eql?(other.table)
129
+ end
130
+
131
+ def hash
132
+ @table.hash
133
+ end
17
134
  end
@@ -1,5 +1,5 @@
1
1
  require 'ostruct'
2
2
 
3
3
  class PersistentOpenStruct < OpenStruct
4
- VERSION = "0.0.1"
4
+ VERSION = "0.0.2"
5
5
  end
@@ -22,4 +22,5 @@ Gem::Specification.new do |spec|
22
22
  spec.add_development_dependency "bundler", "~> 1.6"
23
23
  spec.add_development_dependency 'rake', '~> 10.4', '>= 10.4.2'
24
24
  spec.add_development_dependency 'minitest', '~> 5.4', '>= 5.4.3'
25
+ spec.add_development_dependency 'benchmark-ips', '~> 2.6'
25
26
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: persistent_open_struct
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - amcaplan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-14 00:00:00.000000000 Z
11
+ date: 2016-07-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -64,6 +64,20 @@ dependencies:
64
64
  - - ">="
65
65
  - !ruby/object:Gem::Version
66
66
  version: 5.4.3
67
+ - !ruby/object:Gem::Dependency
68
+ name: benchmark-ips
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '2.6'
74
+ type: :development
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: '2.6'
67
81
  description: Unlike OpenStruct, which defines singleton methods on an object, PersistentOpenStruct
68
82
  defines methods on the class. This is useful when storing many hashes with the
69
83
  same keys as OpenStructs.
@@ -105,7 +119,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
105
119
  version: '0'
106
120
  requirements: []
107
121
  rubyforge_project:
108
- rubygems_version: 2.4.5
122
+ rubygems_version: 2.5.1
109
123
  signing_key:
110
124
  specification_version: 4
111
125
  summary: A variant of OpenStruct that persists defined methods