fast_ostruct 1.0.0 → 1.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 +4 -4
- data/CHANGELOG.md +34 -0
- data/Gemfile +11 -0
- data/README.md +38 -24
- data/fast_ostruct.gemspec +0 -8
- data/lib/fast_ostruct/config.rb +12 -0
- data/lib/fast_ostruct/version.rb +5 -0
- data/lib/fast_ostruct.rb +32 -16
- data/lib/tasks/benchmark.rake +22 -9
- metadata +5 -100
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 46b3703a02825829511af5040efa372482bdde0ea0df6ed66e3cd547ead5db37
|
4
|
+
data.tar.gz: e172571b4121f06c48a8042f1207c76734fadf9c80d41570798eb5eae668fb38
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 38f59dfe9385fd2d596520255a9db8a24997b16e947c117ccc827dc2fbd2c867f264a7e1870ff2dbd92f2c11e69949bfc95bbd2962e97b819680d1defbcb2fbb
|
7
|
+
data.tar.gz: 06db7c8fa97ad0c0f8d64dc8c2576ad8cefdfcb3c3e46df48295ab1af2355046c750c55477a08882a2e3592f07f1bf553213ba6efa71c03bd45b8b1ff7c55df4
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
|
+
|
8
|
+
## [Unreleased]
|
9
|
+
|
10
|
+
## [1.1.0] - 2023-08-16
|
11
|
+
|
12
|
+
### Added
|
13
|
+
|
14
|
+
- Configuration file
|
15
|
+
- Changelog file
|
16
|
+
- New method `slice` to slice attributes from FastOpenStruct
|
17
|
+
|
18
|
+
### Fixed
|
19
|
+
|
20
|
+
- Fixed bug when using `save` method with attributes (it happens when using as FactoryBot class). Thanks to [Andre Cesar Gomes](https://github.com/andrecego) for reporting this issue.
|
21
|
+
|
22
|
+
### Changed
|
23
|
+
|
24
|
+
- Readme file updated with new features documentation
|
25
|
+
|
26
|
+
### Removed
|
27
|
+
|
28
|
+
|
29
|
+
|
30
|
+
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
|
data/Gemfile
CHANGED
@@ -3,4 +3,15 @@
|
|
3
3
|
source 'https://rubygems.org'
|
4
4
|
|
5
5
|
# Specify your gem's dependencies in fast_open_struct.gemspec.
|
6
|
+
|
7
|
+
group :development, :test do
|
8
|
+
gem 'benchmark-ips', '>= 2'
|
9
|
+
gem 'pry', '>= 0.14'
|
10
|
+
gem 'rack-test', '~> 1.1'
|
11
|
+
gem 'rake', '~> 13.0'
|
12
|
+
gem 'rspec', '~> 3.0'
|
13
|
+
gem 'rubocop', '~> 1.25'
|
14
|
+
gem 'rubocop-performance', '~> 1.15'
|
15
|
+
end
|
16
|
+
|
6
17
|
gemspec
|
data/README.md
CHANGED
@@ -56,65 +56,79 @@ my_struct['lastname'] # => 'Connor'
|
|
56
56
|
my_struct.dig(:age) # => 30
|
57
57
|
```
|
58
58
|
|
59
|
+
## Configuration
|
60
|
+
|
61
|
+
Create an initializer file and add the following code:
|
62
|
+
```ruby
|
63
|
+
FastOpenStruct.configure do |config|
|
64
|
+
# default: true | it will make a deep initialization of the attributes, converting Hash to FastOpenStruct
|
65
|
+
config.initialize_options = { deep_initialize: true }
|
66
|
+
|
67
|
+
# default: false | if true, it will symbolize the keys of the attributes
|
68
|
+
config.attributes_options = { symbolize_keys: false }
|
69
|
+
end
|
70
|
+
```
|
71
|
+
|
59
72
|
## Benchmarks
|
60
73
|
```
|
61
74
|
Initialization benchmark
|
62
75
|
|
63
76
|
Warming up --------------------------------------
|
64
|
-
OpenStruct
|
65
|
-
FastOpenStruct
|
77
|
+
OpenStruct 6.967k i/100ms
|
78
|
+
FastOpenStruct 45.819k i/100ms
|
66
79
|
Calculating -------------------------------------
|
67
|
-
OpenStruct
|
68
|
-
FastOpenStruct
|
80
|
+
OpenStruct 70.614k (± 4.4%) i/s - 355.317k in 5.042038s
|
81
|
+
FastOpenStruct 454.480k (± 1.6%) i/s - 2.291M in 5.042112s
|
69
82
|
|
70
83
|
Comparison:
|
71
|
-
FastOpenStruct:
|
72
|
-
OpenStruct:
|
84
|
+
FastOpenStruct: 454479.6 i/s
|
85
|
+
OpenStruct: 70614.1 i/s - 6.44x slower
|
73
86
|
|
74
87
|
|
75
88
|
|
76
89
|
Assignment Benchmark
|
77
90
|
|
78
91
|
Warming up --------------------------------------
|
79
|
-
OpenStruct
|
80
|
-
FastOpenStruct 1.
|
92
|
+
OpenStruct 317.626k i/100ms
|
93
|
+
FastOpenStruct 1.413M i/100ms
|
81
94
|
Calculating -------------------------------------
|
82
|
-
OpenStruct
|
83
|
-
FastOpenStruct
|
95
|
+
OpenStruct 3.222M (± 0.3%) i/s - 16.199M in 5.027118s
|
96
|
+
FastOpenStruct 14.663M (± 1.0%) i/s - 73.465M in 5.010767s
|
84
97
|
|
85
98
|
Comparison:
|
86
|
-
FastOpenStruct:
|
87
|
-
OpenStruct:
|
99
|
+
FastOpenStruct: 14662985.4 i/s
|
100
|
+
OpenStruct: 3222330.4 i/s - 4.55x slower
|
88
101
|
|
89
102
|
|
90
103
|
|
91
104
|
Access Benchmark
|
92
105
|
|
93
106
|
Warming up --------------------------------------
|
94
|
-
OpenStruct
|
95
|
-
FastOpenStruct
|
107
|
+
OpenStruct 455.965k i/100ms
|
108
|
+
FastOpenStruct 1.511M i/100ms
|
96
109
|
Calculating -------------------------------------
|
97
|
-
OpenStruct
|
98
|
-
FastOpenStruct
|
110
|
+
OpenStruct 4.533M (± 3.2%) i/s - 22.798M in 5.034950s
|
111
|
+
FastOpenStruct 15.154M (± 0.5%) i/s - 77.063M in 5.085386s
|
99
112
|
|
100
113
|
Comparison:
|
101
|
-
FastOpenStruct:
|
102
|
-
OpenStruct:
|
114
|
+
FastOpenStruct: 15154237.8 i/s
|
115
|
+
OpenStruct: 4533257.4 i/s - 3.34x slower
|
103
116
|
|
104
117
|
|
105
118
|
|
106
119
|
All-Together Benchmark
|
107
120
|
|
108
121
|
Warming up --------------------------------------
|
109
|
-
OpenStruct
|
110
|
-
FastOpenStruct
|
122
|
+
OpenStruct 5.084k i/100ms
|
123
|
+
FastOpenStruct 218.702k i/100ms
|
111
124
|
Calculating -------------------------------------
|
112
|
-
OpenStruct
|
113
|
-
FastOpenStruct
|
125
|
+
OpenStruct 52.705k (± 4.7%) i/s - 264.368k in 5.027437s
|
126
|
+
FastOpenStruct 2.135M (± 4.1%) i/s - 10.716M in 5.028416s
|
114
127
|
|
115
128
|
Comparison:
|
116
|
-
FastOpenStruct:
|
117
|
-
OpenStruct:
|
129
|
+
FastOpenStruct: 2135322.0 i/s
|
130
|
+
OpenStruct: 52704.7 i/s - 40.51x slower
|
131
|
+
|
118
132
|
```
|
119
133
|
|
120
134
|
## Contributing
|
data/fast_ostruct.gemspec
CHANGED
@@ -24,14 +24,6 @@ Gem::Specification.new do |spec|
|
|
24
24
|
end
|
25
25
|
spec.require_paths = ['lib']
|
26
26
|
|
27
|
-
spec.add_development_dependency 'benchmark-ips', '>= 2'
|
28
|
-
spec.add_development_dependency 'pry', '>= 0.14'
|
29
|
-
spec.add_development_dependency 'rack-test', '~> 1.1'
|
30
|
-
spec.add_development_dependency 'rake', '~> 13.0'
|
31
|
-
spec.add_development_dependency 'rspec', '~> 3.0'
|
32
|
-
spec.add_development_dependency 'rubocop', '~> 1.25'
|
33
|
-
spec.add_development_dependency 'rubocop-performance', '~> 1.15'
|
34
|
-
|
35
27
|
spec.add_dependency 'json', '>= 2'
|
36
28
|
|
37
29
|
spec.metadata['rubygems_mfa_required'] = 'true'
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class FastOpenStruct
|
4
|
+
class Config
|
5
|
+
attr_accessor :initialize_options, :attributes_options
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@initialize_options = { deep_initialize: true }
|
9
|
+
@attributes_options = { symbolize_keys: false }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/lib/fast_ostruct.rb
CHANGED
@@ -2,14 +2,25 @@
|
|
2
2
|
|
3
3
|
require 'json'
|
4
4
|
require 'set'
|
5
|
+
require_relative 'fast_ostruct/config'
|
6
|
+
require_relative 'fast_ostruct/version'
|
5
7
|
|
6
8
|
class FastOpenStruct
|
7
|
-
|
9
|
+
class << self
|
10
|
+
def configure
|
11
|
+
yield config
|
12
|
+
end
|
8
13
|
|
9
|
-
|
10
|
-
|
14
|
+
def config
|
15
|
+
@config ||= Config.new
|
16
|
+
end
|
17
|
+
end
|
11
18
|
|
12
|
-
def
|
19
|
+
def config
|
20
|
+
self.class.config
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(attributes = {}, options = config.initialize_options)
|
13
24
|
if options[:deep_initialize]
|
14
25
|
attributes.except(*options.keys).each_pair do |name, value|
|
15
26
|
self[name] = deep_initialize(value, options)
|
@@ -21,7 +32,7 @@ class FastOpenStruct
|
|
21
32
|
end
|
22
33
|
end
|
23
34
|
|
24
|
-
def attributes(options =
|
35
|
+
def attributes(options = config.attributes_options)
|
25
36
|
key_formatter = if options[:symbolize_keys]
|
26
37
|
proc { |name| name.to_sym }
|
27
38
|
else
|
@@ -41,12 +52,12 @@ class FastOpenStruct
|
|
41
52
|
end
|
42
53
|
|
43
54
|
def to_json(*_args)
|
44
|
-
attributes
|
55
|
+
JSON.generate(attributes)
|
45
56
|
end
|
46
57
|
alias serializable_hash attributes
|
47
58
|
alias to_h attributes
|
48
59
|
|
49
|
-
#
|
60
|
+
# Hasheable
|
50
61
|
def [](name)
|
51
62
|
attribute_get(name)
|
52
63
|
end
|
@@ -61,6 +72,11 @@ class FastOpenStruct
|
|
61
72
|
attributes(symbolize_keys: true).dig(name, *identifiers)
|
62
73
|
end
|
63
74
|
|
75
|
+
def slice(*names)
|
76
|
+
names.map!(&:to_sym)
|
77
|
+
attributes(symbolize_keys: true).slice(*names)
|
78
|
+
end
|
79
|
+
|
64
80
|
def delete_field(name)
|
65
81
|
attribute_unset(name)
|
66
82
|
end
|
@@ -79,7 +95,7 @@ class FastOpenStruct
|
|
79
95
|
false
|
80
96
|
end
|
81
97
|
|
82
|
-
def save
|
98
|
+
def save(*)
|
83
99
|
self
|
84
100
|
end
|
85
101
|
|
@@ -104,16 +120,16 @@ class FastOpenStruct
|
|
104
120
|
@defined_methods ||= Set.new
|
105
121
|
end
|
106
122
|
|
107
|
-
def
|
123
|
+
def define_methods!(name)
|
108
124
|
class_exec do
|
109
|
-
|
110
|
-
|
125
|
+
attr_accessor name
|
126
|
+
|
111
127
|
defined_methods << name
|
112
128
|
end
|
113
129
|
end
|
114
130
|
end
|
115
131
|
|
116
|
-
def deep_initialize(value, options =
|
132
|
+
def deep_initialize(value, options = config.initialize_options)
|
117
133
|
if value.is_a?(Array)
|
118
134
|
value = value.map { |v| v.is_a?(Hash) ? self.class.new(v, options) : v }
|
119
135
|
elsif value.is_a?(Hash)
|
@@ -126,16 +142,16 @@ class FastOpenStruct
|
|
126
142
|
return if name.nil?
|
127
143
|
|
128
144
|
name = name.to_sym
|
129
|
-
self.class.
|
130
|
-
instance_variable_set("@#{name}", value)
|
145
|
+
self.class.define_methods!(name) unless self.class.defined_methods.include?(name)
|
146
|
+
instance_variable_set(:"@#{name}", value)
|
131
147
|
end
|
132
148
|
|
133
149
|
def attribute_get(name)
|
134
|
-
instance_variable_get("@#{name}")
|
150
|
+
instance_variable_get(:"@#{name}")
|
135
151
|
end
|
136
152
|
|
137
153
|
def attribute_unset(name)
|
138
|
-
remove_instance_variable("@#{name}")
|
154
|
+
remove_instance_variable(:"@#{name}")
|
139
155
|
end
|
140
156
|
|
141
157
|
def method_missing(mid, *args)
|
data/lib/tasks/benchmark.rake
CHANGED
@@ -10,7 +10,7 @@ namespace :fast_open_struct do
|
|
10
10
|
task :benchmark do
|
11
11
|
puts "Initialization benchmark\n\n"
|
12
12
|
Benchmark.ips do |x|
|
13
|
-
input_hash = { foo: :bar }
|
13
|
+
input_hash = { foo: :bar, bar: :baz, baz: :foo }
|
14
14
|
|
15
15
|
x.report('OpenStruct') do
|
16
16
|
OpenStruct.new(input_hash)
|
@@ -23,47 +23,60 @@ namespace :fast_open_struct do
|
|
23
23
|
|
24
24
|
puts "\n\nAssignment Benchmark\n\n"
|
25
25
|
Benchmark.ips do |x|
|
26
|
-
|
27
|
-
|
28
|
-
fos = FastOpenStruct.new(input_hash)
|
26
|
+
os = OpenStruct.new
|
27
|
+
fos = FastOpenStruct.new
|
29
28
|
|
30
29
|
x.report('OpenStruct') do
|
31
30
|
os.foo = :bar
|
31
|
+
os.bar = :baz
|
32
|
+
os.baz = :foo
|
32
33
|
end
|
33
34
|
x.report('FastOpenStruct') do
|
34
35
|
fos.foo = :bar
|
36
|
+
fos.bar = :baz
|
37
|
+
fos.baz = :foo
|
35
38
|
end
|
36
39
|
x.compare!
|
37
40
|
end
|
38
41
|
|
39
42
|
puts "\n\nAccess Benchmark\n\n"
|
40
43
|
Benchmark.ips do |x|
|
41
|
-
input_hash = { foo: :bar }
|
44
|
+
input_hash = { foo: :bar, bar: :baz, baz: :foo }
|
42
45
|
os = OpenStruct.new(input_hash)
|
43
46
|
fos = FastOpenStruct.new(input_hash)
|
44
47
|
|
45
48
|
x.report('OpenStruct') do
|
46
49
|
os.foo
|
50
|
+
os.bar
|
51
|
+
os.baz
|
47
52
|
end
|
48
53
|
x.report('FastOpenStruct') do
|
49
54
|
fos.foo
|
55
|
+
fos.bar
|
56
|
+
fos.baz
|
50
57
|
end
|
51
58
|
x.compare!
|
52
59
|
end
|
53
60
|
|
54
61
|
puts "\n\nAll-Together Benchmark\n\n"
|
55
62
|
Benchmark.ips do |x|
|
56
|
-
input_hash = { foo: :bar }
|
57
|
-
|
58
63
|
x.report('OpenStruct') do
|
59
|
-
os = OpenStruct.new
|
64
|
+
os = OpenStruct.new
|
60
65
|
os.foo = :bar
|
61
66
|
os.foo
|
67
|
+
os.bar = :baz
|
68
|
+
os.bar
|
69
|
+
os.baz = :foo
|
70
|
+
os.baz
|
62
71
|
end
|
63
72
|
x.report('FastOpenStruct') do
|
64
|
-
fos = FastOpenStruct.new
|
73
|
+
fos = FastOpenStruct.new
|
65
74
|
fos.foo = :bar
|
66
75
|
fos.foo
|
76
|
+
fos.bar = :baz
|
77
|
+
fos.bar
|
78
|
+
fos.baz = :foo
|
79
|
+
fos.baz
|
67
80
|
end
|
68
81
|
x.compare!
|
69
82
|
end
|
metadata
CHANGED
@@ -1,113 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fast_ostruct
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Washington Silva
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-08-16 00:00:00.000000000 Z
|
12
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'
|
20
|
-
type: :development
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '2'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: pry
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0.14'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0.14'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: rack-test
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - "~>"
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '1.1'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - "~>"
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '1.1'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: rake
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - "~>"
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '13.0'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - "~>"
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '13.0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: rspec
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - "~>"
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '3.0'
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - "~>"
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '3.0'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: rubocop
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - "~>"
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '1.25'
|
90
|
-
type: :development
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - "~>"
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: '1.25'
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: rubocop-performance
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
100
|
-
requirements:
|
101
|
-
- - "~>"
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
version: '1.15'
|
104
|
-
type: :development
|
105
|
-
prerelease: false
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
107
|
-
requirements:
|
108
|
-
- - "~>"
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: '1.15'
|
111
13
|
- !ruby/object:Gem::Dependency
|
112
14
|
name: json
|
113
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -134,6 +36,7 @@ files:
|
|
134
36
|
- ".rspec"
|
135
37
|
- ".rubocop.yml"
|
136
38
|
- ".ruby-version"
|
39
|
+
- CHANGELOG.md
|
137
40
|
- Gemfile
|
138
41
|
- LICENSE
|
139
42
|
- README.md
|
@@ -142,6 +45,8 @@ files:
|
|
142
45
|
- bin/setup
|
143
46
|
- fast_ostruct.gemspec
|
144
47
|
- lib/fast_ostruct.rb
|
48
|
+
- lib/fast_ostruct/config.rb
|
49
|
+
- lib/fast_ostruct/version.rb
|
145
50
|
- lib/tasks/benchmark.rake
|
146
51
|
homepage: https://github.com/w-osilva/fast_ostruct
|
147
52
|
licenses:
|