production_open_struct 1.0.0 → 1.0.1

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: d66f2bc476482745d878fdd7ddcbeca5eb7ee08f3f6f455449913fee661f848b
4
- data.tar.gz: 52799916fea68a7a9c0fc57cdabfdc9a62aff4f16c0014cb1291bf50e07725a4
3
+ metadata.gz: 8c050b2ea17cf5ea57c4a707234b2a1e2f6aa8f3db1955a81375392f3c9e75a0
4
+ data.tar.gz: 0f8225d7a222ff8dcccd1ceb8fb349e2bc70f3977143e0b8585321c38cd31285
5
5
  SHA512:
6
- metadata.gz: d074f8eea4c9a220c41394b91a69123a43e04f9018293af6c87f2d798806628ace0a9ebdb05855da01c8c13d75bd4ea280321d7f5540e520e1847eb7d3501484
7
- data.tar.gz: 3c6ed9917bebdbb928ec8dff696eac4ea9e626c60ea552b0841e9ec8ae6100eb6df9a05dcb9cddf4a5bb8c58a02d76e9d8a9ede92b33fcf1427267a0605d1794
6
+ metadata.gz: e938bb6035f79a5bbafefa6a620f2624fe14d6585d02a4c30c63f76d954a3691c859e183f5ee33facfcda1b83ad93a6b588b78517c66753a8f1a1ebe5c9ab477
7
+ data.tar.gz: bcc856b4c14684b64cea93323fce0e04ccc0ab35eb41e921c2a35286d5d014e7f421e01f51d371f98b1457024d6ba0e511b0e4bc06861bcf1a3e1b37afdf4fcd
data/CHANGELOG.md CHANGED
@@ -4,7 +4,17 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## 1.0.1
8
+
9
+ ### Fixed
10
+
11
+ - Use `raise` and an explicit block argument instead of the `raise!` and `block_given!` aliases which do not exist in the ostruct versions bundled with Ruby <= 3.0 or on JRuby. Previously on those versions, errors from `delete_field` on a missing field, setter calls with the wrong number of arguments, and undefined method calls with arguments could be silently swallowed instead of raised.
12
+ - `respond_to?` no longer raises an error on an allocated but uninitialized OpenStruct.
13
+ - `delete_field` now raises a `FrozenError` on frozen objects on ostruct versions that do not freeze the internal table.
14
+ - `delete_field` removes stale singleton method accessors from objects created before the gem was loaded.
15
+
7
16
  ## 1.0.0
8
17
 
9
18
  ### Added
19
+
10
20
  - Override OpenStuct to not define singleton methods which clear the Ruby method cache.
