resource-struct 0.4.0 → 0.5.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: 81f9dfcc67f4c1670776860dd53c7d0885eb1b1d53ca7b9bae7202c782cbad85
4
- data.tar.gz: ae36515a1e79be8653e2b4497dee9ab5f8d522e740cfcafe93681d946a5e528e
3
+ metadata.gz: dbe0071df5f0b590203edc5208102dd5f8f8436ef41525067b3c3e740b84357e
4
+ data.tar.gz: 267154989b507ecd1ce651682d17437275721c64c24533ea201a73e7892a03a1
5
5
  SHA512:
6
- metadata.gz: 835d23cd09aa4a26cce6cf30378e749f5eee0a9439501fd1120ee3ac6f77f6adaed05ea688632e23dce09dd7bee8616859176fbc135b2d027c388aa3eec10e1b
7
- data.tar.gz: 102bada15cf4151340c2fd95c5927335e19ec9bd637a2e74ba3b1e2193ead54a3a3a5af8d451caf364f0465caf5663079eeeaf72e3b7c7acc7b1ef7c847e3f95
6
+ metadata.gz: fb4d3626e03fd029e8305c4adc635f5e8b361dc468caa4d84167079b57e7916bbe7a3f2d0b83d29eefb45a0e4f89c7c8859eaf8f35141e338742b5f8119dcf44
7
+ data.tar.gz: 8f1b47a618fefc03bea375c50bf7030fc2b4dcb0efa1f6ca5f98c11156a2d60b9c65411de7cbd8e6c9388dbcfc62a9736a167fd17367b095d84c2162baeafc30
data/.rubocop.yml CHANGED
@@ -1,5 +1,6 @@
1
1
  AllCops:
2
- TargetRubyVersion: 2.7
2
+ TargetRubyVersion: 3.3
3
+ NewCops: enable
3
4
 
4
5
  Style/StringLiterals:
5
6
  Enabled: true
data/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.5.0] - 2026-07-03
4
+ ### Breaking
5
+ - Symbol hash keys are now canonicalized to strings internally. This means:
6
+ - `Struct#to_h`, `#to_hash`, and `#inspect` return string keys even when the input hash used symbols.
7
+ - Hashes containing both a symbol and string key for the same logical property (e.g. `{ foo: 1, "foo" => 2 }`) will keep only one value after canonicalization.
8
+ - Marshalled data from previous versions that used symbol keys will load with string keys.
9
+ - Raised minimum Ruby version to 3.3.
10
+ - `StrictStruct#to_h` / `#to_hash` now return a shallow copy instead of the internal mutable hash.
11
+
12
+ ### Fixed
13
+ - Canonicalize symbol keys to strings internally so symbol/string key collisions are handled consistently and cached nested structs have stable identity.
14
+ - README typos (`ResouceStruct`) and GitHub URL placeholder.
15
+ - Removed unused `bindir` / `executables` configuration from the gemspec.
16
+ - Fixed GitHub Actions pull_request trigger so pushes to PR branches (including force pushes) re-run CI.
17
+
18
+ ### Changed
19
+ - Updated RuboCop and CI to current Ruby versions (3.3, 3.4, 4.0) and action versions.
20
+
3
21
  ## [0.4.0] - 2022-01-09
4
22
  ### Feature
5
23
  - Better support for `ArgumentError` on `FlexStruct`
data/Gemfile CHANGED
@@ -5,10 +5,11 @@ source "https://rubygems.org"
5
5
  # Specify your gem's dependencies in resource-struct.gemspec
6
6
  gemspec
7
7
 
8
- gem "rake", "~> 13.0"
8
+ gem "irb"
9
+ gem "rake", "~> 13.2"
9
10
 
10
11
  gem "rspec", "~> 3.0"
11
12
 
12
- gem "rubocop", "~> 1.21"
13
+ gem "rubocop", "~> 1.75"
13
14
  gem "rubocop-rake"
14
15
  gem "rubocop-rspec"
