Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Interface for Operations #469

Merged
merged 10 commits into from
Oct 15, 2020
104 changes: 77 additions & 27 deletions spec/define_attribute_spec.cr
Original file line number Diff line number Diff line change
@@ -1,6 +1,23 @@
require "./spec_helper"

private class Operation < Post::SaveOperation
private class OperationWithAttributes < Avram::Operation
param_key :data
attribute title : String
attribute count : Int32
attribute checked : Bool = false
attribute thing : String = "taco"
file_attribute :thumb

def update_count
count.value = 4
end

def run
[title, count]
end
end

private class SaveOperationWithAttributes < Post::SaveOperation
attribute password_confirmation : String
attribute terms_of_service : Bool
attribute best_kind_of_bear : String = "black bear"
Expand All @@ -20,13 +37,19 @@ end

describe "attribute in operations" do
it "is a PermittedAttribute" do
operation.password_confirmation.should be_a(Avram::PermittedAttribute(String?))
operation.password_confirmation.name.should eq(:password_confirmation)
operation.password_confirmation.param_key.should eq("post")
operation.title.should be_a(Avram::PermittedAttribute(String?))
operation.title.name.should eq(:title)
operation.title.param_key.should eq("data")

save_operation.password_confirmation.should be_a(Avram::PermittedAttribute(String?))
save_operation.password_confirmation.name.should eq(:password_confirmation)
save_operation.password_confirmation.param_key.should eq("post")
end

