Summarized using AI

Next Generation Ruby Web Appswith dry-rb, ROM, and Roda

Tim Riley • June 23, 2016 • Singapore • Talk

In the presentation, Tim Riley discusses innovative methods for building Ruby web applications using a suite of modern tools, specifically dry-rb, ROM, and Roda. He emphasizes the need for a fresh perspective beyond traditional Rails methodologies, drawing on concepts from functional programming.

Key points covered include:

- Functional Programming: Riley advocates for treating functions as first-class values, favoring immutability, and reducing internal state and side effects to streamline data flow and enhance code clarity.

- Using dry-rb: This collection of single-purpose Ruby gems helps organize web applications more efficiently than traditional MVC approaches.

- ROM (Ruby Object Mapper): Described as a flexible persistence toolkit, ROM allows developers to keep the core application logic clean and focused by clearly separating it from the persistence layer.

- Roda for Routing: Roda is introduced as an expressive routing tool that enables concise routing logic through nested blocks, enhancing the routing process.

- Dependency Injection: Through the use of dry-container and dry-auto-inject, the presentation illustrates how dependencies are managed effectively, facilitating scalability and maintenance.

- Validation and Error Handling: Tim discusses how dry-validation provides a structured way to handle input validations, while dry-monads help manage success and failure states seamlessly.

- Change Management: A 'change-positive architecture' is highlighted, showcasing the ease of making adjustments as application requirements evolve—demonstrating how their approach fosters maintainability and sustainability of Ruby applications.

Throughout the talk, Tim uses practical code examples to illustrate concepts, such as creating functional objects for handling HTTP requests and validating form data before persisting it in a database. He concludes that by adopting these tools and methodologies, Ruby developers can create applications that are not only efficient and enjoyable to work on but also poised for future evolution and sustainability.

Overall, Riley invites Ruby developers to delve into these tools and explore the joy of building web applications with improved organizational methodologies and functional programming principles.

Next Generation Ruby Web Appswith dry-rb, ROM, and Roda
Tim Riley • June 23, 2016 • Singapore • Talk

Speaker: Tim Riley, Partner, Icelab

If you’ve ever yearned for more than the Rails way, come along and learn how a small set of tools and techniques can bring joy to your Ruby web app development, from the smallest beginnings through to the most complex of codebases. Discover how concepts like functional programming, immutability, strict typing, dependency injection and object composition can actually be easy and natural in Ruby (yes, really!), and how they will make your web app a pleasure to build, test and extend.

Speaker's Bio
Tim is a partner at Australian design agency Icelab, and a core developer of dry-rb. He’s excited about advancing the state of web development in Ruby.

Event Page: http://www.reddotrubyconf.com/

Produced by Engineers.SG

Help us caption & translate this video!

http://amara.org/v/ONqV/

Red Dot Ruby Conference 2016

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
Explore all talks recorded at Red Dot Ruby Conference 2016
+17