error_builder 0.2.0 → 0.3.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: 17afb5de8cfc97f5dc0d6618ae6397e938c64b3abe36de5fae6e9fd12b54173c
4
- data.tar.gz: e06de617586f31e251e154dc7b7c7abbbf92a5f04e7c73ba0ceb8ba4bb17ca6b
3
+ metadata.gz: fef4923015c619400c0faaeba5412188078ad963740c10f8dcfced24a65e3a8e
4
+ data.tar.gz: 77857ce23ee12bed15d4b53e3a9c47fcff761f1b51bbfb94309e2502db8af1b7
5
5
  SHA512:
6
- metadata.gz: 04abd9e0e2ad14d96a78ce758cbe5788dfa8f6488b6d58fcfe0831855a26a5ce2f05a7a090d22fb7e469436c8bf5957517636a4d23863409fd488582d2cae048
7
- data.tar.gz: '0394b4e8612224edc4cd25b677a02788ddb663a0a803f55abe0535b4157075a6aae5281f6a4683b8af59164f302b8c7d8b04d0065f01cff320f6c2e73531a608'
6
+ metadata.gz: 18b4a185c1d4b2f59405561539700b82e4a516ed5915d7a84ec63cef36c1b3f100c07fffe596a28a878096c657198a230c196f2f3936e2119e5ceb53c0469461
7
+ data.tar.gz: 3a68abedfdb7a80e72aec329afd3bb735153f7e5f4d5fbb8c55accbcca4f5e7887d2ee8750750da4e22c52b88f0fc8fc29f3cdbb19da2dc3ca68f6eb23e71c29
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.3.0] - 2025-04.06
4
+
5
+ - Changed Array and Hash formats.
6
+ - Added support for nested and flat error structures in both hash and array formats.
7
+ - Updated documentation and usage examples.
8
+ - Covered tests.
9
+
10
+ ## [0.2.0] - 2025-04-05
11
+
12
+ - Provide advanced way of error collecting
13
+
3
14
  ## [0.1.0] - 2025-03-30
4
15
 
5
16
  - Initial release
data/README.md CHANGED
@@ -53,7 +53,7 @@ errors.add(:base, "Something went wrong")
53
53
 
54
54
  3. Convert Errors to Hash or Array (depends on configuration)
55
55
  ```ruby
56
- errors.to_h #=> { errors: { base: ["Something went wrong"] } }
56
+ errors.to_h #=> { base: ["Something went wrong"] }
57
57
  ```
58
58
 
59
59
  ### Including in Classes
@@ -71,7 +71,28 @@ end
71
71
 
72
72
  my_service = MyService.new
73
73
  my_service.call
