Yesterday we started into our handler test for the Initiated
event, and I gushed a little too much about how in Eventide you can just instantiate things and call methods on them.
And call methods we will today! Remember you can find all the final code in this repo.
We left the test off in this state:
context "Initiated" do
handler = Handlers::Commands.new
end
We’re going to end up actuating our handler with a Build
event, so we could go ahead and make one here:
context "Initiated" do
handler = Handlers::Commands.new
build = Messages::Commands::Build.new
end
Worth calling out, the automated_init.rb
file we require at the top of our test file require_relatives
a test_init.rb
. This file has a line include CompositeComponent
which include
s our component’s top-level namespace. That’s how we’re able to use modules under CompositeComponent
like Handlers
and Messages
without listing the full namespace like CompositeComponent::Handlers::Commands
. Not needed, just convention.
Anyway, we want to keep our code organized, and we’re going to have messages. Some of them will be commands, hence the Messages::Commands
namespacing. In this test we’re handling the Build
event. If we ran the test now, we’ll get an error because none among Messages
, Commands
, or Build
exists:
$ ruby test/automated/handle_commands/build/initiated.rb
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [arm64-darwin21]
TEST_BENCH_DETAIL: nil
Handle Commands
Build
Initiated
test/automated/handle_commands/build/initiated.rb:8:in `block (3 levels) in <main>': uninitialized constant Messages (NameError)
So we need to define our message class in lib/composite_component/messages/commands/build.rb
, with just enough code to get the test to pass:
module CompositeComponent
module Messages
module Commands
class Build
end
end
end
end
If we run the test here, it passes again:
$ ruby test/automated/handle_commands/build/initiated.rb
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [arm64-darwin21]
TEST_BENCH_DETAIL: nil
Handle Commands
Build
Initiated
You might be thinking that the command looks pretty empty right now. What data does it hold? Doesn’t matter for where we are in the test right now. There is nothing in the test that requires the message to have any attributes, so we’re not going to add them yet.
We are, however, going to actuate the handler now. Remember me gushing about instantiating things and calling methods on them? Here’s where we do that:
context "Initiated" do
handler = Handlers::Commands.new
build = Messages::Commands::Build.new
handler.(build)
end
Mmmm. So simple. No hyper-specialized class that our test has to inherit from. Instantiate and call. Incidentally, if you’re not familiar with Ruby, handler.()
is equivalent to calling a call
method on handler
or handler.call()
. I did not have fun googling that when I first learned it.
Running the test, we’ll get a NoMethodError
because handler
has no method call
:
$ ruby test/automated/handle_commands/build/initiated.rb
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [arm64-darwin21]
TEST_BENCH_DETAIL: nil
Handle Commands
Build
Initiated
test/automated/handle_commands/build/initiated.rb:17:in `block (3 levels) in <main>': undefined method `call' for #<CompositeComponent::Handlers::Commands:0x00000001062e97b0> (NoMethodError)
Thus far, it’s just a PORO, or Plain Old Ruby Object. Let’s make it a Handler in lib/composite_component/handlers/commands.rb
:
module CompositeComponent
module Handlers
class Commands
include Messaging::Handle
end
end
end
Messaging::Handle
comes from Eventide, and include
ing it turns our class into a Handler. We didn’t need it to be a Handler until this point in the test because this is the first time we’ve call
ed it.
However, if we run the test again, we’ll get a NoMethodError
because our Build
instance has no message_name
method on it:
$ ruby test/automated/handle_commands/build/initiated.rb
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [arm64-darwin21]
TEST_BENCH_DETAIL: nil
Handle Commands
Build
Initiated
/path/to/component/composite-component/gems/ruby/3.2.0/gems/evt-messaging-2.7.0.1/lib/messaging/handle.rb:110:in `handler_method_name': undefined method `message_name' for #<CompositeComponent::Messages::Commands::Build:0x00000001043956c0> (NoMethodError)
It’s also currently just a PORO and not yet a Message
. Let’s turn it into one in lib/composite_component/messages/commands/build.rb
(some lines omitted for space):
module Commands
class Build
include Messaging::Message
end
end
Messaging::Message
is also from Eventide
, and it includes the methods that will let the rest of Eventide recognize a class as a Message
. Now if we run the test, we’re back to passing:
$ ruby test/automated/handle_commands/build/initiated.rb
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [arm64-darwin21]
TEST_BENCH_DETAIL: nil
Handle Commands
Build
Initiated
We need to wrap it up here today. We didn’t get as far as I thought we would. So be it.
You may wonder why I take the space to show all the test runs. The goal with this series is to get familiar with the TDD flow when working with Eventide and what the incremental steps are. Also, when you’re new to a framework, one of the first learning curves is making sense of error messages. With this methodical approach, you see what the error messages you’re likely to encounter are and how to fix them. As a side benefit, we’re also learning what the framework code is and why it’s important, so as you look at examples of Eventide components, you know what the pieces are.
My predictions aren’t great, so it may be tomorrow that we get to test controls, or it may be the day after. Whichever day it is, I hope the rest of whatever day you’re reading this on goes swimmingly.