error_builder 0.2.0 → 0.4.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: 18b518b8436e2ef66d0bb88f820bef052f36df55a4e41bb28324fe0aebe237d8
4
+ data.tar.gz: 3c2430d0678d632349469410999bb82825ae5e07c1f36e185857222864e59a59
5
5
  SHA512:
6
- metadata.gz: 04abd9e0e2ad14d96a78ce758cbe5788dfa8f6488b6d58fcfe0831855a26a5ce2f05a7a090d22fb7e469436c8bf5957517636a4d23863409fd488582d2cae048
7
- data.tar.gz: '0394b4e8612224edc4cd25b677a02788ddb663a0a803f55abe0535b4157075a6aae5281f6a4683b8af59164f302b8c7d8b04d0065f01cff320f6c2e73531a608'
6
+ metadata.gz: 3d67f22c74f876da70c19d27267aa4036c796bed1737a18f3ca3da380e446f90705cf5e19a246bba2314bf8c126346545f385102e9a9c325b958205d508c1a3b
7
+ data.tar.gz: 91deb51af311a62b39edec12ddfc6440e7c02c8f13227d8cc7922ce5c59e0e198b4f728dc1ba085efd1619bdab6df7c107b4fd6173283f09daa4112cda6b570a
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.4.0] - 2025-04-13
4
+ - Added support of custom format classes.
5
+
6
+ ## [0.3.0] - 2025-04-06
7
+
8
+ - Changed Array and Hash formats.
9
+ - Added support for nested and flat error structures in both hash and array formats.
10
+ - Updated documentation and usage examples.
11
+ - Covered tests.
12
+
13
+ ## [0.2.0] - 2025-04-05
14
+
15
+ - Provide advanced way of error collecting
16
+
3
17
  ## [0.1.0] - 2025-03-30
4
18
 
5
19
  - Initial release
data/README.md CHANGED
@@ -34,7 +34,7 @@ If you have to use in Rails:
34
34
  3. You can configure the gem by using the ErrorBuilder.configure block:
35
35
  ```ruby
36
36
  ErrorBuilder.configure do |config|
37
- config.format = :hash # Supported formats: :hash, :array
37
+ config.format = :hash # Supported formats: :hash, :array, custom class format
38
38
  config.message_format = :string # Supported formats: :string, :array
39
39
  end
40
40
  ```
@@ -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,46 @@ 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"] }
96
+ ```
97
+
98
+ #### Using custom formats
99
+
100
+ ```ruby
101
+ class CustomFormat
102
+ def initialize(errors, **)
103
+ @errors = errors
104
+ end
105
+
106
+ def to_h
107
+ @errors.map { |error| "#{error.key} #{error.message.join(", ")}" }
108
+ end
109
+ end
110
+
111
+ errors = ErrorBuilder::Engine.new(format: CustomFormat)
112
+ errors.add(:base, "Something went wrong")
113
+ errors.to_h #=> ["base Something went wrong"]
75
114
  ```
76
115
 
77
116
  ## Development
@@ -16,15 +16,12 @@ module ErrorBuilder
16
16
  @errors << error
17
17
  end
18
18
 
19
- def to_h
20
- case format
21
- when :array
22
- Formats::Array.new(@errors).to_h
23
- when :hash
24
- Formats::Hash.new(@errors).to_h
25
- else
26
- raise ArgumentError, "Unsupported format: #{format}"
27
- end
19
+ def to_h(flat: false)
20
+ formatter.new(errors, flat:).to_h
21
+ end
22
+
23
+ def formatter
24
+ FormatResolver.new(format).formatter
28
25
  end
29
26
  end
30
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
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ErrorBuilder
4
+ class FormatResolver
5
+ attr_reader :format
6
+
7
+ def initialize(format)
8
+ @format = format
9
+ end
10
+
11
+ def formatter
12
+ return built_in_formatter unless format.is_a?(Class)
13
+
14
+ format
15
+ end
16
+
17
+ def built_in_formatter
18
+ case format
19
+ when :array
20
+ Formats::Array
21
+ when :hash
22
+ Formats::Hash
23
+ else
24
+ raise ArgumentError, "Unsupported format: #{format}"
25
+ end
26
+ end
27
+ end
28
+ 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: false)
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.4.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.4.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-13 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: zeitwerk
@@ -43,6 +43,7 @@ files:
43
43
  - lib/error_builder/configuration.rb
44
44
  - lib/error_builder/engine.rb
45
45
  - lib/error_builder/error.rb
46
+ - lib/error_builder/format_resolver.rb
46
47
  - lib/error_builder/formats/array.rb
47
48
  - lib/error_builder/formats/base.rb
48
49
  - lib/error_builder/formats/hash.rb
@@ -55,7 +56,7 @@ metadata:
55
56
  allowed_push_host: https://rubygems.org
56
57
  homepage_uri: https://github.com/mmarusyk/error_builder
57
58
  source_code_uri: https://github.com/mmarusyk/error_builder
58
- changelog_uri: https://github.com/yourusername/error_builder/blob/main/CHANGELOG.md
59
+ changelog_uri: https://github.com/mmarusyk/error_builder/blob/main/CHANGELOG.md
59
60
  rdoc_options: []
60
61
  require_paths:
61
62
  - lib