Skip to content

Commit

Permalink
Add NestedError and ability to import
Browse files Browse the repository at this point in the history
Fix #6
  • Loading branch information
lulalala committed Dec 14, 2017
1 parent bb02157 commit b4ee77e
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 1 deletion.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,22 @@ Same as Rails, provide the attribute name to see if that attribute has errors.

Same as built-in counterparts.

## `import`

For copying an error from inner model to outer model, such as form object. This ensures lazy message generation can still reference all information that the inner error has.

```ruby
inner_model.errors.adequate.each do |error|
errors.adequate.import(error)
end
```

Attribute and type can be overriden in case when attribute does not exist in the outer model:

```ruby
errors.adequate.import(error, attribute: :foo, type: :bar)
```

## Message and I18n

Error message strings reside under `adequate_errors` namespace. Unlike Rails, there is no global prefixing of attributes. Instead, `%{attribute}` is added into each error message when needed.
Expand Down
13 changes: 13 additions & 0 deletions lib/adequate_errors/errors.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require 'active_model/errors'
require 'forwardable'
require 'adequate_errors/error'
require 'adequate_errors/nested_error'

module AdequateErrors
# Collection of {Error} objects.
Expand Down Expand Up @@ -45,6 +46,18 @@ def add(attribute, type = :invalid, options = {})
@errors.append(::AdequateErrors::Error.new(@base, attribute, type, options))
end

# Imports error
# For copying nested model's errors back to base model.
# The provided error will be wrapped, and its attribute/type will be copied across.
# If attribute or type needs to be overriden, use `override_options`.
#
# @param override_options [Hash]
# @option override_options [Symbol] :attribute Override the attribute the error belongs to
# @option override_options [Symbol] :type Override type of the error.
def import(error, override_options = {})
@errors.append(::AdequateErrors::NestedError.new(@base, error, override_options))
end

# @return [Array(String)] all error messages
def messages
@errors.map(&:message)
Expand Down
30 changes: 30 additions & 0 deletions lib/adequate_errors/nested_error.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
module AdequateErrors
# Represents one single error
# @!attribute [r] base
# @return [ActiveModel::Base] the object which the error belongs to
# @!attribute [r] attribute
# @return [Symbol] attribute of the object which the error belongs to
# @!attribute [r] type
# @return [Symbol] error's type
# @!attribute [r] options
# @return [Hash] additional options
# @!attribute [r] inner_error
# @return [Error] inner error
class NestedError < Error
def initialize(base, inner_error, override_options = {})
@base = base
@inner_error = inner_error
@attribute = override_options.fetch(:attribute) { inner_error.attribute }
@type = override_options.fetch(:type) { inner_error.type }
@options = inner_error.options
end

attr_reader :inner_error

# Full message of the error.
# Sourced from inner error.
def message
@inner_error.message
end
end
end
2 changes: 1 addition & 1 deletion lib/adequate_errors/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module AdequateErrors
VERSION = "0.1.0"
VERSION = "0.1.1"
end
17 changes: 17 additions & 0 deletions test/cases/adequate_errors/errors_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,23 @@
end
end

describe '#import' do
let(:inner_error) { AdequateErrors::Error.new(model, :foo, :not_attractive) }

it 'creates a NestedError' do
assert_equal 0, subject.size

subject.import(inner_error)

assert_equal 1, subject.size
error = subject.first
assert_equal :foo, error.attribute
assert_equal :not_attractive, error.type
assert_equal AdequateErrors::NestedError, error.class
assert_equal inner_error, error.inner_error
end
end

describe '#delete' do
it 'assigns attributes' do
subject.add(:title, :not_attractive)
Expand Down
41 changes: 41 additions & 0 deletions test/cases/adequate_errors/nested_error_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
require "cases/helper"
require "minitest/autorun"
require 'rspec/mocks/minitest_integration'
require 'adequate_errors'
require "models/topic"
require "models/reply"

describe AdequateErrors::NestedError do
let(:topic) { Topic.new }
let(:inner_error) { AdequateErrors::Error.new(topic, :mineral, :not_enough, count: 2) }

let(:reply) { Reply.new }
subject { AdequateErrors::NestedError.new(reply, inner_error) }

describe '#initialize' do
it 'assigns attributes' do
assert_equal reply, subject.base
assert_equal inner_error.attribute, subject.attribute
assert_equal inner_error.type, subject.type
assert_equal(inner_error.options, subject.options)
end

describe 'overriding attribute and type' do
subject { AdequateErrors::NestedError.new(reply, inner_error, attribute: :parent, type: :foo) }

it 'assigns attributes' do
assert_equal reply, subject.base
assert_equal :parent, subject.attribute
assert_equal :foo, subject.type
assert_equal(inner_error.options, subject.options)
end
end
end

describe '#message' do
it "return inner error's message" do
expect(inner_error).to receive(:message)
subject.message
end
end
end

0 comments on commit b4ee77e

Please sign in to comment.