data/README.md CHANGED
@@ -1,7 +1,8 @@
1
+ # Production OpenStruct
2
+
1
3
  [![Continuous Integration](https://github.com/bdurand/production_open_struct/actions/workflows/continuous_integration.yml/badge.svg)](https://github.com/bdurand/production_open_struct/actions/workflows/continuous_integration.yml)
2
4
  [![Ruby Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://github.com/testdouble/standard)
3
-
4
- # Production OpenStuct
5
+ [![Gem Version](https://badge.fury.io/rb/production_open_struct.svg)](https://badge.fury.io/rb/production_open_struct)
5
6
 
6
7
  This gem overrides behavior in the [OpenStruct](https://github.com/ruby/ostruct) implementation defined in the Ruby standard library. While OpenStruct can be useful for one off scripts or as testing mocks, it can cause performance issues in production environments.
7
8
 
@@ -12,12 +13,12 @@ OpenStruct defines singleton methods on every object created for each key in the
12
13
  object = OpenStruct.new(foo: "bar")
13
14
  object.foo # => "bar"
14
15
  object.foo = "biz"
15
- object.foo # => "bix"
16
+ object.foo # => "biz"
16
17
  ```
17
18
 
18
19
  It is not a good idea to use OpenStruct in your production code. It is much more efficient and safer to just define some simple classes or use `Struct` rather than using OpenStruct. However, not everyone sticks to this and you can end up with external libraries in your application that do use OpenStruct.
19
20
 
20
- This gem solves the OpenStruct performance issues by simply overriding the code in OpenStruct that defines singleton methods. This doesn't have any functional affect on OpenStruct objects; you can still use the attribute reader and writer methods. However, these will now go through `method_missing` every time you call them. This does add its own overhead but it is far more performant in most cases than defining dynamic methods. Furthermore, the global method cache is no longer impacted by creating OpenStruct objects.
21
+ This gem solves the OpenStruct performance issues by simply overriding the code in OpenStruct that defines singleton methods. This doesn't have any functional effect on OpenStruct objects; you can still use the attribute reader and writer methods. However, these will now go through `method_missing` every time you call them. This does add its own overhead but it is far more performant in most cases than defining dynamic methods. Furthermore, the global method cache is no longer impacted by creating OpenStruct objects.
21
22
 
22
23
  ## Usage
23
24
 
@@ -27,6 +28,12 @@ Nothing is needed to use this gem other than requiring it.
27
28
  require "production_open_struct"
28
29
  ```
29
30
 
31
+ If you need to disable the OpenStruct patch (for instance, to compare behavior in a test suite), you can set the `PRODUCTION_OPEN_STRUCT_AUTO_INCLUDE` environment variable to `false` before the gem is required. You can then opt in manually with:
32
+
33
+ ```ruby
34
+ OpenStruct.prepend(ProductionOpenStruct)
35
+ ```
36
+
30
37
  ## Installation
31
38
 
32
39
  Add this line to your application's Gemfile:
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.0
1
+ 1.0.1
@@ -3,12 +3,28 @@
3
3
  require "ostruct"
4
4
 
5
5
  # Overrides OpenStruct behavior to no longer define singleton methods on each object.
6
+ #
7
+ # Note: unlike the standard library implementation, this code does not need to use
8
+ # the `raise!` and `block_given!` aliases. Those aliases exist to protect OpenStruct
9
+ # internals from fields that shadow built in methods via singleton methods. Since this
10
+ # module prevents singleton methods from ever being defined, built in methods can no
11
+ # longer be shadowed. The aliases also don't exist in the ostruct versions bundled
12
+ # with Ruby <= 3.0 or on JRuby, so using them would break on those platforms.
6
13
  module ProductionOpenStruct
7
- def delete_field(name)
14
+ # @param name [Symbol, String] the name of the field to delete
15
+ # @return [void]
16
+ def delete_field(name, &block)
17
+ raise FrozenError, "can't modify frozen #{self.class}: #{inspect}" if frozen?
18
+
8
19
  sym = name.to_sym
20
+ begin
21
+ # Clean up stale accessors on objects created before this module was prepended.
22
+ singleton_class.remove_method(sym, "#{sym}=")
23
+ rescue NameError
24
+ end
9
25
  @table.delete(sym) do
10
- return yield if block_given!
11
- raise! NameError.new("no field `#{sym}' in #{self}", sym)
26
+ return yield if block
27
+ raise NameError.new("no field `#{sym}' in #{self}", sym)
12
28
  end
13
29
  end
14
30
 
@@ -23,7 +39,7 @@ module ProductionOpenStruct
23
39
  len = args.length
24
40
  if method_name.to_s.end_with?("=")
25
41
  if len != 1
26
- raise! ArgumentError, "wrong number of arguments (given #{len}, expected 1)", caller(1)
42
+ raise ArgumentError, "wrong number of arguments (given #{len}, expected 1)", caller(1)
27
43
  end
28
44
  self[method_name.to_s.chomp("=")] = args[0]
29
45
  elsif len == 0
@@ -33,15 +49,15 @@ module ProductionOpenStruct
33
49
  super
34
50
  rescue NoMethodError => err
35
51
  err.backtrace.shift
36
- raise!
52
+ raise
37
53
  end
38
54
  end
39
55
  end
40
56
 
41
57
  def respond_to_missing?(method_name, include_private = false)
42
58
  key = method_name.to_s.chomp("=").to_sym
43
- @table.include?(key) || super
59
+ (defined?(@table) && @table.include?(key)) || super
44
60
  end
45
61
  end
46
62
 
47
- OpenStruct.prepend(ProductionOpenStruct) unless ENV["PRODUCTION_OPEN_STRUCT_AUTO_INCLUDE"] == "false"
63
+ OpenStruct.send(:prepend, ProductionOpenStruct) unless ENV["PRODUCTION_OPEN_STRUCT_AUTO_INCLUDE"] == "false"
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = "production_open_struct"
3
- spec.version = File.read(File.expand_path("../VERSION", __FILE__)).strip
3
+ spec.version = File.read(File.expand_path("VERSION", __dir__)).strip
4
4
  spec.authors = ["Brian Durand"]
5
5
  spec.email = ["bbdurand@gmail.com"]
6
6
 
@@ -8,6 +8,12 @@ Gem::Specification.new do |spec|
8
8
  spec.homepage = "https://github.com/bdurand/production_open_struct"
9
9
  spec.license = "MIT"
10
10
 
11
+ spec.metadata = {
12
+ "homepage_uri" => spec.homepage,
13
+ "source_code_uri" => spec.homepage,
14
+ "changelog_uri" => "#{spec.homepage}/blob/main/CHANGELOG.md"
15
+ }
16
+
11
17
  # Specify which files should be added to the gem when it is released.
12
18
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
13
19
  ignore_files = %w[
@@ -20,13 +26,15 @@ Gem::Specification.new do |spec|
20
26
  gemfiles/
21
27
  spec/
22
28
  ]
23
- spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do
29
+ spec.files = Dir.chdir(__dir__) do
24
30
  `git ls-files -z`.split("\x0").reject { |f| ignore_files.any? { |path| f.start_with?(path) } }
25
31
  end
26
32
 
27
33
  spec.require_paths = ["lib"]
28
34
 
35
+ spec.add_dependency "ostruct"
36
+
29
37
  spec.add_development_dependency "bundler"
30
38
 
31
- spec.required_ruby_version = ">= 2.5"
39
+ spec.required_ruby_version = ">= 2.6"
32
40
  end
metadata CHANGED
@@ -1,15 +1,28 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: production_open_struct
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Durand
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2021-08-26 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: ostruct
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
13
26
  - !ruby/object:Gem::Dependency
14
27
  name: bundler
15
28
  requirement: !ruby/object:Gem::Requirement
@@ -24,7 +37,6 @@ dependencies:
24
37
  - - ">="
25
38
  - !ruby/object:Gem::Version
26
39
  version: '0'
27
- description:
28
40
  email:
29
41
  - bbdurand@gmail.com
30
42
  executables: []
@@ -32,7 +44,7 @@ extensions: []
32
44
  extra_rdoc_files: []
33
45
  files:
34
46
  - CHANGELOG.md
35
- - MIT-LICENSE
47
+ - MIT-LICENSE.txt
36
48
  - README.md
37
49
  - VERSION
38
50
  - lib/production_open_struct.rb
@@ -40,8 +52,10 @@ files:
40
52
  homepage: https://github.com/bdurand/production_open_struct
41
53
  licenses:
42
54
  - MIT
43
- metadata: {}
44
- post_install_message:
55
+ metadata:
56
+ homepage_uri: https://github.com/bdurand/production_open_struct
57
+ source_code_uri: https://github.com/bdurand/production_open_struct
58
+ changelog_uri: https://github.com/bdurand/production_open_struct/blob/main/CHANGELOG.md
45
59
  rdoc_options: []
46
60
  require_paths:
47
61
  - lib
@@ -49,15 +63,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
49
63
  requirements:
50
64
  - - ">="
51
65
  - !ruby/object:Gem::Version
52
- version: '2.5'
66
+ version: '2.6'
53
67
  required_rubygems_version: !ruby/object:Gem::Requirement
54
68
  requirements:
55
69
  - - ">="
56
70
  - !ruby/object:Gem::Version
57
71
  version: '0'
58
72
  requirements: []
59
- rubygems_version: 3.1.6
60
- signing_key:
73
+ rubygems_version: 4.0.3
61
74
  specification_version: 4
62
75
  summary: Modifies OpenStruct so that it doesn't define singleton methods on each object
63
76
  which busts the Ruby method cache which can cause performance issues in production
File without changes