deep_hash_transformer 2.2.1 → 3.0.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: cd3cd6700a7e51238c1c16feb30a81fb8debe53c5225401f33442ef29d771c92
4
- data.tar.gz: 93aeb34e4a4e0bdbefa97d6d946d21fc8ab87457bd8361d992aa66399100849e
3
+ metadata.gz: 61bf68f57a229a41899b5c91536acfd67d687bc6e32b3b0d783f67607ce63d7d
4
+ data.tar.gz: da363a1e6058dc2bd8c997600cc5dc8e64b347acf5026836b51bb801027f5d83
5
5
  SHA512:
6
- metadata.gz: 515fe31abc501135498b1450e9f90820c7612161e643198f9276006095abc5eff05e5c54cb924c37ae4e801e078a9cf9f26ccb17900341b2b7f093e86f9d4c03
7
- data.tar.gz: 4cb38f5bcfcf1265f77244e61c8d4b13f0d833a52b6ef4e334cbe43bbe157eeee4a3c467f5d08f45b2e738cfee936395cee3a073e79e2c9453a7ba8aef7ab001
6
+ metadata.gz: 0a809f6749a8d01eff82ac032735c82f238d456da8a59476c873d469a0210a995d62d1cbe924e6de1fcf67900dfd8026754c745c63426dea291f7cd8457c2d07
7
+ data.tar.gz: a1162926bfe17950fcf918e19ecdb7f065f33b425dba4a66a1b515a1f0c62b68124c83adfd5f109637124a3163b5ef5773939b4790f77017bc4740bb5912f8a6
data/CHANGELOG.md ADDED
@@ -0,0 +1,78 @@
1
+ *3.0.0 (September 30, 2026)*
2
+
3
+ Maintenance status:
4
+
5
+ * Enter maintenance mode: the feature set is considered complete and no new features
6
+ are planned. Security fixes may be released when needed. Compatibility with future
7
+ Ruby versions is not guaranteed.
8
+
9
+ Breaking behavior changes:
10
+
11
+ * Execute String operation names; previously accepted String names did nothing.
12
+ * Reject operation-name objects other than Strings or Symbols with ArgumentError.
13
+ * Lowercase Unicode initial capitals in camel_case (über_name becomes überName).
14
+ * Transform String-subclass keys instead of skipping them.
15
+ * Raise ArgumentError for cycles through collection values instead of SystemStackError.
16
+
17
+ Fixes and maintenance:
18
+
19
+ * Recognize valid UTF-16/UTF-32 whitespace during compact_blank cleanup.
20
+ * Normalize and partition operations once per call, skip cleanup dispatch on scalars,
21
+ and build hashes directly to reduce traversal time and allocations. See benchmark
22
+ methodology and measured limits in docs/performance.md in the source repository.
23
+ * Preserve last-value-wins collisions, combined traversal, and existing leaf sharing.
24
+ * Restore coverage instrumentation before library loading and add behavioral regression tests.
25
+ * Correct operation descriptions, the ActiveSupport comparison, and the contribution link.
26
+ * Package README.md and CHANGELOG.md; omit specs from the runtime package.
27
+ * Retain Ruby 3.0 support and test Ruby 3.0–3.4 and 4.0 using their latest patch releases.
28
+ * Add isolated package verification and a reproducible runtime/allocation benchmark.
29
+
30
+ See README migration notes before upgrading.
31
+
32
+ *2.2.1 (December 25, 2023)*
33
+
34
+ * Ensure Ruby 3.3 compability
35
+ * Stop testing against Ruby < 3.0
36
+ * Switch from Rubocop to Standard
37
+
38
+ *2.2.0 (May 17, 2023)*
39
+
40
+ * Two new transformations added:
41
+ - compact
42
+ - compact_blank
43
+ * Ensure Ruby 3.2 compability
44
+ * Ensure Ruby 3.3.0-preview compability
45
+ * Rename default branch to `main`
46
+
47
+ *2.1.0 (March 06, 2022)*
48
+
49
+ * Three new transformations added:
50
+ - snake_case (thanks, @Finnegan5),
51
+ - camel_case,
52
+ - pascal_case
53
+ * Improved error message when called with an unknown transformation name
54
+ * Improved documentation
55
+ * Ensure Ruby 3.1 compability
56
+ * Switch from Travis CI to GitHub Actions
57
+
58
+ *2.0.0 (December 28, 2020)*
59
+
60
+ * Ensure Ruby 2.6 compability
61
+ * Ensure Ruby 2.7 compability
62
+ * Ensure Ruby 3.0 compability
63
+ * Drops support for Ruby <= 2.2
64
+ * Drops support for Ruby 2.3
65
+ * Drops support for Ruby 2.4
66
+
67
+ *1.0.0 (December 26, 2017)*
68
+
69
+ * Runs specs against Ruby up to version 2.5
70
+ * Drops support for Ruby <= 2.1
71
+
72
+ *0.1.0 (April 27, 2017)*
73
+
74
+ * Initial version
75
+
76
+ *0.1.1 (April 27, 2017)*
77
+
78
+ * Fix `undefined method 'Array#to_h'` on Ruby '< 2.1'
data/README.md ADDED
@@ -0,0 +1,217 @@
1
+ # DeepHashTransformer
2
+
3
+ Transform keys and recursively clean nested hashes and arrays without runtime dependencies.
4
+ Combine operations to convert API data into Ruby-friendly structures:
5
+
6
+ ```ruby
7
+ require "deep_hash_transformer"
8
+
9
+ input = {
10
+ "nested-array" => [{"a-key" => "a-value", "b-key" => nil}],
11
+ "foo_bar" => "baz"
12
+ }
13
+
14
+ DeepHashTransformer.new(input).tr(:underscore, :symbolize, :compact)
15
+ # => {nested_array: [{a_key: "a-value"}], foo_bar: "baz"}
16
+ ```
17
+
18
+ [![License MIT](https://img.shields.io/badge/license-MIT-brightgreen.svg)](MIT-LICENSE)
19
+ [![Gem Version](https://badge.fury.io/rb/deep_hash_transformer.svg)](https://rubygems.org/gems/deep_hash_transformer)
20
+ [![Build Status](https://github.com/spickermann/deep_hash_transformer/actions/workflows/CI.yml/badge.svg)](https://github.com/spickermann/deep_hash_transformer/actions/workflows/CI.yml)
21
+
22
+ ## Maintenance status
23
+
24
+ DeepHashTransformer is in maintenance mode. Its feature set is considered complete,
25
+ and no new features are planned. Security fixes may be released when needed.
26
+ Compatibility with future Ruby versions is not guaranteed.
27
+
28
+ For general-purpose recursive key transformations, consider ActiveSupport’s
29
+ [`deep_transform_keys`](https://api.rubyonrails.org/classes/Hash.html#method-i-deep_transform_keys),
30
+ which supports nested hashes and arrays.
31
+
32
+ DeepHashTransformer remains useful for applications that need combined key
33
+ transformations and recursive cleanup without runtime dependencies. If that
34
+ matches your requirements, the existing functionality remains available and
35
+ documented.
36
+
37
+ ## Installation
38
+
39
+ Ruby **3.0 or newer** is required. CI tests the latest patch releases of Ruby 3.0,
40
+ 3.1, 3.2, 3.3, 3.4 and 4.0. Older series remain supported while the implementation
41
+ can support them without version-specific behavior.
42
+
43
+ Add `gem "deep_hash_transformer", "~> 3.0"` to your Gemfile and run `bundle install`,
44
+ or run `gem install deep_hash_transformer`.
45
+
46
+ ## Operations
47
+
48
+ ```ruby
49
+ DeepHashTransformer.new({foo_bar: "baz"}).camel_case
50
+ # => {"fooBar" => "baz"}
51
+
52
+ DeepHashTransformer.new({"FooBar" => "baz"}).tr("snake_case", :symbolize)
53
+ # => {foo_bar: "baz"}
54
+ ```
55
+
56
+ `tr` accepts known operation names as symbols or strings; other arguments raise
57
+ `ArgumentError`. Each operation also has a method of the same name.
58
+
59
+ | Operation | Effect on keys or collections |
60
+ |---|---|
61
+ | `camel_case` | `"foo_bar"` → `"fooBar"`; `"über_name"` → `"überName"` |
62
+ | `pascal_case` | `"foo_bar"` → `"FooBar"` |
63
+ | `snake_case` | `"HTTPServer"` → `"http_server"` |
64
+ | `dasherize` | Replaces underscores with dashes: `"foo_bar"` → `"foo-bar"` |
65
+ | `underscore` | Replaces dashes with underscores: `:"foo-bar"` → `"foo_bar"` |
66
+ | `stringify` | Converts symbol keys to strings: `:foo` → `"foo"` |
67
+ | `symbolize` | Converts string keys to symbols: `"foo"` → `:foo` |
68
+ | `identity` | Leaves keys unchanged |
69
+ | `compact` | Removes `nil` hash values and array elements |
70
+ | `compact_blank` | Removes blank hash values and array elements, recursively |
71
+
72
+ Key operations apply to strings (including subclasses) and symbols. Other key
73
+ types are unchanged; collections used as keys are not traversed. Case and
74
+ separator conversions return strings; `symbolize` returns symbols. `identity`,
75
+ `compact`, and `compact_blank` preserve key types. `tr` without arguments copies
76
+ the collection structure without converting keys.
77
+
78
+ Word-boundary detection remains ASCII-based. There is no acronym registry or
79
+ locale-specific inflection: `camel_case` converts `"HTTPServer"` to `"hTTPServer"`.
80
+ Its initial-letter lowercasing supports Unicode, but that does not make all word
81
+ segmentation Unicode-aware. `underscore` only replaces separators; use
82
+ `snake_case` when you also want case boundaries converted.
83
+
84
+ ## Combined operations and collisions
85
+
86
+ `tr` combines operations in **one recursive traversal**, not a series of complete
87
+ transformations. At each hash:
88
+
89
+ 1. Key operations run in their supplied order, and values are recursively transformed.
90
+ 2. The result hash is assembled. If multiple keys become equal, **the last value wins**.
91
+ 3. Collection operations run in their supplied order on the result.
92
+
93
+ Arrays transform their elements before collection operations run. Cleanup thus
94
+ proceeds from the inside out. The position of a collection operation relative to
95
+ a key operation does not change this model:
96
+
97
+ ```ruby
98
+ input = {"a" => 1, :a => nil}
99
+ DeepHashTransformer.new(input).tr(:compact, :stringify)
100
+ # => {}: stringify first causes a collision; compact then removes the winning nil.
101
+ DeepHashTransformer.new(input).tr(:stringify, :compact)
102
+ # => {}
103
+ ```
104
+
105
+ For complete sequential transformations, use separate instances:
106
+
107
+ ```ruby
108
+ cleaned = DeepHashTransformer.new(input).compact
109
+ DeepHashTransformer.new(cleaned).stringify
110
+ # => {"a" => 1}
111
+ ```
112
+
113
+ Key-operation order still matters: `tr(:snake_case, :symbolize)` returns symbol
114
+ keys, while `tr(:symbolize, :snake_case)` returns string keys.
115
+
116
+ ## Recursive cleanup and root values
117
+
118
+ `compact` removes only `nil`, retaining `false`, whitespace, and empty collections.
119
+ `compact_blank` additionally removes `false`, empty strings/collections, and
120
+ strings containing only whitespace. Zero remains. Blankness honors an object's
121
+ `blank?` method when present, otherwise checks string whitespace or `empty?`.
122
+ User-defined methods and loaded extensions can therefore affect blankness.
123
+
124
+ ```ruby
125
+ DeepHashTransformer.new({a: {b: [nil, {c: " "}]}}).compact_blank
126
+ # => {}
127
+ DeepHashTransformer.new({a: [nil]}).compact
128
+ # => {a: []}
129
+ DeepHashTransformer.new([nil, {"FooBar" => 1}]).tr(:snake_case, :compact)
130
+ # => [{"foo_bar" => 1}]
131
+ ```
132
+
133
+ Array roots are supported. Scalar roots are returned unchanged, even `nil`,
134
+ `false`, or whitespace: cleanup removes entries from collections, not the root
135
+ itself. Empty roots remain `{}` or `[]`.
136
+
137
+ Whitespace detection includes Unicode whitespace such as nonbreaking space, and
138
+ supports valid UTF-16/UTF-32 strings. Zero-width space is not whitespace here.
139
+ Invalid byte sequences raise Ruby argument/encoding errors; they are not silently
140
+ discarded. This encoding support concerns cleanup, not every key conversion.
141
+
142
+ ## Mutation, references, and supported structures
143
+
144
+ The transformer does not mutate ordinary input collections or values. Frozen
145
+ collections are accepted. Results contain new, mutable hashes and arrays **per
146
+ occurrence**; shared input collections are copied independently. Mutable leaf
147
+ objects and unchanged non-string keys remain shared. Mutating a returned leaf
148
+ can therefore change the input's leaf; this is not a general-purpose deep copy.
149
+ The input is read when a transformation runs, not snapshotted at initialization.
150
+
151
+ Output collections are ordinary `Hash` and `Array` instances. Subclass identity,
152
+ hash defaults/default procs, `compare_by_identity`, and frozen state are not
153
+ preserved. Ordinary key equality applies even to identity hashes. Transformed
154
+ string keys need not retain their subclass.
155
+
156
+ Cycles through hash values and array elements raise `ArgumentError` with a
157
+ `cyclic Hash/Array structure` message. Repeated references without cycles are
158
+ allowed. Recursion depth is bounded by Ruby's stack; extremely deep acyclic
159
+ inputs can still raise `SystemStackError`. Heavily shared graphs may expand
160
+ substantially because collections are copied per occurrence. Custom methods on
161
+ user objects are responsible for their own side effects.
162
+
163
+ ## Ruby and ActiveSupport
164
+
165
+ Ruby's `transform_keys` and `compact` handle one collection level.
166
+ [ActiveSupport's `deep_transform_keys`](https://api.rubyonrails.org/classes/Hash.html#method-i-deep_transform_keys)
167
+ already traverses nested hashes **and arrays**. Use it when that API meets your
168
+ needs, especially in an application that already loads ActiveSupport.
169
+
170
+ DeepHashTransformer offers named, combinable operations and recursive cleanup
171
+ without runtime dependencies or core-class extensions. It is not a drop-in
172
+ replacement for ActiveSupport's key-type, subclass, or inflection behavior, nor
173
+ is it guaranteed to be faster. See [benchmark methodology and results](https://github.com/spickermann/deep_hash_transformer/blob/main/docs/performance.md)
174
+ in the source repository.
175
+
176
+ ## Migrating from 2.2.1
177
+
178
+ Version 3.0.0 intentionally changes these results:
179
+
180
+ - String operation names now execute instead of silently doing nothing.
181
+ - `camel_case` lowercases Unicode initial capitals: `"über_name"` now becomes
182
+ `"überName"`, previously `"ÜberName"`.
183
+ - String subclasses used as keys now participate in transformations.
184
+ - Cyclic collections raise `ArgumentError` instead of exhausting the stack.
185
+ - Arbitrary objects are no longer accepted as operation names via `to_s`.
186
+
187
+ Review generated keys and possible new collisions, especially if they identify
188
+ persisted fields or external API parameters. Last-value collision resolution and
189
+ the combined traversal model remain unchanged. Ruby 3.0 support is retained.
190
+ README and Changelog are now packaged; specs are no longer included in the gem.
191
+
192
+ ## Development and release
193
+
194
+ Run `bundle install`, then `bundle exec rake` for tests and Standard.
195
+ `ruby script/verify_package.rb` builds the gem under `pkg/`, checks its contents,
196
+ and installs/tests that exact package in a temporary isolated gem directory.
197
+ It does not publish anything. The committed `Gemfile.lock` fixes development
198
+ dependencies across the supported Ruby versions. Update it using the oldest
199
+ supported Ruby, then verify the complete CI matrix before committing the update.
200
+ The lockfile is not included in the published gem and adds no runtime dependencies.
201
+
202
+ See [release procedure](https://github.com/spickermann/deep_hash_transformer/blob/main/docs/releasing.md) in the source repository for the
203
+ separate review and publication steps. `bundle exec rake release` is a publishing
204
+ command: it creates and pushes a Git tag, pushes the branch, and uploads the gem.
205
+ It does **not** run the test suite first or create a GitHub Release.
206
+
207
+ ## Contributing
208
+
209
+ Bug reports and pull requests are welcome at
210
+ [spickermann/deep_hash_transformer](https://github.com/spickermann/deep_hash_transformer).
211
+ [Fork this repository](https://github.com/spickermann/deep_hash_transformer/fork),
212
+ create a branch, add tests for your change, and open a pull request.
213
+ Please follow the [Code of Conduct](https://github.com/spickermann/deep_hash_transformer/blob/main/CODE_OF_CONDUCT.md).
214
+
215
+ ## License
216
+
217
+ [MIT](MIT-LICENSE).
@@ -15,7 +15,7 @@ class DeepHashTransformer
15
15
  def blank?
16
16
  return true unless value
17
17
  return value.blank? if value.respond_to?(:blank?)
18
- return BLANK_STRING.match?(value) if value.is_a?(String)
18
+ return blank_string? if value.is_a?(String)
19
19
  return value.empty? if value.respond_to?(:empty?)
20
20
 
21
21
  false
@@ -24,5 +24,11 @@ class DeepHashTransformer
24
24
  private
25
25
 
26
26
  attr_reader :value
27
+
28
+ def blank_string?
29
+ BLANK_STRING.match?(value)
30
+ rescue Encoding::CompatibilityError
31
+ BLANK_STRING.match?(value.encode(Encoding::UTF_8))
32
+ end
27
33
  end
28
34
  end
@@ -5,7 +5,7 @@ class DeepHashTransformer
5
5
  class << self
6
6
  def camel_case(val)
7
7
  pascal_case(val)
8
- .sub(/^[A-Z]/, &:downcase)
8
+ .sub(/\A[[:upper:]]/, &:downcase)
9
9
  end
10
10
 
11
11
  def dasherize(val)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class DeepHashTransformer
4
- VERSION = "2.2.1"
4
+ VERSION = "3.0.0"
5
5
  end
@@ -21,21 +21,22 @@ class DeepHashTransformer
21
21
  compact_blank
22
22
  ].freeze
23
23
 
24
- OPS = ELEMENT_OPS + COLLECTION_OPS
24
+ OPS = (ELEMENT_OPS + COLLECTION_OPS).freeze
25
25
 
26
26
  def initialize(hash)
27
27
  @hash = hash
28
28
  end
29
29
 
30
30
  def tr(*ops)
31
- unknown_transformations = ops.map(&:to_s) - OPS.map(&:to_s)
32
- if unknown_transformations.any?
33
- raise(
34
- ArgumentError, "unknown transformation(s): #{unknown_transformations.join(",")}"
35
- )
31
+ ops = ops.map do |op|
32
+ name = op.to_sym if op.is_a?(String) || op.is_a?(Symbol)
33
+ raise ArgumentError, "unknown transformation: #{op.inspect}" unless OPS.include?(name)
34
+
35
+ name
36
36
  end
37
37
 
38
- transform_value(hash, ops)
38
+ key_ops, collection_ops = ops.partition { |op| ELEMENT_OPS.include?(op) }
39
+ transform_value(hash, key_ops, collection_ops, {}.compare_by_identity)
39
40
  end
40
41
 
41
42
  OPS.each do |operation|
@@ -48,28 +49,36 @@ class DeepHashTransformer
48
49
 
49
50
  def transform_collection(collection, ops)
50
51
  ops.inject(collection) do |c, op|
51
- COLLECTION_OPS.include?(op) ? CollectionOperation.public_send(op, c) : c
52
+ CollectionOperation.public_send(op, c)
52
53
  end
53
54
  end
54
55
 
55
- def transform_value(value, ops)
56
- collection = case value
57
- when Array
58
- value.map { |e| transform_value(e, ops) }
59
- when Hash
60
- value.map { |k, v| [transform_key(k, ops), transform_value(v, ops)] }.to_h
61
- else
62
- value
63
- end
56
+ def transform_value(value, key_ops, collection_ops, ancestors)
57
+ return value unless value.is_a?(Array) || value.is_a?(Hash)
58
+
59
+ raise ArgumentError, "cyclic Hash/Array structure" if ancestors.key?(value)
64
60
 
65
- transform_collection(collection, ops)
61
+ ancestors[value] = true
62
+ begin
63
+ collection = if value.is_a?(Array)
64
+ value.map { |e| transform_value(e, key_ops, collection_ops, ancestors) }
65
+ else
66
+ result = {}
67
+ value.each { |k, v| result[transform_key(k, key_ops)] = transform_value(v, key_ops, collection_ops, ancestors) }
68
+ result
69
+ end
70
+
71
+ transform_collection(collection, collection_ops)
72
+ ensure
73
+ ancestors.delete(value)
74
+ end
66
75
  end
67
76
 
68
77
  def transform_key(key, ops)
69
- return key unless [String, Symbol].include?(key.class)
78
+ return key unless key.is_a?(String) || key.is_a?(Symbol)
70
79
 
71
80
  ops.inject(key) do |k, op|
72
- ELEMENT_OPS.include?(op) ? ElementOperation.public_send(op, k) : k
81
+ ElementOperation.public_send(op, k)
73
82
  end
74
83
  end
75
84
  end
metadata CHANGED
@@ -1,40 +1,39 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: deep_hash_transformer
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.1
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Spickermann
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2023-12-25 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies: []
13
- description: " DeepHashTransformer helps to transform keys in deeply nested hash
14
- structures\n"
12
+ description: " Dependency-free key transformations and recursive cleanup for nested
13
+ hashes and arrays.\n"
15
14
  email:
16
15
  - spickermann@gmail.com
17
16
  executables: []
18
17
  extensions: []
19
18
  extra_rdoc_files: []
20
19
  files:
20
+ - CHANGELOG.md
21
21
  - MIT-LICENSE
22
+ - README.md
22
23
  - lib/deep_hash_transformer.rb
23
24
  - lib/deep_hash_transformer/blank.rb
24
25
  - lib/deep_hash_transformer/collection_operation.rb
25
26
  - lib/deep_hash_transformer/element_operation.rb
26
27
  - lib/deep_hash_transformer/version.rb
27
- - spec/deep_hash_transformer/blank_spec.rb
28
- - spec/deep_hash_transformer/collection_operation_spec.rb
29
- - spec/deep_hash_transformer/element_operation_spec.rb
30
- - spec/deep_hash_transformer_spec.rb
31
- - spec/spec_helper.rb
32
28
  homepage: https://github.com/spickermann/deep_hash_transformer
33
29
  licenses:
34
30
  - MIT
35
31
  metadata:
36
32
  rubygems_mfa_required: 'true'
37
- post_install_message:
33
+ allowed_push_host: https://rubygems.org
34
+ source_code_uri: https://github.com/spickermann/deep_hash_transformer
35
+ changelog_uri: https://github.com/spickermann/deep_hash_transformer/blob/main/CHANGELOG.md
36
+ bug_tracker_uri: https://github.com/spickermann/deep_hash_transformer/issues
38
37
  rdoc_options: []
39
38
  require_paths:
40
39
  - lib
@@ -49,8 +48,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
49
48
  - !ruby/object:Gem::Version
50
49
  version: '0'
51
50
  requirements: []
52
- rubygems_version: 3.5.3
53
- signing_key:
51
+ rubygems_version: 4.0.20
54
52
  specification_version: 4
55
- summary: Transforms deeply nested hash structure
53
+ summary: Transforms keys and cleans deeply nested hashes and arrays
56
54
  test_files: []
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe DeepHashTransformer::Blank do
4
- describe ".call" do
5
- subject { described_class.call(value) }
6
-
7
- presents = [true, 1, 0, "any", [nil], {A: nil}]
8
- blanks = [nil, false, "", " ", [], {}]
9
-
10
- presents.each do |value|
11
- context "with #{value.inspect}" do
12
- let(:value) { value }
13
-
14
- it { is_expected.to be false }
15
- end
16
- end
17
-
18
- blanks.each do |value|
19
- context "with #{value.inspect}" do
20
- let(:value) { value }
21
-
22
- it { is_expected.to be true }
23
- end
24
- end
25
- end
26
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe DeepHashTransformer::CollectionOperation do
4
- examples = [
5
- {a: "b"}, {a: nil}, {a: " "}, [:foo], ["", nil]
6
- ]
7
-
8
- subject do
9
- examples.map { |value| described_class.public_send(method, value) }
10
- end
11
-
12
- describe ".compact" do
13
- let(:method) { :compact }
14
-
15
- expected_output = [
16
- {a: "b"}, {}, {a: " "}, [:foo], [""]
17
- ]
18
-
19
- it { is_expected.to eq(expected_output) }
20
- end
21
-
22
- describe ".compact_blank" do
23
- let(:method) { :compact_blank }
24
-
25
- expected_output = [
26
- {a: "b"}, {}, {}, [:foo], []
27
- ]
28
-
29
- it { is_expected.to eq(expected_output) }
30
- end
31
- end
@@ -1,32 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe DeepHashTransformer::ElementOperation do
4
- examples = [
5
- :symbol, "string", "camelCase", "dashed-key", "PascalCase", "under_scored"
6
- ]
7
-
8
- subject do
9
- examples.map { |value| described_class.public_send(method, value) }
10
- end
11
-
12
- # rubocop:disable Layout/HashAlignment, Style/SymbolArray, Style/WordArray
13
- expected_output = {
14
- camel_case: ["symbol", "string", "camelCase", "dashedKey", "pascalCase", "underScored"],
15
- dasherize: ["symbol", "string", "camelCase", "dashed-key", "PascalCase", "under-scored"],
16
- identity: [:symbol, "string", "camelCase", "dashed-key", "PascalCase", "under_scored"],
17
- pascal_case: ["Symbol", "String", "CamelCase", "DashedKey", "PascalCase", "UnderScored"],
18
- snake_case: ["symbol", "string", "camel_case", "dashed_key", "pascal_case", "under_scored"],
19
- stringify: ["symbol", "string", "camelCase", "dashed-key", "PascalCase", "under_scored"],
20
- symbolize: [:symbol, :string, :camelCase, :"dashed-key", :PascalCase, :under_scored],
21
- underscore: ["symbol", "string", "camelCase", "dashed_key", "PascalCase", "under_scored"]
22
- }
23
- # rubocop:enable Layout/HashAlignment, Style/SymbolArray, Style/WordArray
24
-
25
- DeepHashTransformer::ELEMENT_OPS.each do |method|
26
- describe "##{method}" do
27
- let(:method) { method }
28
-
29
- it { is_expected.to eq(expected_output[method]) }
30
- end
31
- end
32
- end
@@ -1,128 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe DeepHashTransformer do
4
- subject(:operation) { described_class.new(example) }
5
-
6
- let(:example) { {foo: "bar"} }
7
-
8
- describe "autogenerated methods" do
9
- DeepHashTransformer::ELEMENT_OPS.each do |method|
10
- describe ".#{method}" do
11
- it "delegates to `Operation.#{method}`" do
12
- expect(
13
- DeepHashTransformer::ElementOperation
14
- ).to receive(method).with(:foo).once # rubocop:disable RSpec/MessageSpies
15
-
16
- operation.public_send(method)
17
- end
18
- end
19
- end
20
- end
21
-
22
- describe "#tr" do
23
- context "with `:snake_case, :symbolize`" do
24
- subject { super().tr(:snake_case, :symbolize) }
25
-
26
- let(:example) { {"FooBar" => "baz"} }
27
-
28
- it { is_expected.to eq(foo_bar: "baz") }
29
- end
30
-
31
- context "with an unknown transformation" do
32
- subject(:unknown_ops) { operation.tr(:unknown) }
33
-
34
- it "raise an exception" do
35
- expect { unknown_ops }.to raise_error(ArgumentError, /unknown/)
36
- end
37
- end
38
-
39
- context "with a complex, nested example" do
40
- subject { super().tr(:camel_case, :symbolize) }
41
-
42
- let(:example) do
43
- {
44
- Integer => 123,
45
- :symbol => {foo_bar: "bar"},
46
- "string" => {"foo_bar" => 123},
47
- "nested-array" => [
48
- {
49
- "camelCased" => "camelCased",
50
- "dashed-key" => "dashed-key",
51
- "PascalCased" => "PascalCased",
52
- "under_scored" => "under_scored"
53
- }
54
- ]
55
- }
56
- end
57
-
58
- it do # rubocop:disable RSpec/ExampleLength
59
- is_expected.to eq( # rubocop:disable RSpec/ImplicitSubject
60
- Integer => 123,
61
- :symbol => {fooBar: "bar"},
62
- :string => {fooBar: 123},
63
- :nestedArray => [
64
- {
65
- camelCased: "camelCased",
66
- dashedKey: "dashed-key",
67
- pascalCased: "PascalCased",
68
- underScored: "under_scored"
69
- }
70
- ]
71
- )
72
- end
73
- end
74
-
75
- context "with nil values" do
76
- subject { super().tr(:compact, :stringify) }
77
-
78
- let(:example) do
79
- {a: {b: ["", nil, :value], c: "", d: nil, e: true, f: false, g: 123, h: [""]}}
80
- end
81
-
82
- it do # rubocop:disable RSpec/ExampleLength
83
- is_expected.to eq( # rubocop:disable RSpec/ImplicitSubject
84
- "a" => {
85
- "b" => ["", :value],
86
- "c" => "",
87
- "e" => true,
88
- "f" => false,
89
- "g" => 123,
90
- "h" => [""]
91
- }
92
- )
93
- end
94
- end
95
-
96
- context "with blank values" do
97
- subject { super().tr(:compact_blank, :stringify) }
98
-
99
- let(:example) do
100
- {a: {b: ["", nil, :value], c: "", d: nil, e: true, f: false, g: 123, h: [""]}}
101
- end
102
-
103
- it do # rubocop:disable RSpec/ExampleLength
104
- is_expected.to eq( # rubocop:disable RSpec/ImplicitSubject
105
- "a" => {
106
- "b" => [:value],
107
- "e" => true,
108
- "g" => 123
109
- }
110
- )
111
- end
112
- end
113
-
114
- context "with deeply nested blank values" do
115
- subject { super().tr(:compact_blank) }
116
-
117
- let(:example) do
118
- {a: {b: [nil, {c: nil}]}}
119
- end
120
-
121
- it { is_expected.to eq({}) }
122
- end
123
- end
124
-
125
- it "has a version number" do
126
- expect(DeepHashTransformer::VERSION).not_to be_nil
127
- end
128
- end
data/spec/spec_helper.rb DELETED
@@ -1,36 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/setup"
4
- require "deep_hash_transformer"
5
- require "deep_hash_transformer/blank"
6
- require "deep_hash_transformer/collection_operation"
7
- require "deep_hash_transformer/element_operation"
8
-
9
- require "simplecov"
10
-
11
- SimpleCov.start do
12
- if ENV["CI"]
13
- require "simplecov_json_formatter"
14
- require "simplecov-lcov"
15
-
16
- SimpleCov::Formatter::LcovFormatter.config do |c|
17
- c.report_with_single_file = true
18
- c.single_report_path = "coverage/lcov.info"
19
- end
20
-
21
- formatter SimpleCov::Formatter::MultiFormatter.new [
22
- SimpleCov::Formatter::JSONFormatter, SimpleCov::Formatter::LcovFormatter
23
- ]
24
- end
25
-
26
- add_filter %w[version.rb initializer.rb]
27
- end
28
-
29
- RSpec.configure do |config|
30
- # Enable flags like --only-failures and --next-failure
31
- config.example_status_persistence_file_path = ".rspec_status"
32
-
33
- config.expect_with :rspec do |c|
34
- c.syntax = :expect
35
- end
36
- end