fast_ostruct 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: