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 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
@@ -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