data/Gemfile.lock CHANGED
@@ -1,61 +1,99 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- resource-struct (0.4.0)
4
+ resource-struct (0.5.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
- ast (2.4.2)
10
- diff-lcs (1.4.4)
11
- parallel (1.21.0)
12
- parser (3.0.3.2)
9
+ ast (2.4.3)
10
+ diff-lcs (1.6.2)
11
+ erb (6.0.4)
12
+ io-console (0.8.2)
13
+ irb (1.18.0)
14
+ pp (>= 0.6.0)
15
+ prism (>= 1.3.0)
16
+ rdoc (>= 4.0.0)
17
+ reline (>= 0.4.2)
18
+ json (2.20.0)
19
+ language_server-protocol (3.17.0.6)
20
+ lint_roller (1.1.0)
21
+ logger (1.7.0)
22
+ parallel (2.1.0)
23
+ parser (3.3.11.1)
13
24
  ast (~> 2.4.1)
14
- rainbow (3.0.0)
15
- rake (13.0.6)
16
- regexp_parser (2.2.0)
17
- rexml (3.2.5)
18
- rspec (3.10.0)
19
- rspec-core (~> 3.10.0)
20
- rspec-expectations (~> 3.10.0)
21
- rspec-mocks (~> 3.10.0)
22
- rspec-core (3.10.1)
23
- rspec-support (~> 3.10.0)
24
- rspec-expectations (3.10.1)
25
+ racc
26
+ pp (0.6.4)
27
+ prettyprint
28
+ prettyprint (0.2.0)
29
+ prism (1.9.0)
30
+ racc (1.8.1)
31
+ rainbow (3.1.1)
32
+ rake (13.4.2)
33
+ rbs (4.0.3)
34
+ logger
35
+ prism (>= 1.6.0)
36
+ tsort
37
+ rdoc (8.0.0)
38
+ erb
39
+ prism (>= 1.6.0)
40
+ rbs (>= 4.0.0)
41
+ tsort
42
+ regexp_parser (2.12.0)
43
+ reline (0.6.3)
44
+ io-console (~> 0.5)
45
+ rspec (3.13.2)
46
+ rspec-core (~> 3.13.0)
47
+ rspec-expectations (~> 3.13.0)
48
+ rspec-mocks (~> 3.13.0)
49
+ rspec-core (3.13.6)
50
+ rspec-support (~> 3.13.0)
51
+ rspec-expectations (3.13.5)
25
52
  diff-lcs (>= 1.2.0, < 2.0)
26
- rspec-support (~> 3.10.0)
27
- rspec-mocks (3.10.2)
53
+ rspec-support (~> 3.13.0)
54
+ rspec-mocks (3.13.8)
28
55
  diff-lcs (>= 1.2.0, < 2.0)
29
- rspec-support (~> 3.10.0)
30
- rspec-support (3.10.3)
31
- rubocop (1.23.0)
32
- parallel (~> 1.10)
33
- parser (>= 3.0.0.0)
56
+ rspec-support (~> 3.13.0)
57
+ rspec-support (3.13.7)
58
+ rubocop (1.88.1)
59
+ json (~> 2.3)
60
+ language_server-protocol (~> 3.17.0.2)
61
+ lint_roller (~> 1.1.0)
62
+ parallel (>= 1.10)
63
+ parser (>= 3.3.0.2)
34
64
  rainbow (>= 2.2.2, < 4.0)
35
- regexp_parser (>= 1.8, < 3.0)
36
- rexml
37
- rubocop-ast (>= 1.12.0, < 2.0)
65
+ regexp_parser (>= 2.9.3, < 3.0)
66
+ rubocop-ast (>= 1.49.0, < 2.0)
38
67
  ruby-progressbar (~> 1.7)
39
- unicode-display_width (>= 1.4.0, < 3.0)
40
- rubocop-ast (1.15.0)
41
- parser (>= 3.0.1.1)
42
- rubocop-rake (0.6.0)
43
- rubocop (~> 1.0)
44
- rubocop-rspec (2.6.0)
45
- rubocop (~> 1.19)
46
- ruby-progressbar (1.11.0)
47
- unicode-display_width (2.1.0)
68
+ unicode-display_width (>= 2.4.0, < 4.0)
69
+ rubocop-ast (1.49.1)
70
+ parser (>= 3.3.7.2)
71
+ prism (~> 1.7)
72
+ rubocop-rake (0.7.1)
73
+ lint_roller (~> 1.1)
74
+ rubocop (>= 1.72.1)
75
+ rubocop-rspec (3.10.2)
76
+ lint_roller (~> 1.1)
77
+ regexp_parser (>= 2.0)
78
+ rubocop (~> 1.86, >= 1.86.2)
79
+ ruby-progressbar (1.13.0)
80
+ tsort (0.2.0)
81
+ unicode-display_width (3.2.0)
82
+ unicode-emoji (~> 4.1)
83
+ unicode-emoji (4.2.0)
48
84
 
49
85
  PLATFORMS
86
+ arm64-darwin-25
50
87
  x86_64-darwin-18
51
88
  x86_64-darwin-19
52
89
  x86_64-linux
53
90
 
54
91
  DEPENDENCIES
55
- rake (~> 13.0)
92
+ irb
93
+ rake (~> 13.2)
56
94
  resource-struct!
57
95
  rspec (~> 3.0)
58
- rubocop (~> 1.21)
96
+ rubocop (~> 1.75)
59
97
  rubocop-rake
60
98
  rubocop-rspec
61
99
 
data/README.md CHANGED
@@ -7,11 +7,11 @@ This is a gem for working with JSON resources from a network source with indiffe
7
7
  Instead of overriding Hash implementation, this wraps a Hash with indifferent access (by symbol or string keys).
8
8
  This makes it fast at runtime, while still providing the necessary lookup method of choice.
9
9
 
10
- There are two types `ResouceStruct::StrictStruct` and `ResourceStruct::FlexStruct`.
10
+ There are two types `ResourceStruct::StrictStruct` and `ResourceStruct::FlexStruct`.
11
11
 
12
12
  `ResourceStruct::StrictStruct` provides a way of wrapping a Hash such that accesses to invalid keys will raise an exception through the method lookup method; it also is immutable.
13
13
 
14
- `ResouceStruct::FlexStruct` provides a way of wrapping a Hash such that it returns nil instead of raising an exception when the key is not present in the hash.
14
+ `ResourceStruct::FlexStruct` provides a way of wrapping a Hash such that it returns nil instead of raising an exception when the key is not present in the hash.
15
15
 
16
16
  ## Installation
17
17
 
@@ -71,7 +71,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
71
71
 
72
72
  ## Contributing
73
73
 
74
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/resource-struct.
74
+ Bug reports and pull requests are welcome on GitHub at https://github.com/AlexRiedler/resource-struct.
75
75
 
76
76
  ## License
77
77
 
@@ -13,10 +13,11 @@ module ResourceStruct
13
13
  def_delegators :@hash, :to_h, :to_hash, :to_s, :as_json, :to_json
14
14
 
15
15
  def initialize(hash = {})
16
- @hash = hash || {}
17
- @ro_struct = {}
16
+ hash = {} if hash.nil?
17
+ raise ::ArgumentError, "first argument must be a Hash, found #{hash.class.name}" unless hash.is_a?(Hash)
18
18
 
19
- raise ::ArgumentError, "first argument must be a Hash, found #{@hash.class.name}" unless @hash.is_a?(Hash)
19
+ @hash = ___canonicalize_hash(hash)
20
+ @ro_struct = {}
20
21
  end
21
22
 
22
23
  def inspect
@@ -24,24 +25,23 @@ module ResourceStruct
24
25
  end
25
26
 
26
27
  def ==(other)
27
- other.is_a?(Hash) && ___all_keys_equal(other) ||
28
- (other.is_a?(LooseStruct) || other.is_a?(FirmStruct)) &&
29
- ___all_keys_equal(other.instance_variable_get(:@hash))
28
+ other_hash = case other
29
+ when Hash then other
30
+ when LooseStruct, FirmStruct then other.instance_variable_get(:@hash)
31
+ else return false
32
+ end
33
+
34
+ ___all_keys_equal(other_hash)
30
35
  end
31
36
 
32
37
  def dig(key, *sub_keys)
33
38
  ckey = ___convert_key(key)
34
39
 
35
- result =
36
- if @ro_struct.key?(ckey)
37
- @ro_struct[ckey]
38
- elsif @hash.key?(key)
39
- @ro_struct[ckey] = ___convert_value(@hash[key])
40
- elsif key.is_a?(String) && @hash.key?(key.to_sym)
41
- @ro_struct[ckey] = ___convert_value(@hash[key.to_sym])
42
- elsif key.is_a?(Symbol) && @hash.key?(key.to_s)
43
- @ro_struct[ckey] = ___convert_value(@hash[key.to_s])
44
- end
40
+ result = if @ro_struct.key?(ckey)
41
+ @ro_struct[ckey]
42
+ elsif @hash.key?(ckey)
43
+ @ro_struct[ckey] = ___convert_value(@hash[ckey])
44
+ end
45
45
 
46
46
  return result if sub_keys.empty?
47
47
 
@@ -54,18 +54,35 @@ module ResourceStruct
54
54
  alias [] dig
55
55
 
56
56
  def marshal_dump
57
- {
58
- data: @hash
59
- }
57
+ { data: @hash }
60
58
  end
61
59
 
62
60
  def marshal_load(obj)
63
61
  @ro_struct = {}
64
- @hash = obj[:data]
62
+ @hash = ___canonicalize_hash(obj[:data] || {})
65
63
  end
66
64
 
67
65
  private
68
66
 
67
+ def ___canonicalize_hash(hash)
68
+ hash.each_with_object({}) do |(key, value), memo|
69
+ memo[___convert_key(key)] = ___canonicalize_value(value)
70
+ end
71
+ end
72
+
73
+ def ___canonicalize_value(value)
74
+ case value
75
+ when LooseStruct, FirmStruct
76
+ value.instance_variable_get(:@hash)
77
+ when ::Array
78
+ value.map { |v| ___canonicalize_value(v) }
79
+ when Hash
80
+ ___canonicalize_hash(value)
81
+ else
82
+ value
83
+ end
84
+ end
85
+
69
86
  def ___convert_value(value)
70
87
  case value
71
88
  when ::Array
@@ -78,9 +95,7 @@ module ResourceStruct
78
95
  end
79
96
 
80
97
  def ___key?(key)
81
- @hash.key?(key) ||
82
- @hash.key?(___convert_key(key)) ||
83
- key.is_a?(String) && @hash.key?(key.to_sym)
98
+ @hash.key?(___convert_key(key))
84
99
  end
85
100
 
86
101
  def ___convert_key(key)
@@ -90,17 +105,15 @@ module ResourceStruct
90
105
  def ___all_keys_equal(other)
91
106
  return false unless @hash.count == other.count
92
107
 
93
- @hash.reduce(true) do |acc, (k, _)|
94
- value = self[k]
95
- if other.key?(k)
96
- acc && value == other[k]
97
- elsif k.is_a?(String)
98
- ck = k.to_sym
99
- acc && other.key?(ck) && value == other[ck]
100
- else
101
- ck = ___convert_key(k)
102
- acc && other.key?(ck) && value == other[ck]
103
- end
108
+ @hash.all? do |k, _|
109
+ other_value = if other.key?(k)
110
+ other[k]
111
+ elsif k.is_a?(String) && other.key?(k.to_sym)
112
+ other[k.to_sym]
113
+ else
114
+ return false
115
+ end
116
+ self[k] == other_value
104
117
  end
105
118
  end
106
119
  end
@@ -22,18 +22,7 @@ module ResourceStruct
22
22
  def []=(key, value)
23
23
  ckey = ___convert_key(key)
24
24
  @ro_struct.delete(ckey)
25
-
26
- value = value.instance_variable_get(:@hash) if value.is_a?(FlexStruct) || value.is_a?(StrictStruct)
27
-
28
- if @hash.key?(key)
29
- @hash[key] = value
30
- elsif key.is_a?(String) && @hash.key?(key.to_sym)
31
- @hash[key.to_sym] = value
32
- elsif key.is_a?(Symbol) && @hash.key?(key.to_s)
33
- @hash[key.to_s] = value
34
- else
35
- @hash[key] = value
36
- end
25
+ @hash[ckey] = ___canonicalize_value(value)
37
26
  end
38
27
 
39
28
  def method_missing(name, *args)
@@ -22,7 +22,14 @@ module ResourceStruct
22
22
  class StrictStruct
23
23
  include ::ResourceStruct::Extensions::IndifferentLookup
24
24
 
25
- def method_missing(name, *args, &blk)
25
+ # StrictStruct is documented as immutable. Return a shallow copy so callers
26
+ # cannot mutate the internal canonicalized hash through #to_h / #to_hash.
27
+ def to_h
28
+ @hash.dup
29
+ end
30
+ alias to_hash to_h
31
+
32
+ def method_missing(name, *args, &)
26
33
  args_length = args.length
27
34
  return self[name] if ___key?(name) && args_length.zero?
28
35
  return !!self[name[...-1]] if name.end_with?("?") && args_length.zero?
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ResourceStruct
4
- VERSION = "0.4.0"
4
+ VERSION = "0.5.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: resource-struct
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Riedler
8
- autorequire:
9
- bindir: exe
8
+ bindir: bin
10
9
  cert_chain: []
11
- date: 2022-01-10 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies: []
13
12
  description: Openstruct like access without all the headaches of Hash method overrides
14
13
  etc...
@@ -41,7 +40,7 @@ metadata:
41
40
  homepage_uri: https://github.com/AlexRiedler/resource-struct
42
41
  source_code_uri: https://github.com/AlexRiedler/resource-struct
43
42
  changelog_uri: https://raw.githubusercontent.com/AlexRiedler/resource-struct/master/CHANGELOG.md
44
- post_install_message:
43
+ rubygems_mfa_required: 'true'
45
44
  rdoc_options: []
46
45
  require_paths:
47
46
  - lib
@@ -49,15 +48,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
49
48
  requirements:
50
49
  - - ">="
51
50
  - !ruby/object:Gem::Version
52
- version: 2.7.0
51
+ version: 3.3.0
53
52
  required_rubygems_version: !ruby/object:Gem::Requirement
54
53
  requirements:
55
54
  - - ">="
56
55
  - !ruby/object:Gem::Version
57
56
  version: '0'
58
57
  requirements: []
59
- rubygems_version: 3.1.6
60
- signing_key:
58
+ rubygems_version: 4.0.15
61
59
  specification_version: 4
62
60
  summary: Ruby structs for resource responses
63
61
  test_files: []