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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4a550fb9fdce2464d1b4f156eee3538ff7c214b53da5d704a2a54c326433e2fc
4
- data.tar.gz: e11bde8d2b58a8cf595352a4d814364a2f08b51b5f33eb8404f6a8c2428c14e8
3
+ metadata.gz: 46b3703a02825829511af5040efa372482bdde0ea0df6ed66e3cd547ead5db37
4
+ data.tar.gz: e172571b4121f06c48a8042f1207c76734fadf9c80d41570798eb5eae668fb38
5
5
  SHA512:
6
- metadata.gz: 5d7e83a8427f638bbc0c75c099a85c29cd9f2f8164fa3b2b0334c60409b9fa0a4bc0c0975974bee51db0b29392232c6f90aa53eb0489727bc5552351c6764a3f
7
- data.tar.gz: 3306b542370c0ab7d0fa41a3e2ad1b4c5653f70dbb024c2b373f0224412f5e4e22dd6a1e615c5f37178ea07309a9e88c38d5521d201dec9a7e185ee3636a6fba
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 15.041k i/100ms
65
- FastOpenStruct 116.264k i/100ms
77
+ OpenStruct 6.967k i/100ms
78
+ FastOpenStruct 45.819k i/100ms
66
79
  Calculating -------------------------------------
67
- OpenStruct 152.606k2.4%) i/s - 767.091k in 5.029398s
68
- FastOpenStruct 1.148M (± 1.4%) i/s - 5.813M in 5.063585s
80
+ OpenStruct 70.614k4.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: 1148278.4 i/s
72
- OpenStruct: 152606.3 i/s - 7.52x slower
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 682.010k i/100ms
80
- FastOpenStruct 1.972M i/100ms
92
+ OpenStruct 317.626k i/100ms
93
+ FastOpenStruct 1.413M i/100ms
81
94
  Calculating -------------------------------------
82
- OpenStruct 6.980M3.3%) i/s - 35.465M in 5.086554s
83
- FastOpenStruct 19.964M (± 0.8%) i/s - 100.582M in 5.038516s
95
+ OpenStruct 3.222M0.3%) i/s - 16.199M in 5.027118s
96
+ FastOpenStruct 14.663M1.0%) i/s - 73.465M in 5.010767s
84
97
 
85
98
  Comparison:
86
- FastOpenStruct: 19964034.5 i/s
87
- OpenStruct: 6980499.3 i/s - 2.86x slower
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 1.005M i/100ms
95
- FastOpenStruct 2.185M i/100ms
107
+ OpenStruct 455.965k i/100ms
108
+ FastOpenStruct 1.511M i/100ms
96
109
  Calculating -------------------------------------
97
- OpenStruct 10.085M0.5%) i/s - 51.245M in 5.081712s
98
- FastOpenStruct 21.798M1.1%) i/s - 109.268M in 5.013290s
110
+ OpenStruct 4.533M3.2%) i/s - 22.798M in 5.034950s
111
+ FastOpenStruct 15.154M0.5%) i/s - 77.063M in 5.085386s
99
112
 
100
113
  Comparison:
101
- FastOpenStruct: 21798290.7 i/s
102
- OpenStruct: 10084528.1 i/s - 2.16x slower
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 12.918k i/100ms
110
- FastOpenStruct 111.460k i/100ms
122
+ OpenStruct 5.084k i/100ms
123
+ FastOpenStruct 218.702k i/100ms
111
124
  Calculating -------------------------------------
112
- OpenStruct 127.875k2.7%) i/s - 645.900k in 5.054749s
113
- FastOpenStruct 1.122M1.1%) i/s - 5.684M in 5.066350s
125
+ OpenStruct 52.705k4.7%) i/s - 264.368k in 5.027437s
126
+ FastOpenStruct 2.135M4.1%) i/s - 10.716M in 5.028416s
114
127
 
115
128
  Comparison:
116
- FastOpenStruct: 1122147.0 i/s
117
- OpenStruct: 127874.6 i/s - 8.78x slower
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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class FastOpenStruct
4
+ VERSION = '1.1.0'
5
+ 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
- VERSION = '1.0.0'
9
+ class << self
10
+ def configure
11
+ yield config
12
+ end
8
13
 
9
- INITIALIZE_OPTIONS = { deep_initialize: true }.freeze
10
- ATTRIBUTES_OPTIONS = { symbolize_keys: false }.freeze
14
+ def config
15
+ @config ||= Config.new
16
+ end
17
+ end
11
18
 
12
- def initialize(attributes = {}, options = INITIALIZE_OPTIONS)
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 = 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.to_json
55
+ JSON.generate(attributes)
45
56
  end
46
57
  alias serializable_hash attributes
47
58
  alias to_h attributes
48
59
 
49
- # Accessible
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 define_method!(name)
123
+ def define_methods!(name)
108
124
  class_exec do
109
- attr_writer name unless method_defined?("#{name}=")
110
- attr_reader name unless method_defined?(name)
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 = INITIALIZE_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.define_method!(name) unless self.class.defined_methods.include?(name)
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)
@@ -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
- input_hash = { foo: :bar }
27
- os = OpenStruct.new(input_hash)
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(input_hash)
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(input_hash)
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.0.0
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-04-17 00:00:00.000000000 Z
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: