< Return to Video

MountainWest RubyConf 2013 Component-based Architectures in Ruby and Rails by Stephan Hagemann

  • Not Synced
    Thanks Mike.
  • Not Synced
    Thanks for the invitation.
  • Not Synced
    I want to start with a picture from this
    building, which I have to say
  • Not Synced
    I love this venue,
    and especially this building.
  • Not Synced
    And especially when it's not on fire.
  • Not Synced
    And I think we were really dumb yesterday.
  • Not Synced
    So if it starts beeping,
    I will be leaving this time.
  • Not Synced
    If you have not done it,
    I suggest you walk up this round arc—
  • Not Synced
    you can walk all the way up to the top—
  • Not Synced
    and on that walk you will see that stairs
    somewhere that says "The Beginning".
  • Not Synced
    I want the beginning of my talk to be the
    end of my last talk on a very similar topic.
  • Not Synced
    And that was an idea that I was proposing
  • Not Synced
    and that was the realization first of all
    that every gem that you start,
  • Not Synced
    the gem starts with a namespace.
  • Not Synced
    When you develop a Rails app,
    you never start with a namespace.
  • Not Synced
    And so if you compare the two,
    a gem is like a box.
  • Not Synced
    You put a little label on it.
  • Not Synced
    And a Rails app is a
    gorgeous infinite nothingness
  • Not Synced
    with all the bells and whistles of
    Active everything.
  • Not Synced
    To get a gauge of how relevant that is,
    who writes Rails apps on a daily basis?
  • Not Synced
    That's about two-thirds, maybe three-quarters.
  • Not Synced
    And who writes Ruby,
    but never touches Rails?
  • Not Synced
    That's way less. Okay.
    But still a few.
  • Not Synced
    So I have a feeling that for the second
    group, what I'm going to say is going to
  • Not Synced
    apply a little bit less.
  • Not Synced
    Maybe because of Rails not being around.
  • Not Synced
    So the idea from my last talk was:
    the next time you start an app,
  • Not Synced
    just put everything in a namespace.
  • Not Synced
    And I end it with what I found to be an
    awesome finishing slide,
  • Not Synced
    give yourself a box so you can start
    thinking outside of it.
  • Not Synced
    With the idea that if you have a gem,
    and you give something a name,
  • Not Synced
    and you put things inside of it,
    you can open that box,
  • Not Synced
    and you can go WTF when you find something
    that obviously doesn't fit in the box.
  • Not Synced
    If you try to do that with a Rails app,
  • Not Synced
    you don't have a box—
    you don't have a name—
  • Not Synced
    so everything is going to fit.
  • Not Synced
    The best game you can play is
    what doesn't belong with the others.
  • Not Synced
    Red. Green. Loud.
    What doesn't belong with the others?
  • Not Synced
    And you're going to know that it's Loud.
  • Not Synced
    But if you look at your typical Rails app,
    what are you going to have?
  • Not Synced
    You're going to have a User, a Company,
    Roles.
  • Not Synced
    And then, the Tulip pricing engine that
    you wrote.
  • Not Synced
    And then the marketing part for that
    and a store.
  • Not Synced
    You're going to tell me that you can still
    play that game and find out what may or
  • Not Synced
    may not belong to this app?
  • Not Synced
    It's very hard, because it kind of pushes
    us in the direction of just putting all of
  • Not Synced
    app in there.
  • Not Synced
    So what I want to talk about today is
    Component-based Ruby
  • Not Synced
    and Rails Architectures.
  • Not Synced
    I promise you I put this slide in before
    that happened.
  • Not Synced
    (Laughter)
  • Not Synced
    I may want this button more desperately
    than you. Trust me.
  • Not Synced
    Stop me if I'm going too fast.
  • Not Synced
    Because I want to talk about
    large applications.
  • Not Synced
    And one thing I heard about them is
    you just never build them.
  • Not Synced
    So I work for Pivotal Labs, and we
    start a new project every few weeks,
  • Not Synced
    and many of them are greenfield,
    so many of them are tiny when we start.
  • Not Synced
    But you heard from Matt yesterday,
    we have pretty big apps as well.
  • Not Synced
    And for every app, since our definition
    of success is that a client can stay
  • Not Synced
    successful after we leave,
    what we have to strive for is some kind of
  • Not Synced
    architecture where you can develop an
    application well over time.
  • Not Synced
    So we have to think about the app being
    larger than it is, while we develop it.
  • Not Synced
    Despite TDD and all.
  • Not Synced
    Sandi Metz wrote her awesome book last
    year and gave a few talks.
  • Not Synced
    And at the one that I was at—
    I don't know if she said it in other talks as well—
  • Not Synced
    but she said, "Your app is out to kill you".
  • Not Synced
    The growing complexity of your app is
    going to chase you
  • Not Synced
    and if the app were a bull,
    you might feel like that guy.
  • Not Synced
    And that is very exciting.
  • Not Synced
    For the next second that is exciting.
  • Not Synced
    For the next hours or days that is painful.
  • Not Synced
    Okay, maybe this is a bit drastic,
    but who has felt like their app was
  • Not Synced
    behaving like that bull to them?
  • Not Synced
    I have felt like that as well.
  • Not Synced
    They are not typically that fast.
  • Not Synced
    Quite the opposite.
  • Not Synced
    But they behave like something
    that is too big to handle.
  • Not Synced
    So what I want to get to, is for us to
    be more like this guy.
  • Not Synced
    Calmer. In control.
    With sunglasses and a hat.
  • Not Synced
    (Laughter)
  • Not Synced
    But I don't really like bull fighting,
    so I actually want us to be this guy.
  • Not Synced
    (Laughter)
  • Not Synced
    Just hanging out.
  • Not Synced
    I want to go home at five on a Friday night.
  • Not Synced
    And I think this guy is probably the one
    who is going to do that the most successfully
  • Not Synced
    because he is just fine
    probably all week long.
  • Not Synced
    This "never build large apps" is part of a
    larger quote from Justin Meyer:
  • Not Synced
    "The secret to building large apps",
    he says, "is never build large apps."
  • Not Synced
    "Break your applications into small
    pieces. Then assemble those testable,
  • Not Synced
    bite-sized pieces into your big
    application."
  • Not Synced
    But how?
  • Not Synced
    He was—I think—talking about JavaScript.
  • Not Synced
    I'm talking about Ruby on Rails.
  • Not Synced
    You can tell from the font that you're
    supposed to read this slide.
  • Not Synced
    (Laughter)
  • Not Synced
    I'm going to use a tiny sample
    application throughout this talk.
  • Not Synced
    And this is where you can find it.
  • Not Synced
    If you can't read this after all, just
    search for github and the next big thing
  • Not Synced
    and I think it comes up first.
  • Not Synced
    I promise this thing is tiny
  • Not Synced
    and it does only one thing.
  • Not Synced
    It's an announcement page.
  • Not Synced
    You can announce whatever you have to
    announce.
  • Not Synced
    And people can sign up for updates.
  • Not Synced
    I spell it with two Ns but I don't know
    if my Twitter handle is only one.
  • Not Synced
    That's how often I tweet.
  • Not Synced
    So the service thanks you for signing up
    and I just press return.
  • Not Synced
    And when I made this sample app
    I had to come up with
  • Not Synced
    a little bit more than just that
    to actually have something to move around.
  • Not Synced
    So I realized pressing return makes it
    really easy for me to do that
  • Not Synced
    many, many times.
  • Not Synced
    But of course I only want my email address
    to be registered once.
  • Not Synced
    So I thought I'll give the server the
    ability to kind of feedback the level
  • Not Synced
    of annoyance that it feels while you're
    just continuing to press return
  • Not Synced
    and annoying it.
  • Not Synced
    At some point it'll just freak out.
  • Not Synced
    The freak out picture didn't come.
  • Not Synced
    Let's try again.
  • Not Synced
    It's an awesome picture of a guy with
    a lot of hair.
  • Not Synced
    I need your help though.
  • Not Synced
    I need you to imagine something
    really big.
  • Not Synced
    Because that app is tiny
    and I'm going to make examples
  • Not Synced
    of refactorings or rearchitecturings that
    don't make sense in that app.
  • Not Synced
    So bear with me and always imagine
    something very big.
  • Not Synced
    I'm going to go through eight ways of
    architecting this application.
  • Not Synced
    And I have roughly two minutes for
    every one.
  • Not Synced
    Let's start with number one.
  • Not Synced
    If you checkout that GitHub project,
    you will find those eight steps as tags
  • Not Synced
    and you can roughly follow the steps that
    I'm taking.
  • Not Synced
    The first one is a normal Rails application.
  • Not Synced
    It's all-in-one.
  • Not Synced
    Let's look at what's happening.
  • Not Synced
    There is a TeaseController here.
  • Not Synced
    It has two actions: new and create.
  • Not Synced
    Let's briefly see what happens.
  • Not Synced
    No surprises. It's not a well written
    method. John would refactor this.
  • Not Synced
    We're trying to find the entry that you
    gave this form.
  • Not Synced
    If we find it, we update tries on that
    entry.
  • Not Synced
    When we put it into this annoyance meter
    it'll tell us how annoyed we are
  • Not Synced
    at this point, and we'll feed that back
    to the client.
  • Not Synced
    If we don't find the entry,
    we'll just say, "Thanks for signing up."
  • Not Synced
    And then there's a bit of catching
    unexpected cases.
  • Not Synced
    That's pretty much what's going on in
    this controller.
  • Not Synced
    There is a model back in this.
  • Not Synced
    It's Entry. It has the email address
    and a number of tries.
  • Not Synced
    And there's this AnnoyanceMeter,
    which is not really important.
  • Not Synced
    It just derives new strings
    out of these counts.
  • Not Synced
    I suspect that everyone has seen this.
  • Not Synced
    And I will—for this talk—assure you
    that it every one of these stages
  • Not Synced
    you can run the tests, and—if I didn't
    pick the wrong commit—they will pass.
  • Not Synced
    So I have tests, but I won't show them.
  • Not Synced
    I will just be moving them around for
    every stage
  • Not Synced
    and adding new ones where there is a new
    test to be written.
  • Not Synced
    Now remember, I said at the beginning of
    every Rails app it's kind of this infinite void.
  • Not Synced
    A good picture for that is probably
    a big dump site.
  • Not Synced
    I've already asked you if you ever felt
    like the guy in front of the bull,
  • Not Synced
    and maybe you also felt like the driver
    in that bulldozer or whatever that is.
  • Not Synced
    The reason I'm saying that about this tiny
    app is that there's two things in there
  • Not Synced
    that are now indistinguishable—if you look
    at it from 30,000 feet—as to what they do,
  • Not Synced
    how they're connected,
    if they have any structure.
  • Not Synced
    It pretty much doesn't exist.
  • Not Synced
    So for all intents and purposes—
    if you just multiply, extrapolate from these two
  • Not Synced
    classes to, say fifty or a hundred—
    you have no clue
  • Not Synced
    who's interacting with whom,
    what's going on, and why.
  • Not Synced
    Unfortunately this doesn't
    look very chaotic.
  • Not Synced
    But if I were to write a hundred class
    names here, it would be chaotic
  • Not Synced
    and you would not be able to assess any
    sort of structure
  • Not Synced
    just by looking at those classes.
  • Not Synced
    A first way of getting structure
    into this application is modules.
  • Not Synced
    The plus we get from that is
    a higher level structure.
  • Not Synced
    And you've all probably seen this one too.
  • Not Synced
    Not much changes.
  • Not Synced
    The controller doesn't change at all,
    except it now references to Entry and
  • Not Synced
    the AnnoyanceMeter within their
    namespaces.
  • Not Synced
    In the AnnoyanceMeter, Annoyance left the
    class name; it's now in a module.
  • Not Synced
    I moved some other stuff around
    that was mainly for testing.
  • Not Synced
    We now have this structure.
  • Not Synced
    And if we come back to this analogy
    then maybe we have moved
  • Not Synced
    from a dump site to a recycling yard.
  • Not Synced
    Recycling yards are awesome.
  • Not Synced
    I should know this because I remodeled my
    house, and you visit those places very
  • Not Synced
    often when you do.
  • Not Synced
    You can point to a corner of the recycling yard and say, "That's where the scrap metal is."
  • Not Synced
    However if you've ever gone to such a place, you also know that you stick the hard-to-recycle stuff under your car because you're not a nice citizen
  • Not Synced
    and then you try to pawn it off to the recycling yard because you just want to get rid of it and it's so hard to get rid of.
  • Not Synced
    People do that all the time.
  • Not Synced
    Just stand in front of the clean wood and then suddenly, not-so-clean wood gets thrown on top.
  • Not Synced
    What does that mean for code?
  • Not Synced
    It means there is structure.
  • Not Synced
    I have now an EmailSignup module and an Annoyance module.
  • Not Synced
    And in that is more classes.
  • Not Synced
    But there is no guarantee that these classes are a) independent, or b) actually doing what they say.
  • Not Synced
    I can't prove it.
  • Not Synced
    That's why I put the rectangles over each other.
  • Not Synced
    But at least I didn't have to write Entry anymore because I now have a higher-level concept, that being this EmailSignup module.
  • Not Synced
    If I want to improve on this, I would like to go to the next step, which is prove that these two pieces are independent.
  • Not Synced
    I call it the gem component app.
  • Not Synced
    I may need to explain a little bit more about this step.
  • Not Synced
    First off, you're not seeing that engines folder right there.
  • Not Synced
    We're not looking at that.
  • Not Synced
    We're looking at the annoyance gem.
  • Not Synced
    I made a folder gems and I put an annoyance folder in there.
  • Not Synced
    Actually this is a complete folder structure of a gem.
  • Not Synced
    We have a gemspec; it defines our annoyance gem—and I should probably fill in this data—but there is a Gemfile which tells us that we are running tests with RSpec and magically—this may be the only time we need to look at tests—I have tests now in this gem.
  • Not Synced
    If I go into that subfolder, I can now independently run.
  • Not Synced
    Because of the way RSpec will load these files, I can prove to you that these tests for the levels and the meter of the annoyance will pass without knowing about EmailSignup.
  • Not Synced
    Again, it doesn't matter here; it matters a whole lot if you're dealing with tens of hundreds of files.
  • Not Synced
    I can prove that this thing is independent.
  • Not Synced
    How many have seen shoving a gem like this into the subfolder of an app?
  • Not Synced
    Everyone else should start doing that.
  • Not Synced
    You could ask me two good questions: one is why am I not using git submodules? "I'm not smart enough" is the answer to that.
  • Not Synced
    Why am I not using a different repository?
  • Not Synced
    "I'm too lazy" is the answer to that.
  • Not Synced
    You can just do this.
  • Not Synced
    So how do I use it in the application?
  • Not Synced
    The Gemfile of the main application is now just pointing to an annoyance gem and it's just referring to it by its path.
  • Not Synced
    That's all there is to it.
  • Not Synced
    This will be loaded.
  • Not Synced
    I can access it like before.
  • Not Synced
    And if I look into the TeaseController, we are still down here just loading that AnnoyanceMeter.
  • Not Synced
    Nothing else special has happened.
  • Not Synced
    The only thing that happened is we now have provably independent tests; we have provably independent code within that one component that we just extracted.
  • Not Synced
    In the words of Eric Evans, "Choose modules that tell the story of the system and contain a cohesive set of concepts."
  • Not Synced
    We are now able to prove that a set of concepts—namely within that gem—is independent of the others.
  • Not Synced
    I think that is of great value.
  • Not Synced
    I was in Iceland last weekend and I found something funny so I wanted to put these slides in.
  • Not Synced
    It's good for push and pull to be so explicit, but if you look at the doors, it's actually not that explicit as to what they do.
  • Not Synced
    They denote how to operate this door, but now look at Icelandic signs.
  • Not Synced
    You know how to open these doors.
  • Not Synced
    They scream at you, "This is how you open me."
  • Not Synced
    You might also rip them apart, but you know.
  • Not Synced
    (Laughter)
  • Not Synced
    For the structure of the app, now there is a second component.
  • Not Synced
    So we've got a little bit more structure.
  • Not Synced
    Is that cool so far?
  • Not Synced
    Gems make up a portion of apps, but if I have dependencies towards anything Rails, I have to do a lot of homework myself.
  • Not Synced
    In the next step—what I want to call the Rails component app—I'm still going to do that for a Rails component.
  • Not Synced
    So I want provable structure for Rails.
  • Not Synced
    The way to do this easily is with Rails engines.
  • Not Synced
    If a Rails engine were actually a train engine, then a Rails application would actually be a train.
  • Not Synced
    So you might want to reconsider to call the whole thing Ruby in Trains.
  • Not Synced
    But I still have to clarify a thing that engines have still this wrong perception of being for pagination and generic administration and authentication.
  • Not Synced
    They are not.
  • Not Synced
    If you look at the docs it says Rails engines allow you to wrap a specific Rails application or a subset of functionality and share it with other applications of within a larger packaged application.
  • Not Synced
    I should know because I pushed that change last time I gave a talk like this.
  • Not Synced
    It's very good that it says that now.
  • Not Synced
    Let's look at what that means.
  • Not Synced
    Now next to this gems folder we also have an engines folder.
  • Not Synced
    Now you're allowed to notice that the app folder went away.
  • Not Synced
    Since we only have one controller, there is no longer any necessity for this app to contain any code itself.
  • Not Synced
    It only contains gems.
  • Not Synced
    Let's start with the more self-contained one: EmailSignup.
  • Not Synced
    If we go in here, there is still that Entry and it's now within a module and within a gem, which is also incidentally an engine.
  • Not Synced
    If you don't know a lot about them check out this source code or find out about them.
  • Not Synced
    I can't go into more detail but I'm happy to do that in Questions.
  • Not Synced
    So essentially this means I have my database migrations in here; I have tests for this; it runs independently.
  • Not Synced
    And the main app, as before, just includes those gems and references them.
  • Not Synced
    Let's quickly look at the Teaser because the Teaser is now what contains the controller.
  • Not Synced
    Actually I also want to point out that the Teaser contains assets, controllers and views in my example here.
  • Not Synced
    It does not contain models because it doesn't have any data itself.
  • Not Synced
    EmailSignup only has the model. It only has this entry thing.
  • Not Synced
    The Teaser thus must be getting that data from somewhere else.
  • Not Synced
    And indeed it does.
  • Not Synced
    It too requires EmailSignup to be present.
  • Not Synced
    And it references that dependency of course in the gemspec so that it's actually a valid gem definition.
Title:
MountainWest RubyConf 2013 Component-based Architectures in Ruby and Rails by Stephan Hagemann
Description:

more » « less
Video Language:
English
Duration:
31:32

English subtitles

Incomplete

Revisions