ofstruct 0.1.0

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