it "generates a list of attributes" do
operation.attributes.map(&.name).should eq [
operation.attributes.map(&.name).should eq [:thumb, :thing, :checked, :count, :title]

save_operation.attributes.map(&.name).should eq [
:thumb,
:default_is_false,
:best_kind_of_bear,
Expand All @@ -41,69 +64,79 @@ describe "attribute in operations" do
end

it "sets a default value of nil if another one is not given" do
operation.password_confirmation.value.should be_nil
operation.terms_of_service.value.should be_nil
operation.title.value.should be_nil
operation.count.value.should be_nil
save_operation.password_confirmation.value.should be_nil
save_operation.terms_of_service.value.should be_nil
end

it "assigns the default value to an attribute if one is set and no param is given" do
operation.best_kind_of_bear.value.should eq "black bear"
operation.default_is_false.value.should be_false
operation.checked.value.should eq false
operation.thing.value.should eq "taco"
save_operation.best_kind_of_bear.value.should eq "black bear"
save_operation.default_is_false.value.should be_false
end

it "overrides the default value with a param if one is given" do
operation({"best_kind_of_bear" => "brown bear"}).best_kind_of_bear.value.should eq "brown bear"
operation({"best_kind_of_bear" => ""}).best_kind_of_bear.value.should be_nil
operation({"title" => "Random Food"}).title.value.should eq "Random Food"
operation({"count" => "4"}).count.value.should eq 4
operation({"checked" => "true"}).checked.value.should eq true
save_operation({"best_kind_of_bear" => "brown bear"}).best_kind_of_bear.value.should eq "brown bear"
save_operation({"best_kind_of_bear" => ""}).best_kind_of_bear.value.should be_nil
end

it "sets the param and value based on the passed in params" do
operation = operation({"password_confirmation" => "password"})
operation = operation({"title" => "secret"})
operation.title.value.should eq "secret"
operation.title.param.should eq "secret"

operation.password_confirmation.value.should eq "password"
operation.password_confirmation.param.should eq "password"
save_operation = save_operation({"password_confirmation" => "password"})
save_operation.password_confirmation.value.should eq "password"
save_operation.password_confirmation.param.should eq "password"
end

it "is memoized so you can change the attribute in `prepare`" do
operation = operation({"password_confirmation" => "password"})
operation = save_operation({"password_confirmation" => "password"})
operation.password_confirmation.value.should eq "password"

operation.prepare
operation.password_confirmation.value.should eq "reset"
end

it "parses the value using the declared type" do
operation = operation({"terms_of_service" => "1"})
operation = save_operation({"terms_of_service" => "1"})
operation.terms_of_service.value.should be_true

operation = operation({"terms_of_service" => "0"})
operation = save_operation({"terms_of_service" => "0"})
operation.terms_of_service.value.should be_false
end

it "gracefully handles invalid params" do
operation = operation({"terms_of_service" => "not a boolean"})
operation = save_operation({"terms_of_service" => "not a boolean"})
operation.terms_of_service.value.should be_nil
operation.terms_of_service.errors.first.should eq "is invalid"
end

it "includes attribute errors when calling SaveOperation#valid?" do
operation = operation({"terms_of_service" => "not a boolean"})
operation = save_operation({"terms_of_service" => "not a boolean"})
operation.setup_required_database_columns
operation.valid?.should be_false
end

it "can still save to the database" do
params = {"password_confirmation" => "password", "terms_of_service" => "1"}
operation = operation(params)
operation = save_operation(params)
operation.setup_required_database_columns
operation.save.should eq true
end

it "sets named args for attributes, leaves other empty" do
Operation.create(title: "My Title", best_kind_of_bear: "brown bear") do |operation, post|
SaveOperationWithAttributes.create(title: "My Title", best_kind_of_bear: "brown bear") do |operation, post|
operation.best_kind_of_bear.value.should eq("brown bear")
operation.terms_of_service.value.should be_nil
post.should_not be_nil

Operation.update(post.not_nil!, best_kind_of_bear: "koala bear") do |op, _post|
SaveOperationWithAttributes.update(post.not_nil!, best_kind_of_bear: "koala bear") do |op, _post|
op.best_kind_of_bear.value.should eq("koala bear")
end
end
Expand All @@ -114,37 +147,54 @@ describe "file_attribute in operation" do
it "is a PermittedAttribute" do
operation.thumb.should be_a(Avram::PermittedAttribute(Avram::Uploadable?))
operation.thumb.name.should eq(:thumb)
operation.thumb.param_key.should eq("post")
operation.thumb.param_key.should eq("data")

save_operation.thumb.should be_a(Avram::PermittedAttribute(Avram::Uploadable?))
save_operation.thumb.name.should eq(:thumb)
save_operation.thumb.param_key.should eq("post")
end

it "is included in the list of attributes" do
operation.attributes.map(&.name).should contain(:thumb)
save_operation.attributes.map(&.name).should contain(:thumb)
end

it "gracefully handles invalid params" do
operation = operation({"thumb" => "not a file"})
operation.thumb.value.should be_nil
operation.thumb.errors.first.should eq "is invalid"

save_operation = save_operation({"thumb" => "not a file"})
save_operation.thumb.value.should be_nil
save_operation.thumb.errors.first.should eq "is invalid"
end

it "includes file attribute errors when calling SaveOperation#valid?" do
operation = operation({"thumb" => "not a file"})
operation = save_operation({"thumb" => "not a file"})
operation.setup_required_database_columns
operation.valid?.should be_false
end

it "can still save to the database" do
params = {"thumb" => Avram::UploadedFile.new("thumb.png")}
operation = upload_operation(params)
operation = upload_save_operation(params)
operation.setup_required_database_columns
operation.save.should eq true
end
end

private def operation(attrs = {} of String => String)
Operation.new(Avram::Params.new(attrs))
OperationWithAttributes.new(Avram::Params.new(attrs))
end

private def upload_operation(attrs = {} of String => Avram::Uploadable)
Operation.new(Avram::UploadParams.new(attrs))
OperationWithAttributes.new(Avram::UploadParams.new(attrs))
end

private def save_operation(attrs = {} of String => String)
SaveOperationWithAttributes.new(Avram::Params.new(attrs))
end

private def upload_save_operation(attrs = {} of String => Avram::Uploadable)
SaveOperationWithAttributes.new(Avram::UploadParams.new(attrs))
end
52 changes: 52 additions & 0 deletions spec/operation_callbacks_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
require "./spec_helper"

module TestableOperation
macro included
@callbacks_that_ran = [] of String
getter callbacks_that_ran

def mark_callback(callback_name : String)
@callbacks_that_ran << callback_name
end
end
end

private class OperationWithCallbacks < Avram::Operation
include TestableOperation
attribute number : Int32 = 1

before_run :update_number
before_run { mark_callback("before_run_in_a_block") }

after_run :notify_complete
after_run do |return_value_of_run_method|
mark_callback("after_run_in_a_block with #{return_value_of_run_method}")
end

def run
number.value
end

private def update_number
mark_callback("before_run_update_number")
number.value = 4
end

private def notify_complete(return_value_of_run_method)
mark_callback("after_run_notify_complete is #{return_value_of_run_method}")
end
end

describe "Avram::Operation callbacks" do
it "runs before_run and after_run callbacks" do
OperationWithCallbacks.run do |operation, value|
operation.callbacks_that_ran.should contain "before_run_update_number"
operation.callbacks_that_ran.should contain "before_run_in_a_block"
value.should eq 4
operation.number.value.should eq 4
operation.number.original_value.should eq 1
operation.callbacks_that_ran.should contain "after_run_notify_complete is 4"
operation.callbacks_that_ran.should contain "after_run_in_a_block with 4"
end
end
end
32 changes: 32 additions & 0 deletions spec/operation_needs_spec.cr
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
require "./spec_helper"

private class OperationWithNeeds < Avram::Operation
needs tags : Array(String)
needs id : Int32
attribute title : String
attribute published : Bool = false

def run
tags.join(", ")
end
end

class Needs::SaveOperation < User::SaveOperation
before_save prepare

Expand All @@ -20,6 +31,27 @@ private class NeedsSaveOperation < Needs::SaveOperation
needs optional : String = "bar"
end

describe "Avram::Operation needs" do
it "sets up named args for needs" do
OperationWithNeeds.run(tags: ["one", "two"], id: 3) do |operation, value|
value.should eq "one, two"
operation.tags.should eq ["one", "two"]
operation.id.should eq 3
end
end

it "allows params to be passed in along with named args for needs" do
params = Avram::Params.new({"title" => "test", "published" => "true"})
OperationWithNeeds.run(params, tags: ["one", "two"], id: 3) do |operation, value|
value.should eq "one, two"
operation.tags.should eq ["one", "two"]
operation.id.should eq 3
operation.title.value.should eq "test"
operation.published.value.should eq true
end
end
end

describe "Avram::SaveOperation needs" do
it "sets up a method arg for save, update, and new" do
params = Avram::Params.new({"name" => "Paul"})
Expand Down
Loading