00:00:21.680
hello everyone and and thank you to
00:00:23.480
engineers.sg for that live stream uh my
00:00:26.480
wife and daughter will be watching me uh
00:00:28.279
say this talk for the fifth time in days
00:00:30.359
and hopefully they enjoyed it just as
00:00:32.399
much so hello uh good afternoon I'm Tim
00:00:35.600
Riley uh I'm part of the dryb and ROM IB
00:00:39.360
core teams and I work at ice lab over in
00:00:42.039
Australia where for these last nine
00:00:44.280
months or so uh We've adopted a whole
00:00:46.520
new approach to building our web
00:00:48.840
apps so what got us here well a good
00:00:52.359
many years of this of of following
00:00:54.840
established Community practices for Ruby
00:00:57.359
building our MVC apps and still ending
00:00:59.680
up with projects that became more and
00:01:01.760
more difficult to work on over time we
00:01:04.600
wanted to find a methodology to improve
00:01:07.200
this for
00:01:08.520
us and that's what I'm here to talk to
00:01:10.680
you about today about building better
00:01:12.840
web apps with Ruby uh we'll cover a lot
00:01:15.400
of ground today and we'll look at a lot
00:01:16.600
of code but I hope I can give you a
00:01:18.600
sense a new sense for what's possible
00:01:20.520
with Ruby and inspire you to look at web
00:01:23.159
apps from a fresh
00:01:25.119
perspective we'll look at three things
00:01:26.920
today we'll look at dry be a new
00:01:29.200
collection of modern single purpose ruby
00:01:31.360
gems at ROM RB a flexible persistence
00:01:34.600
tool kit for Ruby and at Roa which
00:01:36.759
offers an expressive approach to
00:01:39.360
routing and we'll also combine these
00:01:42.000
with a Fresh Approach to Ruby itself uh
00:01:44.320
with a hybrid of functional and object D
00:01:46.560
programming that we call functional
00:01:49.240
Ruby so let's start with that thinking
00:01:51.920
about functional programming what are a
00:01:53.680
few things from it that can help us with
00:01:55.799
our Ruby well firstly if we want to
00:01:58.240
orient an app around fun
00:02:00.680
we need to treat those as values so that
00:02:02.920
we can pass them around and combine them
00:02:05.159
to build larger
00:02:06.880
systems our app is also simplest to work
00:02:09.520
with if we favor immutability wherever
00:02:12.120
possible avoiding internal mutable state
00:02:14.640
in our
00:02:15.680
objects and we also like to avoid
00:02:18.280
external side effects wherever possible
00:02:20.519
so most of our code can be run in any
00:02:22.800
sort of context and together this helps
00:02:26.319
us better understand the flow of data at
00:02:29.000
any part of our
00:02:31.160
application and we can satisfy all of
00:02:33.400
these criteria by building functional
00:02:35.680
objects in
00:02:37.280
Ruby let's take a look at one now this
00:02:39.599
is the simplest possible functional
00:02:41.760
object it's a class it has a call method
00:02:45.080
just like Ruby's own procs and
00:02:47.680
lambdas that core method accepts an
00:02:50.519
input and returns an output that's it it
00:02:53.519
doesn't mutate the object State and it
00:02:55.560
shouldn't mutate the input data
00:02:57.879
either functional objects in
00:03:59.920
request so we can see writing data and
00:04:02.200
we'll look at these step by step
00:04:03.439
building them up layer by layer and in
00:04:05.360
doing so I hope to give you an idea
00:04:07.360
about the small pieces that combined to
00:04:09.599
make the general shape of a larger
00:04:11.680
working app built in this way so let's
00:04:14.560
start with the get let's get some
00:04:16.000
articles we need to do five things to
00:04:18.359
tackle this we need to handle routing
00:04:20.199
and HTTP we'll need to look at object
00:04:22.759
dependency management we'll have to
00:04:24.400
render some views we'll need to query
00:04:26.360
the database and finally we'll need to
00:04:27.960
model the data coming back from that dat
00:04:30.039
base so let's start the first thing
00:04:32.400
we'll need to do obviously is handle the
00:04:34.240
incoming HTTP request and to do this
00:04:36.840
we'll work with dry web and rotor dry
00:04:40.039
web is a high level organizing gem that
00:04:43.039
forms the umbrella around our
00:04:44.560
application and it works with rotor this
00:04:47.960
is what we'll use in our examples today
00:04:50.360
and is what exists right now with the
00:04:52.280
gem but the plan is to actually go on
00:04:53.919
and support other routing libraries as
00:04:55.479
well so in this way we're not talking
00:04:57.720
about a one- siiz fits all stack here
00:05:00.039
what we're talking about is a collection
00:05:01.360
of libraries that work really well
00:05:03.360
together to form a larger more coherent
00:05:05.240
web app so we can start by installing
00:05:08.199
the gem and generating a new project
00:05:10.360
with
00:05:11.160
it and then we can go into a route this
00:05:14.759
is rotor at work and rotor works by
00:05:17.919
building up a routing tree and it works
00:05:20.560
by um basically lots of nested blocks
00:05:23.240
like this and in a larger app this can
00:05:25.560
make for some really concise routing
00:05:27.440
logic because we can set context in the
00:05:29.759
out of blocks and have that context
00:05:31.600
available for any block that's nested
00:05:33.319
within it Rota is also a flexible
00:05:36.440
routing tool kit because it starts with
00:05:38.160
a minimal core and then offers plugins
00:05:40.520
for us to layer on any sort of behavior
00:05:42.600
on top of it and that's what we're using
00:05:44.680
here to provide this view method this
00:05:47.000
view method is provided by a plugin and
00:05:48.840
we're using it here to render our index
00:05:51.160
of
00:05:54.759
Articles and this index of Articles is
00:05:57.319
provided for us by another object
00:06:00.240
and this is where dry component and dry
00:06:02.199
container come in dry component sits at
00:06:05.600
the heart of every dry web app it is a
00:06:08.240
high level organizing system for our
00:06:10.360
apps objects and it uses a simple naming
00:06:12.960
convention to register register those
00:06:14.960
objects with a container that container
00:06:17.319
is provided by dry container it's a
00:06:19.319
lightweight inversion of control
00:06:20.840
container for Ruby and what it basically
00:06:22.919
does is it allows us to access those
00:06:25.039
registered objects later at any other
00:06:26.960
time so what does this actually mean it
00:06:29.840
means when we call view with articles.
00:06:32.039
index it's translated into a fetch from
00:06:34.680
the container with a particular
00:06:36.080
identifier and what's return to us is a
00:06:38.680
pre-built object so we can go to work on
00:06:40.639
it right away and that pre-built object
00:06:43.560
is automatically made available to US
00:06:45.680
based on this simple naming convention
00:06:47.319
from a matching source
00:06:49.319
file and that convention doesn't use any
00:06:52.120
magic it uses basic language level Ruby
00:06:54.639
features good old require and load path
00:06:57.440
which means it's easy to understand and
00:06:59.360
easy to put to work for
00:07:01.080
us so after all of this we have a view
00:07:04.479
object and this is taken care of for us
00:07:07.240
by Drive View Drive view is a helpless
00:07:10.720
data oriented view rendering system
00:07:12.919
which combines functional view object
00:07:14.800
objects and templates to render our
00:07:16.840
views and just like the router uh we
00:07:20.039
plan to make view objects completely um
00:07:22.560
independent or they are so you can use
00:07:24.360
whatever works best for
00:07:26.160
you so here's our index view class what
00:07:29.680
it does is it specifies a template to
00:07:31.440
use and then in its local method it
00:07:34.000
returns a hash of locals to use within
00:07:36.960
the template now right now this isn't
00:07:39.560
quite enough to render our list of
00:07:40.720
Articles we need to fetch them from the
00:07:42.199
database and this is the job of a
00:07:45.199
dependency that dependency will be made
00:07:47.599
available for us by dry Auto inject dry
00:07:51.080
Auto inject provides a nice way for
00:07:54.000
class to declare its dependencies to
00:07:56.240
have those dependencies resolve for us
00:07:58.440
from our container and made available
00:08:00.400
for us to use anywhere inside the
00:08:02.759
object so let's go back to that
00:08:05.360
view and set up a dependency here we are
00:08:09.039
we're importing the Articles repository
00:08:11.199
as a dependency in that include line
00:08:13.120
there and then inside the locals method
00:08:15.199
we're using it to pass our listing of
00:08:16.879
Articles to the
00:08:18.520
view how do we get that listing we get
00:08:21.240
that using ROM and ROM repository ROM is
00:08:24.240
a flexible persistence toolkit for Ruby
00:08:27.000
it offers explicit customizable
00:08:29.520
distractions that we can use for every
00:08:31.560
step of the persistence process it's
00:08:34.080
also built around functional Ruby
00:08:35.959
principles and it's a really big part of
00:08:38.320
what makes these apps
00:08:39.640
sing and why is that because ROM lets us
00:08:42.560
Define a clear boundary between the core
00:08:45.279
of our app and our persistence layer it
00:08:48.080
lets us hide away the nitty-gritty
00:08:49.839
details about persistence and let our
00:08:52.080
apps core objects stay clean and focused
00:08:54.959
on their own
00:08:56.440
jobs how does this boundary look well
00:08:59.560
here it is with our repository class
00:09:01.959
we're saying here that we want to work
00:09:03.160
with an article's repository and we
00:09:05.200
Define a listing method which specifies
00:09:07.200
that we want published articles only in
00:09:09.160
this
00:09:11.920
case now ROM is actually a multi backend
00:09:14.959
persistence toolkit and since we want to
00:09:17.160
work with SQL here we'll use this SQL
00:09:19.480
adapter and we'll use that adapter to
00:09:21.760
set up a relation a relation is example
00:09:24.800
is an example of ROM's explicit
00:09:26.720
abstractions that we can use at various
00:09:28.360
points what the Rel does is Define a
00:09:30.680
mapping to a particular database table
00:09:32.760
or database
00:09:33.920
View and here we're using it to define a
00:09:36.320
published query method and that
00:09:39.240
specifies that we only want the the rows
00:09:41.160
where that published column is set to
00:09:42.440
true this is a great example of ROM's
00:09:44.920
explicit design because it it allows us
00:09:47.399
to Define exactly the persistence API
00:09:50.279
that we need for our app and nothing
00:09:51.920
more which means that every interaction
00:09:54.480
with the database goes through code that
00:09:56.640
we've written so we have full control
00:09:58.560
over those interactions and that would
00:10:00.200
be a great um a great comfort as apps
00:10:02.440
grow larger and get worked on by by many
00:10:05.720
people so with this relation in place uh
00:10:08.760
we can go back to our repository which
00:10:10.320
will now return the right data and this
00:10:12.920
is a very simple repository of course
00:10:14.560
but in a real app they'll grow fast
00:10:16.440
because we'll set up methods for each
00:10:18.320
unique data requirement in our app and
00:10:20.839
we can also compose data from multiple
00:10:22.800
sources so this is why it's helpful to
00:10:24.920
keep that lowlevel query logic
00:10:26.640
encapsulated back in those relations
00:10:30.480
by default repositories just return
00:10:32.720
plain struct objects but we would like
00:10:35.680
them to come back as objects that are
00:10:37.519
more specific to our domain and this is
00:10:39.480
what we do here we're specifying that
00:10:41.240
we'd like our objects to come back as
00:10:42.880
article
00:10:44.720
entities so how will we make these
00:10:46.959
entities well we'll make them with dry
00:10:48.839
types dry types is a type system for
00:10:51.839
Ruby not uh not a language level type
00:10:54.440
system but more something that just
00:10:55.959
gives you some stricter typing in the
00:10:57.839
places where you care about it the most
00:11:00.519
and where we care about it in this case
00:11:02.320
is when we're modeling our article
00:11:03.880
entities we want them to contain exactly
00:11:06.600
a certain kind of data and dry types
00:11:09.639
here won't actually allow us to even
00:11:11.880
initialize these objects with inv valid
00:11:13.639
data so it means when we have an
00:11:15.920
instance of an article we can be 100%
00:11:18.800
confident that it will contain the data
00:11:20.480
we expect which means there will be much
00:11:22.519
less second guessing when we're working
00:11:24.079
with these instances and it makes
00:11:25.839
building things like our views much more
00:11:28.320
simple and unlike active record models
00:11:31.560
which we might be used to working with
00:11:33.120
we don't mutate these structs by
00:11:34.680
convention which means they're much
00:11:36.639
simpler to pass around our app because
00:11:38.399
we don't need to worry about accidental
00:11:40.000
side effects or accidental mutation
00:11:41.880
along the
00:11:43.040
way so back to our view with the
00:11:46.120
repository in place everything is
00:11:48.720
complete so we can make our
00:11:51.760
template call The View object and get
00:11:54.880
the output we want our list of Articles
00:11:57.839
and one thing to note here is that this
00:11:59.360
is completely Standalone view rendering
00:12:01.839
so we can use the same mechanic anywhere
00:12:04.320
not just for rendering pages but for
00:12:06.160
anything we want even in things outside
00:12:08.120
the normal HP cycle just like rendering
00:12:10.120
emails for
00:12:11.959
instance so we can come all the way back
00:12:14.279
to the top back to our routes and our
00:12:16.480
job is done we've got our list of
00:12:20.399
Articles what did we do again well we
00:12:22.880
use dry web and rotor to handle routing
00:12:25.079
and hddp our object dependency
00:12:27.519
management was taken care of for us by
00:12:29.360
dry component container and auto inject
00:12:32.120
Drive view rented our views for us uh we
00:12:34.600
used ROM ROM SQL and ROM repository to
00:12:36.920
set up our database queries and finally
00:12:38.959
we use dry types to to model the results
00:12:41.120
from those
00:12:45.279
queries so the get request is done we've
00:12:49.320
got a list of Articles now let's go and
00:12:51.000
create one we'll have to do a few
00:12:53.440
different things this time we'll need to
00:12:55.320
validate input data from a form we'll
00:12:57.760
need to write commands to write data to
00:13:00.360
our database and we need to take care of
00:13:03.160
success and error handling of our
00:13:06.600
Command so we can go back to our routes
00:13:09.120
adjust them to accept a post request
00:13:10.839
this time and here we're using a
00:13:13.000
different rotor plugin we provided it's
00:13:14.760
resolve and what it does it just fetches
00:13:16.760
an object from our apps container using
00:13:18.560
this identifier and makes it available
00:13:20.399
for us to use in this case we're working
00:13:23.040
with create article which is a
00:13:25.079
functional command object that will
00:13:26.920
create our article for us and then we're
00:13:29.399
calling it with the form post
00:13:32.480
parameters and again just like before
00:13:35.000
dry component takes care of this for us
00:13:37.199
so we resolve this uh we resolve this
00:13:40.120
object it comes out of our container as
00:13:42.480
a ready to go instance that has been
00:13:44.600
created automatically for us from this
00:13:46.600
matching source
00:13:48.000
file and here's how this looks right now
00:13:50.680
a simple functional object and right now
00:13:53.399
all it's doing is just validating the
00:13:55.199
input
00:13:56.399
data and to actually make that
00:13:58.320
validation work we will work with Drive
00:14:00.199
validation Drive validation is a
00:14:02.320
powerful Standalone validation Library
00:14:05.240
it lets us model our validations in
00:14:07.240
precision and work with any kind of data
00:14:09.639
in any kind of
00:14:11.360
context so here's our schema what does
00:14:14.120
it do well it requires that all four of
00:14:16.519
these fields are filled it sets an
00:14:18.759
expectation that our title should have a
00:14:20.320
minimum of three characters and it also
00:14:22.880
sets some type expectations for the
00:14:24.720
published and published at keys there
00:14:26.800
one will be a Boolean and one will be a
00:14:28.160
time
00:14:30.680
and dry validation uses dry types under
00:14:33.079
the hood to give us some powerful
00:14:34.920
coercion Behavior around form posts so
00:14:37.839
we can start with this stringy form
00:14:39.399
input where everything is a string a
00:14:41.199
Boolean is represented as a one a time
00:14:43.839
is also a string and then after we've
00:14:45.639
run it through our schema we actually
00:14:47.440
get coerced data we get a proper Boolean
00:14:50.880
and a proper time object so this form
00:14:53.079
schema actually acts as another critical
00:14:55.160
boundary in our system it handles all
00:14:57.600
the baggage of http then out the other
00:14:59.800
end it lets our apps core objects work
00:15:02.399
with properly structured and properly
00:15:04.279
typed
00:15:05.839
data and we'll also get friendly
00:15:08.079
friendly error messages when something's
00:15:09.839
gone wrong we also get hints about
00:15:12.279
checks in the schema that weren't able
00:15:13.959
to be run yet so in the end the user
00:15:15.880
will have all the information they need
00:15:17.600
to correct any
00:15:19.800
errors so we can go back into our
00:15:22.320
Command check for the success of this
00:15:24.759
validation and if this is successful we
00:15:27.199
can save the article to the database
00:15:30.360
we've done this by injecting our
00:15:32.560
repository again and then on the
00:15:34.839
repository we're calling create with the
00:15:37.120
valid form data to persist the
00:15:39.720
article how will we do that well we do
00:15:41.959
that by going back to ROM again a big
00:15:44.160
part of ROM's designs is that it
00:15:46.000
separates queries from commands and
00:15:49.120
again this is about giving you
00:15:50.360
explicitness in your setting up your
00:15:51.920
persistence layer it ensures that we
00:15:54.480
create a persistent API with a surface
00:15:57.519
area that exactly fits our app's
00:16:00.040
requirements so if we want to offer a
00:16:02.839
create command for articles we need to
00:16:04.560
set it up so to do this we go back to
00:16:07.560
our relation this is the thing that
00:16:08.839
models the table and we'll Define a
00:16:11.040
schema to act as the canonical
00:16:13.000
representation of that data source and
00:16:15.519
we do this here again because ROM is a
00:16:17.240
multi- backend um multi backend
00:16:19.920
persistence tool kit not everything can
00:16:22.040
have schemas that are inferable like
00:16:23.519
from database tables now if you're using
00:16:25.440
ROM in a larger framework where it's
00:16:27.519
more integrated for SQL this might be
00:16:29.279
taken care of for us but it's still
00:16:31.120
great to have this facility available to
00:16:32.800
us because it means we can project data
00:16:34.480
freely in a way that's optimal for any
00:16:36.440
part of our
00:16:37.440
app but in the end this schema is used
00:16:39.959
here to ensure only the right data is
00:16:41.480
written back to our table so we can go
00:16:44.000
over to our repository now with the
00:16:45.279
schema in place and all we have to do is
00:16:47.319
declare this create command we'd like it
00:16:49.240
to offer a create command for us and
00:16:51.279
everything else is taken care of for us
00:16:53.199
it goes back to that schema and makes
00:16:55.399
sure the data can get written right
00:16:57.600
through so now our create article
00:17:00.480
operation is fully functional but uh and
00:17:03.839
we're doing one more thing here in that
00:17:05.439
we're returning an instance of the
00:17:06.880
article as the final result just so we
00:17:08.799
have consistency in what our apps
00:17:10.319
objects return but so far this is only
00:17:12.880
handling success we want to handle
00:17:14.760
failures as
00:17:16.039
well so this is where monads come in
00:17:19.319
monads are another concept from
00:17:20.959
functional programming that we can put
00:17:22.559
to good use here in modeling success and
00:17:25.199
failure results of our operation and
00:17:27.039
we'll do this with dry monads and dry
00:17:28.559
result
00:17:30.320
matcher we can integrate them into our
00:17:32.760
operation class like this and what it
00:17:34.960
offers us is Meaningful workable output
00:17:37.679
for both success and failure results and
00:17:40.400
we do it by this we do it by wrapping
00:17:42.360
our return values in either right or
00:17:44.480
left objects both of these are instances
00:17:47.240
of The Ether monad but in practice it
00:17:49.520
just means that in the end the single
00:17:51.919
return value we get is something that
00:17:53.840
has a consistent API regardless of
00:17:55.840
whether it's a success or a
00:17:57.840
failure
00:17:59.600
and it enables things like this this
00:18:01.799
nice flexible result matching that fits
00:18:04.240
really well into our rotor routes by
00:18:06.960
working this way we actually Elevate
00:18:08.559
failure handling to be a first class
00:18:10.600
concern because working with failures
00:18:12.679
becomes just as easy as working with the
00:18:14.320
success
00:18:15.720
case and in this case we do two things
00:18:19.080
on success we redirect and on failure we
00:18:22.000
render the form pass the validation
00:18:23.960
object back so we can present the user
00:18:25.520
with some
00:18:26.400
errors also notice here that this is the
00:18:29.360
only place in this in this request that
00:18:31.679
we've actually handled anything to do
00:18:32.960
with HTTP so just like our repository
00:18:36.000
was a boundary between our app and the
00:18:38.080
persistence layer the rotor routes here
00:18:40.400
are another boundary between the world
00:18:41.880
of HTTP and the world of our app our
00:18:44.559
app's objects only need to concern
00:18:46.440
themselves with doing their job and then
00:18:47.919
returning success or failure knowing
00:18:50.159
that back up in the routes here they'll
00:18:51.799
be handled as appropriate uh for the
00:18:53.720
request to be
00:18:55.200
completed so we've posted our article
00:18:58.520
this request is complete and we used
00:19:01.159
Drive validation to validate that input
00:19:03.240
data we went back to ROM to create some
00:19:05.640
commands and then we handled the success
00:19:07.559
or failure results with dry moads and
00:19:09.480
dry result
00:19:11.640
matcher but our app is not really done
00:19:14.600
here we've done these two requests but
00:19:16.720
as we know as developers our work is
00:19:18.760
never done clients come with more work
00:19:21.080
for us and we have to satisfy that in
00:19:23.559
fact dealing with chains gracefully and
00:19:25.679
effectively is a key part of our jobs
00:19:29.080
and everything we've done so far today
00:19:31.320
has been to prepare ourselves just for
00:19:33.480
this very thing so let's look at some
00:19:35.360
examples of
00:19:37.400
change say we've got a request to make a
00:19:39.720
new article form for admins or or power
00:19:41.760
users well to do this we'd start with a
00:19:44.520
new view objects and a new template and
00:19:46.960
then we could decide whether we want to
00:19:48.280
reuse that existing create article
00:19:50.320
operation or make a new one that more
00:19:52.880
appropriately satisfies our
00:19:55.039
requirements and either way we can be
00:19:57.559
confident about the changes going to
00:19:59.080
make here because in an app built around
00:20:01.919
Loosely coupled objects focused on
00:20:04.039
single tasks with external dependencies
00:20:06.440
injected writing tests becomes very easy
00:20:10.360
in fact we can do much more in true unit
00:20:13.120
test style in complete
00:20:15.240
isolation because our dependencies can
00:20:17.440
be passed in as test doubles which lets
00:20:19.720
us focus more on the code under test
00:20:22.280
rather than the world around it and this
00:20:24.400
is particularly useful for things like
00:20:26.120
database backed applications because
00:20:28.080
there's no need to globally St out
00:20:29.640
database calls or anything like that and
00:20:31.720
the end result is better faster tests
00:20:34.440
and more confidence in our
00:20:36.360
work in fact dry component does a little
00:20:39.440
bit extra to help keep our tests fast
00:20:42.200
when we use it and run any single
00:20:44.320
individual test the components in our
00:20:46.640
app boot up completely empty they don't
00:20:49.240
load any files and then when the test
00:20:51.840
runs the smallest possible set of files
00:20:54.840
just for that test to complete will be
00:20:56.600
loaded this means that individual tests
00:20:59.000
running the minimum possible time and it
00:21:00.919
makes for a truly responsive tdd
00:21:03.760
cycle so that change was was fairly
00:21:05.880
straightforward let's look at a more
00:21:07.400
complex one now say we want to email
00:21:09.760
some subscribers a notification when a
00:21:11.600
new article is created well there are
00:21:14.200
various ways we could handle something
00:21:15.760
like
00:21:16.640
this but we have a truly elegant one at
00:21:19.440
our disposal here we can use dry
00:21:21.440
transaction to make this
00:21:23.279
change we've already laid some
00:21:25.080
groundwork in our app we have a big
00:21:27.039
container of functional objects
00:21:29.240
each of those objects returns a left or
00:21:31.039
right monad for success or
00:21:32.919
failure and with this in place we have
00:21:35.360
everything we need to start to comine
00:21:37.480
combine these in interesting ways like
00:21:39.840
using dry transaction here to model this
00:21:42.360
as a business
00:21:44.159
transaction how it works is that it is a
00:21:47.400
sequence of functional objects called
00:21:49.679
one after the other with the output of
00:21:51.880
each object becoming the input of the
00:21:54.000
next and this sequence continues as long
00:21:56.279
as each one
00:21:57.640
succeeds this is the perfect arrangement
00:22:00.039
to satisfy this change all we need to do
00:22:02.760
is create a notify subscribers operation
00:22:05.559
run it after create article and it will
00:22:08.159
only run if that initial creation has
00:22:10.000
succeeded and then outside of this
00:22:12.360
single transaction these two operations
00:22:14.600
are not coupled in any way so we can
00:22:16.720
reuse them as we need in any sort of
00:22:18.799
context throughout our
00:22:21.039
app and dry transaction also offers an
00:22:23.960
enhanced result matching API so we can
00:22:26.400
hook into specific failure steps and
00:22:28.320
hand them as we want which is a really
00:22:30.440
powerful uh new feature and it's been
00:22:32.440
made possible by the design choices
00:22:33.880
we've made in building our app so
00:22:36.559
far and what are those design choices
00:22:39.400
well it's a decision to found our app on
00:22:42.360
Simplicity what is Simplicity here it's
00:22:45.200
an app that's built of many small
00:22:47.039
components each with a single focus and
00:22:50.360
we combin the best of objectoriented
00:22:52.320
design principles with the data
00:22:54.120
Simplicity inherent in functional
00:22:56.279
programming this makes for components in
00:22:58.400
our app that are therefore easy to
00:23:00.760
understand easy to reuse and easy to
00:23:03.240
test and I think they're actually really
00:23:05.159
easy and natural to write in Ruby code
00:23:07.360
as
00:23:08.360
well we've also taken care to design for
00:23:11.440
our apps domain first this isn't a web
00:23:14.720
app it's a ruby app first and foremost
00:23:17.480
that just so happens to have a web
00:23:18.919
interface around it we've taken note of
00:23:21.799
the critical boundaries in our app the
00:23:23.600
boundary between HTTP and our app and
00:23:26.039
the boundary between our app and
00:23:27.919
persistence and we built around them
00:23:30.039
appropriately leaving the core of our
00:23:31.919
app uh free to focus on its main tasks
00:23:34.919
on its own specific
00:23:36.679
domain and in working towards this goal
00:23:39.400
we've chosen tools to serve our app
00:23:41.520
first rather than the other way around
00:23:43.279
we haven't contorted our app to fit the
00:23:45.360
requirements of some external
00:23:47.880
tool so by focusing on Simplicity by
00:23:50.919
focusing on our domain on proper
00:23:52.799
boundaries on small components working
00:23:54.919
in concert we have an app that is easy
00:23:57.240
to change we have have created a change
00:23:59.840
positive architecture and this is the
00:24:02.600
crucial Advantage we create for
00:24:04.159
ourselves as programmers because this is
00:24:06.840
what we have to deal with every day a
00:24:09.520
change positive app is maintainable we
00:24:12.039
can easily understand it by starting at
00:24:14.120
any point and looking back and forth and
00:24:16.200
we can adjust it as required just as we
00:24:18.240
looked at
00:24:19.640
earlier a change positive app is
00:24:22.440
sustainable because of our design we can
00:24:24.600
work on it just as easily at day 1000 as
00:24:27.559
we did at day one
00:24:29.760
and this makes a change positive app
00:24:31.840
joyful to work on and it's this sense of
00:24:34.240
Joy this happiness as a programmer it's
00:24:37.000
that visceral thing that brought so many
00:24:38.600
of us to Ruby in the first place so we
00:24:40.919
owe it to ourselves to carry this joy
00:24:43.200
throughout every part of our jobs to
00:24:44.919
building web apps whether it's starting
00:24:46.799
something fresh or starting on something
00:24:48.720
with a rich
00:24:50.640
Legacy and by choosing to work in this
00:24:53.320
way we've not only created an investment
00:24:56.159
in our app we've made an investment in
00:24:58.440
Ruby because a healthy future for Ruby
00:25:01.399
is a diverse one with a diverse range of
00:25:04.600
competing tools and techniques this is
00:25:07.200
what drives innovation in our community
00:25:09.559
and will give it life for years to
00:25:12.360
come now everything I've showed you
00:25:14.200
today was put together by a hardworking
00:25:16.360
team of volunteers so I'd like to thank
00:25:18.799
at this moment P snit and Andy Andy
00:25:21.399
Holland for their work on dry RB and
00:25:23.799
rarb also Jeremy Evans for Roa and to
00:25:27.799
all the contrib to these communities
00:25:29.440
they're on the rise people are adopting
00:25:31.360
them people are contributing and we'd
00:25:33.279
love to see you there
00:25:34.720
too and is not the only community of
00:25:37.360
this type in the Ruby World either you
00:25:39.360
may be interested in checking out Hanami
00:25:41.240
or the trail Razer project both of these
00:25:43.440
integrate with various dryb gems and
00:25:46.120
they offer their own unique takes on web
00:25:47.640
app development and one of them may be
00:25:49.360
just right for you but if you're
00:25:51.799
interested in learning more about dry B
00:25:53.679
uh I have a web page full of resources
00:25:55.880
to go along with today's talk uh
00:25:58.440
including slides links and real working
00:26:01.240
open source example
00:26:02.799
apps please buy me at the conference I'd
00:26:05.480
love to talk to you and of course um I
00:26:08.520
have stickers to give away uh many of
00:26:11.000
them so come find me and let's have a
00:26:12.679
chat thank you very
00:26:19.559
much um I think we can open uh one
00:26:22.960
question for Tim
00:26:27.320
anyone
00:26:29.600
go hi
00:26:32.520
Kate okay I'm convinced I want to build
00:26:34.960
apps this way I'm going to go I'm going
00:26:36.559
to go to the bitly draw rdrc I'm going
00:26:39.200
to learn about it what's going to be the
00:26:42.240
first speed bump that I hit right
00:26:44.840
because you painted a lovely glorious
00:26:46.200
picture and I know that a lot of it is
00:26:48.279
going to be easy but when I hit that
00:26:50.159
bump if I hear it from you what I'm
00:26:52.039
going to expect it might make it easy
00:26:53.320
for me to get
00:26:54.399
over I think what it might be is that
00:26:58.039
you yes what we're doing here is doing a
00:27:00.919
little more of the work in connecting
00:27:02.480
the pieces in our own apps that might
00:27:04.679
have otherwise been taken care of for us
00:27:07.039
by larger Frameworks but I think that so
00:27:11.120
you might find there's a little bit more
00:27:12.320
friction in getting those first few
00:27:13.559
features out because you've got to
00:27:15.320
perhaps discover some of your own design
00:27:16.880
patterns um some of these well these
00:27:19.320
libraries are young so you may hit up
00:27:21.320
against rough edges um but we do have
00:27:24.159
really active communities and they're
00:27:25.720
getting better day by day so I think um
00:27:29.679
just persisting through those early
00:27:31.080
steps and also um ROM compared to active
00:27:33.840
record for instance gives you a lot more
00:27:35.919
power but you also have a little bit
00:27:37.399
more work to do and you sit a little bit
00:27:39.039
closer to the SQL but just like
00:27:40.799
everything else this isn't building our
00:27:42.840
app so we can be productive in the first
00:27:44.360
week is building our apps we and
00:27:45.799
productive for years so I think it's
00:27:48.120
keeping that end goal in mind and
00:27:50.360
knowing that the decisions we make today
00:27:52.600
set us up for making our life much
00:27:54.440
easier in years to come with the same
00:27:56.159
with the same code base awesome thank
00:27:58.000
you
00:27:58.799
thanks thank you very much than you