ofstruct 0.1.0
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 +7 -0
- data/LICENSE +21 -0
- data/README.md +111 -0
- data/lib/ofstruct.rb +52 -0
- data/spec/lib/ofstruct_spec.rb +125 -0
- data/spec/spec_helper.rb +18 -0
- metadata +106 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 4affb006d45fd6c59e88924ba63450b0e6b6d96b
|
|
4
|
+
data.tar.gz: 50be84fa6237ee7bb9fad0136dee2388b5499a86
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 604e21308a123f6bffb74c11c338966bade15f65ded6f525cf4b553b76bc98801d586dd8d8827ed4c87c069dfced7bf5d0869bd2433574f342f2ab98ed5c8400
|
|
7
|
+
data.tar.gz: 6f1049641791961250cb4e82a71d4d596df361493be598ffda3bda86843c13233b961fa8929de70ec61bf6263def1276a41510d8588c5a0dac1dc60afb500e89
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) Arturo Herrero, http://arturoherrero.com/
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# OpenFastStruct
|
|
2
|
+
|
|
3
|
+
OpenStruct allows the creation of data objects with arbitrary attributes.
|
|
4
|
+
|
|
5
|
+
**OpenFastStruct** is a data structure, similar to an OpenStruct, that allows the
|
|
6
|
+
definition of arbitrary attributes with their accompanying values. It benchmarks
|
|
7
|
+
~3x slower than a Hash, but it's ~4x faster than OpenStruct.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## Examples
|
|
11
|
+
|
|
12
|
+
### Basic usage
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
require "ofstruct"
|
|
16
|
+
|
|
17
|
+
person = OpenFastStruct.new
|
|
18
|
+
person.name = "John Smith"
|
|
19
|
+
person.age = 70
|
|
20
|
+
|
|
21
|
+
puts person.name # -> "John Smith"
|
|
22
|
+
puts person.age # -> 70
|
|
23
|
+
puts person.address # -> #<OpenFastStruct>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Initialize and update from a Hash
|
|
27
|
+
|
|
28
|
+
An OpenFastStruct employs a Hash internally to store the methods and values and
|
|
29
|
+
can even be initialized or updated with one:
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
require "ofstruct"
|
|
33
|
+
|
|
34
|
+
person = OpenFastStruct.new(:name => "John Smith")
|
|
35
|
+
puts person.name # -> "John Smith"
|
|
36
|
+
|
|
37
|
+
person.update(:name => "David Smith", :age => 70)
|
|
38
|
+
puts person.name # -> "David Smith"
|
|
39
|
+
puts person.age # -> 70
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Remove attributes
|
|
43
|
+
|
|
44
|
+
Removing the presence of a method requires the execution the `#delete_field`
|
|
45
|
+
method as setting the property value to a new empty OpenFastStruct.
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
require "ofstruct"
|
|
49
|
+
|
|
50
|
+
person = OpenFastStruct.new
|
|
51
|
+
person.name = "John Smith"
|
|
52
|
+
person.delete_field(:name)
|
|
53
|
+
puts person.name # -> #<OpenFastStruct>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### *Black hole* object
|
|
57
|
+
|
|
58
|
+
An OpenFastStruct instance is a *black hole* object that supports infinite
|
|
59
|
+
chaining of attributes.
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
require "ofstruct"
|
|
63
|
+
|
|
64
|
+
person = OpenFastStruct.new
|
|
65
|
+
person.address.number = 4
|
|
66
|
+
puts person.address.number # -> 4
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
## Benchmarks
|
|
71
|
+
|
|
72
|
+
Probably you heard that you should never, ever use OpenStruct because the
|
|
73
|
+
performance penalty is prohibitive. You can use OpenFastStruct instead!
|
|
74
|
+
|
|
75
|
+
### Comparation between Hash, OpenFastStruct and OpenStruct:
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
$ ruby benchmark/hash_ofstruct_ostruct.rb
|
|
79
|
+
Calculating -------------------------------------
|
|
80
|
+
Hash 25.518k i/100ms
|
|
81
|
+
OpenFastStruct 10.527k i/100ms
|
|
82
|
+
OpenStruct 3.236k i/100ms
|
|
83
|
+
-------------------------------------------------
|
|
84
|
+
Hash 487.517k (±11.9%) i/s - 2.399M
|
|
85
|
+
OpenFastStruct 159.952k (± 4.0%) i/s - 800.052k
|
|
86
|
+
OpenStruct 45.602k (± 4.7%) i/s - 229.756k
|
|
87
|
+
|
|
88
|
+
Comparison:
|
|
89
|
+
Hash: 487516.9 i/s
|
|
90
|
+
OpenFastStruct: 159952.4 i/s - 3.05x slower
|
|
91
|
+
OpenStruct: 45601.6 i/s - 10.69x slower
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Comparation between RSpec Stubs, OpenFastStruct and OpenStruct:
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
$ ruby benchmark/double_ofstruct_ostruct.rb
|
|
98
|
+
Calculating -------------------------------------
|
|
99
|
+
RSpec Stubs 103.000 i/100ms
|
|
100
|
+
OpenFastStruct 108.000 i/100ms
|
|
101
|
+
OpenStruct 45.000 i/100ms
|
|
102
|
+
-------------------------------------------------
|
|
103
|
+
RSpec Stubs 297.809 (±17.1%) i/s - 1.545k in 5.346697s
|
|
104
|
+
OpenFastStruct 262.381 (±12.2%) i/s - 1.404k in 5.430345s
|
|
105
|
+
OpenStruct 185.150 (± 7.0%) i/s - 945.000
|
|
106
|
+
|
|
107
|
+
Comparison:
|
|
108
|
+
RSpec Stubs: 297.8 i/s
|
|
109
|
+
OpenFastStruct: 262.4 i/s - 1.14x slower
|
|
110
|
+
OpenStruct: 185.2 i/s - 1.61x slower
|
|
111
|
+
```
|
data/lib/ofstruct.rb
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
class OpenFastStruct
|
|
2
|
+
def initialize(args = {})
|
|
3
|
+
@members = {}
|
|
4
|
+
assign(args)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def delete_field(key)
|
|
8
|
+
@members[key.to_sym] = self.class.new
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def each_pair
|
|
12
|
+
@members.each_pair
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def update(args)
|
|
16
|
+
assign(args)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def to_h
|
|
20
|
+
@members
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def inspect
|
|
24
|
+
@members.each_with_object("#<#{self.class}") { |(k,v), output| output << " :#{k}=#{v.inspect}" } << ">"
|
|
25
|
+
end
|
|
26
|
+
alias :to_s :inspect
|
|
27
|
+
|
|
28
|
+
def to_ary
|
|
29
|
+
nil
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def method_missing(name, *args)
|
|
35
|
+
@members.fetch(name) do
|
|
36
|
+
if name[-1] == "="
|
|
37
|
+
@members[name[0..-2].to_sym] = args.first
|
|
38
|
+
else
|
|
39
|
+
@members[name.to_sym] = self.class.new
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def assign(args)
|
|
45
|
+
ensure_hash(args)
|
|
46
|
+
args.each { |k,v| @members[k.to_sym] = v }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def ensure_hash(args)
|
|
50
|
+
raise ArgumentError unless args.is_a?(Hash)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
require "ofstruct"
|
|
2
|
+
|
|
3
|
+
RSpec.describe OpenFastStruct do
|
|
4
|
+
subject(:ofstruct) { described_class.new }
|
|
5
|
+
|
|
6
|
+
it "can be instantiated with no arguments" do
|
|
7
|
+
expect(ofstruct).to be_a(described_class)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "works accessor" do
|
|
11
|
+
ofstruct.name = "John"
|
|
12
|
+
expect(ofstruct.name).to eq("John")
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "works recursive accessor" do
|
|
16
|
+
ofstruct.user.name = "John"
|
|
17
|
+
ofstruct.name = "OpenFastStruct"
|
|
18
|
+
expect(ofstruct.user).to be_a(described_class)
|
|
19
|
+
expect(ofstruct.user.name).to eq("John")
|
|
20
|
+
expect(ofstruct.name).to eq("OpenFastStruct")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "works with unexisting values" do
|
|
24
|
+
expect(ofstruct.name).to be_a(described_class)
|
|
25
|
+
expect(ofstruct.nam).to be_a(described_class)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
context "instantiated from arguments" do
|
|
29
|
+
subject(:ofstruct) { described_class.new(args) }
|
|
30
|
+
|
|
31
|
+
context "without Hash" do
|
|
32
|
+
let(:args) { double }
|
|
33
|
+
|
|
34
|
+
it "raises an exception" do
|
|
35
|
+
expect { ofstruct }.to raise_error(ArgumentError)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
context "with Hash" do
|
|
40
|
+
let(:symbol_args) { Hash[:name => "John"] }
|
|
41
|
+
let(:string_args) { Hash["name" => "John"] }
|
|
42
|
+
|
|
43
|
+
context "with Symbol keys" do
|
|
44
|
+
let(:args) { symbol_args }
|
|
45
|
+
|
|
46
|
+
it "works with reader" do
|
|
47
|
+
expect(ofstruct.name).to eq("John")
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "works accessor" do
|
|
51
|
+
ofstruct.name = "John Smith"
|
|
52
|
+
expect(ofstruct.name).to eq("John Smith")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
describe "#delete_field" do
|
|
56
|
+
it "removes the named key" do
|
|
57
|
+
ofstruct.delete_field(:name)
|
|
58
|
+
expect(ofstruct.name).to be_a(described_class)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
describe "#each_field" do
|
|
63
|
+
it "yields all keys with the values" do
|
|
64
|
+
expect(ofstruct.each_pair.to_a).to eq([[:name, "John"]])
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it "returns an enumerator" do
|
|
68
|
+
expect(ofstruct.each_pair).to be_a(Enumerator)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
describe "#update" do
|
|
73
|
+
it "overwrite previous keys" do
|
|
74
|
+
ofstruct.update(:name => "John Smith")
|
|
75
|
+
expect(ofstruct.name).to eq("John Smith")
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it "adds new keys" do
|
|
79
|
+
ofstruct.update(:surname => "Smith")
|
|
80
|
+
expect(ofstruct.name).to eq("John")
|
|
81
|
+
expect(ofstruct.surname).to eq("Smith")
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
context "without Hash" do
|
|
85
|
+
it "raises an exception" do
|
|
86
|
+
expect { ofstruct.update(double) }.to raise_error(ArgumentError)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
describe "#to_h" do
|
|
92
|
+
it "converts to a hash" do
|
|
93
|
+
expect(ofstruct.to_h).to eq(args)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
describe "#inspect" do
|
|
98
|
+
it "returns the human-readable representation" do
|
|
99
|
+
expect(ofstruct.inspect).to eq('#<OpenFastStruct :name="John">')
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
context "with String keys" do
|
|
105
|
+
let(:args) { string_args }
|
|
106
|
+
|
|
107
|
+
it "works with reader" do
|
|
108
|
+
expect(ofstruct.name).to eq("John")
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
describe "#to_h" do
|
|
112
|
+
it "converts to a hash with keys as symbols" do
|
|
113
|
+
expect(ofstruct.to_h).to eq(symbol_args)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
describe "#inspect" do
|
|
118
|
+
it "returns the human-readable representation" do
|
|
119
|
+
expect(ofstruct.inspect).to eq('#<OpenFastStruct :name="John">')
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
RSpec.configure do |config|
|
|
2
|
+
config.expect_with :rspec do |expectations|
|
|
3
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
config.mock_with :rspec do |mocks|
|
|
7
|
+
mocks.verify_partial_doubles = true
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
config.disable_monkey_patching!
|
|
11
|
+
|
|
12
|
+
if config.files_to_run.one?
|
|
13
|
+
config.default_formatter = 'doc'
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
config.order = :random
|
|
17
|
+
Kernel.srand config.seed
|
|
18
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: ofstruct
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Arturo Herrero
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2015-03-26 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: benchmark-ips
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '2.1'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '2.1'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: guard-rspec
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '4.5'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '4.5'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: rake
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '10.4'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '10.4'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rspec
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '3.2'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '3.2'
|
|
69
|
+
description: OpenFastStruct is a data structure, similar to an OpenStruct but faster.
|
|
70
|
+
email: arturo.herrero@gmail.com
|
|
71
|
+
executables: []
|
|
72
|
+
extensions: []
|
|
73
|
+
extra_rdoc_files: []
|
|
74
|
+
files:
|
|
75
|
+
- LICENSE
|
|
76
|
+
- README.md
|
|
77
|
+
- lib/ofstruct.rb
|
|
78
|
+
- spec/lib/ofstruct_spec.rb
|
|
79
|
+
- spec/spec_helper.rb
|
|
80
|
+
homepage: https://github.com/arturoherrero/ofstruct
|
|
81
|
+
licenses:
|
|
82
|
+
- MIT
|
|
83
|
+
metadata: {}
|
|
84
|
+
post_install_message:
|
|
85
|
+
rdoc_options: []
|
|
86
|
+
require_paths:
|
|
87
|
+
- lib
|
|
88
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
89
|
+
requirements:
|
|
90
|
+
- - ">="
|
|
91
|
+
- !ruby/object:Gem::Version
|
|
92
|
+
version: 2.0.0
|
|
93
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
94
|
+
requirements:
|
|
95
|
+
- - ">="
|
|
96
|
+
- !ruby/object:Gem::Version
|
|
97
|
+
version: '0'
|
|
98
|
+
requirements: []
|
|
99
|
+
rubyforge_project:
|
|
100
|
+
rubygems_version: 2.4.5
|
|
101
|
+
signing_key:
|
|
102
|
+
specification_version: 4
|
|
103
|
+
summary: OpenFastStruct
|
|
104
|
+
test_files:
|
|
105
|
+
- spec/lib/ofstruct_spec.rb
|
|
106
|
+
- spec/spec_helper.rb
|