Skip to content

Commit

Permalink
Merge branch 'master' into v1.1.0
Browse files Browse the repository at this point in the history
Conflicts:
	CHANGELOG.md
  • Loading branch information
tfausak committed Feb 4, 2014
2 parents 5d73d1d + cfe351c commit 630c440
Show file tree
Hide file tree
Showing 15 changed files with 114 additions and 51 deletions.
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
was required.
- Speed up many filters by caching class constants.

# [1.0.1][] (2014-02-04)

- Short circuit `valid?` after successfully running an interaction.
- Fix a bug that prevented merging interpolated symbolic errors.
- Use `:invalid_type` instead of `:invalid` as I18n key for type errors.
- Fix a bug that skipped setting up accessors for imported filters.

# [1.0.0][] (2014-01-21)

- **Replace `Filters` with a hash.** To iterate over `Filter` objects, use
Expand Down Expand Up @@ -124,7 +131,8 @@

- Initial release.

[master]: https://github.com/orgsync/active_interaction/compare/v1.0.0...master
[master]: https://github.com/orgsync/active_interaction/compare/v1.0.1...master
[1.0.0]: https://github.com/orgsync/active_interaction/compare/v1.0.0...v1.0.1
[1.0.0]: https://github.com/orgsync/active_interaction/compare/v0.10.2...v1.0.0
[0.10.2]: https://github.com/orgsync/active_interaction/compare/v0.10.1...v0.10.2
[0.10.1]: https://github.com/orgsync/active_interaction/compare/v0.10.0...v0.10.1
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ hsilgne:
errors:
messages:
invalid: dilavni si
invalid_nested: '%{type} dilav a ton si'
invalid_type: '%{type} dilav a ton si'
missing: deriuqer si
```

Expand Down
2 changes: 1 addition & 1 deletion lib/active_interaction.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,5 @@
#
# @since 1.0.0
#
# @version 1.0.0
# @version 1.0.1
module ActiveInteraction end
23 changes: 16 additions & 7 deletions lib/active_interaction/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ class << self
include Missable

# @!method run(inputs = {})
# @note If the interaction inputs are valid and there are no runtime
# errors and execution completed successfully, {#valid?} will always
# return true.
#
# Runs validations and if there are no errors it will call {#execute}.
#
# @param (see ActiveInteraction::Base#initialize)
Expand Down Expand Up @@ -102,12 +106,7 @@ def method_missing(*args, &block)
def add_filter(klass, name, options, &block)
fail InvalidFilterError, name.inspect if reserved?(name)

filter = klass.new(name, options, &block)
filters[name] = filter
attr_accessor name
define_method("#{name}?") { !public_send(name).nil? }

filter.default if filter.default?
initialize_filter(klass.new(name, options, &block))
end

# Import filters from another interaction.
Expand All @@ -130,14 +129,24 @@ def import_filters(klass, options = {})
other_filters.select! { |k, _| [*only].include?(k) } if only
other_filters.reject! { |k, _| [*except].include?(k) } if except

filters.merge!(other_filters)
other_filters.values.each { |filter| initialize_filter(filter) }
end

# @param klass [Class]
def inherited(klass)
klass.instance_variable_set(:@_interaction_filters, filters.dup)
end

# @param filter [Filter]
def initialize_filter(filter)
filters[filter.name] = filter

attr_accessor filter.name
define_method("#{filter.name}?") { !public_send(filter.name).nil? }

filter.default if filter.default?
end

# @param symbol [Symbol]
#
# @return [Boolean]
Expand Down
8 changes: 7 additions & 1 deletion lib/active_interaction/concerns/runnable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,21 @@ def result=(result)
if errors.empty?
@_interaction_result = result
@_interaction_runtime_errors = nil
@_interaction_valid = true
else
@_interaction_result = nil
@_interaction_runtime_errors = errors.dup
@_interaction_valid = false
end
end

# @return [Boolean]
def valid?(*)
super || (self.result = nil)
unless instance_variable_defined?(:@_interaction_valid)
@_interaction_valid = false
end

@_interaction_valid || super || (self.result = nil)
end

private
Expand Down
7 changes: 3 additions & 4 deletions lib/active_interaction/errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,14 @@ class Errors < ActiveModel::Errors
def add_sym(attribute, symbol = :invalid, message = nil, options = {})
add(attribute, message || symbol, options)

symbolic[attribute] ||= []
symbolic[attribute] << symbol
symbolic[attribute] += [symbol]
end

# @see ActiveModel::Errors#initialize
#
# @private
def initialize(*)
@symbolic = {}.with_indifferent_access
@symbolic = Hash.new([]).with_indifferent_access

super
end
Expand Down Expand Up @@ -128,7 +127,7 @@ def clear
# @return [Errors]
def merge!(other)
other.symbolic.each do |attribute, symbols|
symbols.each { |s| add_sym(attribute, s) }
symbols.each { |s| symbolic[attribute] += [s] }
end

other.messages.each do |attribute, messages|
Expand Down
4 changes: 2 additions & 2 deletions lib/active_interaction/locale/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ en:
time: time
errors:
messages:
invalid_nested: is invalid
invalid: is not a valid %{type}
invalid: is invalid
invalid_type: is not a valid %{type}
missing: is required
2 changes: 1 addition & 1 deletion lib/active_interaction/modules/validation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def self.validate(filters, inputs)
begin
filter.cast(inputs[name])
rescue InvalidValueError
errors << [name, :invalid, nil, type: type(filter)]
errors << [name, :invalid_type, nil, type: type(filter)]
rescue MissingValueError
errors << [name, :missing]
end
Expand Down
2 changes: 1 addition & 1 deletion lib/active_interaction/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ module ActiveInteraction
# The version number.
#
# @return [Gem::Version]
VERSION = Gem::Version.new('1.0.0')
VERSION = Gem::Version.new('1.0.1')
end
14 changes: 14 additions & 0 deletions spec/active_interaction/base_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,20 @@ def filters(klass)
described_class
expect(klass.filters).to eq filters
end

it 'responds to readers, writers, and predicates' do
instance = described_class.new

described_class.filters.keys.each do |name|
[name, "#{name}=", "#{name}?"].each do |method|
expect(instance).to respond_to method
end
end
end
end

context 'with neither :only nor :except' do
include_examples 'import_filters examples'
end

context 'with :only' do
Expand Down
29 changes: 23 additions & 6 deletions spec/active_interaction/concerns/runnable_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,6 @@
it 'returns false' do
expect(instance).to_not be_valid
end

it 'sets the result to nil' do
instance.result = result
instance.valid?
expect(instance.result).to be_nil
end
end
end

Expand Down Expand Up @@ -162,6 +156,29 @@
end
end
end

context 'with invalid post-execution state' do
before do
klass.class_exec do
attr_accessor :attribute

validate { errors.add(:attribute) if attribute }

def execute
self.attribute = true
end
end
end

it 'is valid' do
expect(outcome).to be_valid
end

it 'stays valid' do
outcome.attribute = true
expect(outcome).to be_valid
end
end
end

describe '.run!' do
Expand Down
18 changes: 17 additions & 1 deletion spec/active_interaction/errors_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
attr_reader :attribute

def self.name
SecureRandom.hex
@name ||= SecureRandom.hex
end
end
end
Expand Down Expand Up @@ -129,5 +129,21 @@ def self.name
expect(errors.symbolic[:attribute]).to eq [:invalid]
end
end

context 'with an interpolated symbolic error' do
before do
I18n.backend.store_translations('en', activemodel: {
errors: { models: { klass.name => { attributes: { attribute: {
invalid_type: 'is not a valid %{type}'
} } } } }
})

other.add_sym(:attribute, :invalid_type, type: nil)
end

it 'does not raise an error' do
expect { errors.merge!(other) }.to_not raise_error
end
end
end
end
36 changes: 15 additions & 21 deletions spec/active_interaction/i18n_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,30 +39,24 @@
let(:translation) { I18n.translate(key, type: type, raise: true) }
let(:type) { I18n.translate("#{described_class.i18n_scope}.types.hash") }

context ':invalid' do
let(:key) { "#{described_class.i18n_scope}.errors.messages.invalid" }
shared_examples 'translations' do |key, value|
context key.inspect do
let(:key) { "#{described_class.i18n_scope}.errors.messages.#{key}" }

it 'has a translation' do
expect { translation }.to_not raise_error
end

it 'returns the translation' do
inputs.merge!(a: Object.new)
expect(outcome.errors[:a]).to eq [translation]
end
end
before { inputs[:a] = value }

context ':missing' do
let(:key) { "#{described_class.i18n_scope}.errors.messages.missing" }
it 'has a translation' do
expect { translation }.to_not raise_error
end

it 'has a translation' do
expect { translation }.to_not raise_error
end

it 'returns the translation' do
expect(outcome.errors[:a]).to eq [translation]
it 'returns the translation' do
expect(outcome.errors[:a]).to include translation
end
end
end

include_examples 'translations', :invalid_type, Object.new
include_examples 'translations', :missing, nil
end
end

Expand All @@ -80,8 +74,8 @@
before do
I18n.backend.store_translations('hsilgne', active_interaction: {
errors: { messages: {
invalid: "%{type} #{'invalid'.reverse}",
invalid_nested: 'invalid_nested'.reverse,
invalid: 'is invalid'.reverse,
invalid_type: "%{type} #{'is not a valid'.reverse}",
missing: 'missing'.reverse
} },
types: TYPES.each_with_object({}) { |e, a| a[e] = e.reverse }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def ==(other)
it 'handles time zone changes' do
outcome
allow(Time).to receive(:zone).and_return(nil)
expect(outcome).to be_invalid
expect(described_class.run(inputs)).to be_invalid
end
end
end
Expand Down
6 changes: 3 additions & 3 deletions spec/active_interaction/modules/validation_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,18 @@
let(:exception) { ActiveInteraction::InvalidValueError }
let(:filter) { ActiveInteraction::FloatFilter.new(:name, {}) }

it 'returns an :invalid_nested error' do
it 'returns an :invalid_type error' do
type = I18n.translate(
"#{ActiveInteraction::Base.i18n_scope}.types.#{filter.class.slug}")

expect(result).to eq [[filter.name, :invalid, nil, type: type]]
expect(result).to eq [[filter.name, :invalid_type, nil, type: type]]
end
end

context 'MissingValueError' do
let(:exception) { ActiveInteraction::MissingValueError }

it 'returns an :invalid_nested error' do
it 'returns an :msising error' do
expect(result).to eq [[filter.name, :missing]]
end
end
Expand Down

0 comments on commit 630c440

Please sign in to comment.