74
- my_service.errors.to_h #=> { errors: { base: ["Something went wrong"] } }
74
+ my_service.errors.to_h #=> { base: ["Something went wrong"] }
75
+ ```
76
+
77
+ ### Examples
78
+
79
+ #### Using nested keys
80
+
81
+ ```ruby
82
+ class MyService
83
+ include ErrorBuilder
84
+
85
+ def call
86
+ errors.add("user.locations[0].name", "must be present")
87
+
88
+ true
89
+ end
90
+ end
91
+
92
+ my_service = MyService.new
93
+ my_service.call
94
+ my_service.errors.to_h #=> { "user" => { "locations" => { 0 => { "name" => ["must be present"] } } } } }
95
+ my_service.errors.to_h(flat: true) #=> { "user.locations[0].name" => ["must be present"] }
75
96
  ```
76
97
 
77
98
  ## Development
@@ -16,12 +16,12 @@ module ErrorBuilder
16
16
  @errors << error
17
17
  end
18
18
 
19
- def to_h
19
+ def to_h(flat: false)
20
20
  case format
21
21
  when :array
22
- Formats::Array.new(@errors).to_h
22
+ Formats::Array.new(@errors, flat:).to_h
23
23
  when :hash
24
- Formats::Hash.new(@errors).to_h
24
+ Formats::Hash.new(@errors, flat:).to_h
25
25
  else
26
26
  raise ArgumentError, "Unsupported format: #{format}"
27
27
  end
@@ -29,9 +29,9 @@ module ErrorBuilder
29
29
  end
30
30
 
31
31
  def keys
32
- [key] if key.is_a?(Symbol)
32
+ return [key] unless key.to_s.include?(".")
33
33
 
34
- key.to_s.delete(":").split(".")
34
+ deflat_key
35
35
  end
36
36
 
37
37
  private
@@ -46,5 +46,26 @@ module ErrorBuilder
46
46
  raise ArgumentError, "Unsupported message format: #{format}"
47
47
  end
48
48
  end
49
+
50
+ def deflat_key
51
+ key
52
+ .to_s
53
+ .split(".")
54
+ .flat_map { |part| part.split(/[\[\]]+/) }
55
+ .reject(&:empty?)
56
+ .map { |part| parse_part(part) }
57
+ end
58
+
59
+ def parse_part(part)
60
+ if integer?(part)
61
+ part.to_i
62
+ else
63
+ key.is_a?(String) ? part : part.to_sym
64
+ end
65
+ end
66
+
67
+ def integer?(part)
68
+ part.match?(/\A\d+\z/)
69
+ end
49
70
  end
50
71
  end
@@ -4,22 +4,36 @@ module ErrorBuilder
4
4
  module Formats
5
5
  class Array < Base
6
6
  def to_h
7
- formatted = errors.map do |error|
8
- build_nested_error(error.keys, error.message)
9
- end
7
+ errors.each_with_object([]) do |error, array|
8
+ keys = flat ? [error.key] : error.keys
10
9
 
11
- { errors: formatted }
10
+ build_nested_error(array, keys, error.message)
11
+ end
12
12
  end
13
13
 
14
14
  private
15
15
 
16
- def build_nested_error(keys, value)
16
+ def build_nested_error(array, keys, value)
17
17
  key = keys.shift
18
18
 
19
19
  if keys.empty?
20
- [{ key: key.to_sym, value: value }]
20
+ array << [key, value]
21
+ else
22
+ nested_array = find_or_create_nested_array(array, key)
23
+
24
+ build_nested_error(nested_array, keys, value)
25
+ end
26
+ end
27
+
28
+ def find_or_create_nested_array(array, key)
29
+ existing = array.find { |e| e.is_a?(::Array) && e.first == key }
30
+
31
+ if existing
32
+ existing[1]
21
33
  else
22
- [{ key: key.to_sym, value: build_nested_error(keys, value) }]
34
+ new_array = [key, []]
35
+ array << new_array
36
+ new_array[1]
23
37
  end
24
38
  end
25
39
  end
@@ -3,10 +3,11 @@
3
3
  module ErrorBuilder
4
4
  module Formats
5
5
  class Base
6
- attr_reader :errors
6
+ attr_reader :errors, :flat
7
7
 
8
- def initialize(errors)
8
+ def initialize(errors, flat:)
9
9
  @errors = errors
10
+ @flat = flat
10
11
  end
11
12
 
12
13
  def to_h
@@ -4,24 +4,34 @@ module ErrorBuilder
4
4
  module Formats
5
5
  class Hash < Base
6
6
  def to_h
7
- formatted = errors.each_with_object({}) do |error, hash|
8
- set_nested_key(hash, error.keys, error.message)
9
- end
7
+ errors.each_with_object({}) do |error, hash|
8
+ keys = flat ? [error.key] : error.keys
10
9
 
11
- { errors: formatted }
10
+ add_nested_key(hash, keys, error.message)
11
+ end
12
12
  end
13
13
 
14
14
  private
15
15
 
16
- def set_nested_key(hash, keys, value)
16
+ def add_nested_key(hash, keys, value)
17
17
  key = keys.shift
18
18
 
19
19
  if keys.empty?
20
- hash[key.to_sym] = value
20
+ add_message(hash, key, value)
21
21
  else
22
- hash[key.to_sym] ||= {}
22
+ hash[key] ||= {}
23
23
 
24
- set_nested_key(hash[key.to_sym], keys, value)
24
+ add_nested_key(hash[key], keys, value)
25
+ end
26
+ end
27
+
28
+ def add_message(hash, key, value)
29
+ if value.is_a?(::Array)
30
+ hash[key] ||= []
31
+
32
+ hash[key] += value
33
+ else
34
+ hash[key] = value
25
35
  end
26
36
  end
27
37
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ErrorBuilder
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.0"
5
5
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: error_builder
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mykhailo Marusyk
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-04-05 00:00:00.000000000 Z
10
+ date: 2025-04-06 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: zeitwerk
@@ -55,7 +55,7 @@ metadata:
55
55
  allowed_push_host: https://rubygems.org
56
56
  homepage_uri: https://github.com/mmarusyk/error_builder
57
57
  source_code_uri: https://github.com/mmarusyk/error_builder
58
- changelog_uri: https://github.com/yourusername/error_builder/blob/main/CHANGELOG.md
58
+ changelog_uri: https://github.com/mmarusyk/error_builder/blob/main/CHANGELOG.md
59
59
  rdoc_options: []
60
60
  require_paths:
61
61
  - lib