Skip to content

Commit

Permalink
Created new default_validations which allows us to run the polymorphi…
Browse files Browse the repository at this point in the history
…c validations after before_saves. Fixes #645 (#751)
  • Loading branch information
jwoertink authored Oct 17, 2021
1 parent e19f67c commit 73bbc85
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 1 deletion.
27 changes: 27 additions & 0 deletions spec/operations/save_operation_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,34 @@ private class AllowBlankComment < Comment::SaveOperation
end
end

module DefaultUserValidations
macro included
property starts_nil : String? = nil

default_validations do
self.starts_nil = self.starts_nil.to_s + "!"
validate_required nickname
end
end
end

private class UserWithDefaultValidations < User::SaveOperation
include DefaultUserValidations

before_save do
self.starts_nil = "not nil"
end
end

describe "Avram::SaveOperation" do
it "calls the default validations after the before_save" do
UserWithDefaultValidations.create(name: "TestName", nickname: "TestNickname", joined_at: Time.utc, age: 400) do |op, u|
op.starts_nil.should eq("not nil!")
u.should_not be_nil
u.not_nil!.nickname.should eq("TestNickname")
end
end

it "allows overriding the param_key" do
ParamKeySaveOperation.param_key.should eq "custom_param"
end
Expand Down
14 changes: 14 additions & 0 deletions spec/polymorphic_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,21 @@ private class OptionalPolymorphicEvent < BaseModel
end
end

class TestPolymorphicSave < PolymorphicEvent::SaveOperation
before_save do
task = PolymorphicTask::SaveOperation.create!(title: "Use Lucky")
task_id.value = task.id
end
end

describe "polymorphic belongs to" do
it "allows you to set the association before save" do
TestPolymorphicSave.create do |op, tp|
op.valid?.should eq(true)
tp.should_not be_nil
end
end

it "sets up a method for accessing associated record" do
task = PolymorphicTask::SaveOperation.create!(title: "Use Lucky")
event = PolymorphicEvent::SaveOperation.create!(task_id: task.id)
Expand Down
4 changes: 4 additions & 0 deletions src/avram/delete_operation.cr
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,13 @@ abstract class Avram::DeleteOperation(T)
end
end

# :nodoc:
def default_validations; end

# Returns `true` if all attributes are valid,
# and there's no custom errors
def valid?
default_validations
custom_errors.empty? && attributes.all?(&.valid?)
end

Expand Down
4 changes: 4 additions & 0 deletions src/avram/operation.cr
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,13 @@ abstract class Avram::Operation
@params = Avram::Params.new
end

# :nodoc:
def default_validations; end

# Returns `true` if all attributes are valid,
# and there's no custom errors
def valid?
default_validations
custom_errors.empty? && attributes.all?(&.valid?)
end

Expand Down
5 changes: 4 additions & 1 deletion src/avram/polymorphic.cr
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,10 @@ module Avram::Polymorphic

macro finished
class SaveOperation
before_save do
# These validations must be run after all of the `before_save`
# in case anyone sets their polymorphic association in a callback.
# These are ran in the SaveOperation#valid? method
default_validations do
{% list_of_foreign_keys = associations.map(&.id).map { |assoc| "#{assoc.id}_id".id } %}

# TODO: Needs to actually get the foreign key from the ASSOCIATIONS constant
Expand Down
7 changes: 7 additions & 0 deletions src/avram/save_operation.cr
Original file line number Diff line number Diff line change
Expand Up @@ -206,10 +206,17 @@ abstract class Avram::SaveOperation(T)
end
end

# Runs all required validations for required types
# as well as any additional valitaions the type needs to run
# e.g. polymorphic validations
def run_default_validations
validate_required *required_attributes
default_validations
end

# :nodoc:
def default_validations; end

# This allows you to skip the default validations
# which may be used as an escape hatch when you want
# to allow storing an empty string value.
Expand Down
25 changes: 25 additions & 0 deletions src/avram/validations.cr
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,31 @@ require "./validations/callable_error_message"
module Avram::Validations
extend self

macro included
abstract def default_validations
end

# Defines an instance method that gets called
# during validation of an operation. Define your default
# validations inside of the block.
# ```
# default_validations do
# validate_required some_attribute
# end
# ```
macro default_validations
# :nodoc:
def default_validations
{% if @type.methods.map(&.name).includes?(:default_validations.id) %}
previous_def
{% else %}
super
{% end %}

{{ yield }}
end
end

# Validates that at most one attribute is filled
#
# If more than one attribute is filled it will mark all but the first filled
Expand Down

0 comments on commit 73bbc85

Please sign in to comment.