< Return to Video

RailsConf 2014 -Advanced aRel: When ActiveRecord Just Isn't Enough

  • 0:17 - 0:20
    CAMERON DUTRO: OK. Sweet. Welcome, everybody.
  • 0:20 - 0:22
    Thank you all for coming to this talk.
  • 0:22 - 0:26
    This is the, the second talk that I've given
    at a RailsConf,
  • 0:26 - 0:28
    and first one that I gave was on
  • 0:28 - 0:31
    internationalization, and there were like,
    maybe,
  • 0:31 - 0:33
    fifteen people in the audience.
  • 0:33 - 0:35
    And now there are a lot more.
  • 0:35 - 0:37
    I guess this is a more popular topic,
  • 0:37 - 0:39
    I don't know. So, cool.
  • 0:39 - 0:42
    So, again, thanks all for coming. I am Cameron
  • 0:42 - 0:45
    Dutro. I work for the U.S. Government. No,
    no,
  • 0:45 - 0:50
    not really. I work for Twitter. I'm on International
  • 0:50 - 0:53
    Engineering, which explains the topic of my
    previous talk,
  • 0:53 - 0:56
    two years ago. You can follow me at camertron
  • 0:56 - 0:59
    on Twitter. Look at me on GitHub as well.
  • 0:59 - 1:01
    A little bit of personal background, this
    is my
  • 1:01 - 1:05
    cat. He's pretty cute. His name is Chester.
    You
  • 1:05 - 1:08
    can follow him at @catwithtail. All right,
    so, we're
  • 1:08 - 1:11
    gonna do a little bit of an interactive exercise
  • 1:11 - 1:15
    before I dive into the deep technical portions
    of
  • 1:15 - 1:18
    this. How many, raise your hand if you've
    ever
  • 1:18 - 1:23
    written Rails 2 code? You guys, wow, look
    at
  • 1:23 - 1:25
    that. OK. Nice. How many of you, in your
  • 1:25 - 1:28
    Rails 2 code, saw maybe a query that looked
  • 1:28 - 1:30
    like this? Yeah.
  • 1:30 - 1:33
    So, a ton of strings in here. There's not
  • 1:33 - 1:36
    really an object-oriented way to construct
    queries in Rails
  • 1:36 - 1:39
    2. So, thank heavens for Rails 3 and 4,
  • 1:39 - 1:43
    because they introduced a much more chainable
    object-oriented pattern
  • 1:43 - 1:46
    for doing query construction. So, how many
    of you
  • 1:46 - 1:48
    wrote Rails 3 or 4 code that looks like
  • 1:48 - 1:54
    this?
  • 1:54 - 1:57
    That's pretty much the same thing as before.
    And,
  • 1:57 - 1:59
    I don't want to say, you know, you're doing
  • 1:59 - 2:06
    it wrong, but, you're doing it wrong. So,
    let's
  • 2:06 - 2:09
    go into this code and maybe clean it up
  • 2:09 - 2:11
    a little bit, right.
  • 2:11 - 2:13
    So I just moved one of those strings that
  • 2:13 - 2:16
    defined a join association, or a join, on,
    on
  • 2:16 - 2:18
    this table, and I moved it up into a
  • 2:18 - 2:20
    symbol, so I'm joining the comments table
    on Post.
  • 2:20 - 2:23
    So this is a lot cleaner looking, and it's
  • 2:23 - 2:27
    allowing ActiveRecord to introspect the associations
    between these two
  • 2:27 - 2:30
    models, derive the columns to use, and then
    automatically
  • 2:30 - 2:32
    construct that query for you to do the right
  • 2:32 - 2:34
    thing.
  • 2:34 - 2:36
    So that's awesome. But, we still have a couple
  • 2:36 - 2:38
    other problems with this code, right. We still
    have
  • 2:38 - 2:40
    these question marks in the where clause,
    and we
  • 2:40 - 2:42
    still have that second join. And the reason
    that
  • 2:42 - 2:47
    join is not yet a symbol is because it's
  • 2:47 - 2:50
    an association that doesn't pertain immediately
    to Post, right.
  • 2:50 - 2:53
    It's joining authors in relation to comments.
  • 2:53 - 2:55
    And, by the way, in this system, authors are
  • 2:55 - 2:57
    a part of comments, not part of posts. I
  • 2:57 - 2:59
    know it's a little confusing. So, you could
    simplify
  • 2:59 - 3:01
    this even further and you could use this cool
  • 3:01 - 3:04
    hash rocket syntax that says, even though
    comments and
  • 3:04 - 3:06
    author are not, well comments is, but author
    is
  • 3:06 - 3:07
    not directly related to posts, I want to get
  • 3:07 - 3:10
    there through comments. And so I can add this
  • 3:10 - 3:12
    hash rocket style syntax also.
  • 3:12 - 3:15
    But we still have these question marks. So,
    fortunately
  • 3:15 - 3:17
    ActiveRecord has given us a lot over the past
  • 3:17 - 3:19
    couple of years that have passed between 2
    and
  • 3:19 - 3:21
    3 and 4. But this is still a little
  • 3:21 - 3:24
    bit, you know, a little bit dirty, and, especially
  • 3:24 - 3:25
    if you wanted to do something like an outer
  • 3:25 - 3:27
    join in this join clause, or if you wanted
  • 3:27 - 3:30
    to do something a little bit more than say
  • 3:30 - 3:31
    I want to say something is equal to something
  • 3:31 - 3:34
    else, right. A column is equal to a value.
  • 3:34 - 3:37
    So I kind of look at this style as
  • 3:37 - 3:39
    better but, you know, you're still handed
    a great
  • 3:39 - 3:44
    weapon and not quite using it appropriately,
    right. So
  • 3:44 - 3:46
    let's look at parts of this query. I'm gonna
  • 3:46 - 3:48
    reuse the previous string example to identify
    what's wrong
  • 3:48 - 3:51
    with these queries, but let's look at the
    previous
  • 3:51 - 3:53
    queries. And we'll extract little bits of
    this and
  • 3:53 - 3:57
    talk about why they're less than ideal.
  • 3:57 - 4:00
    So you've got this join, and the first thing
  • 4:00 - 4:01
    that's wrong with this is you have to write
  • 4:01 - 4:05
    the words join and the words on. And that's
  • 4:05 - 4:08
    not difficult for somebody to do. It's not
    difficult
  • 4:08 - 4:11
    to type J-O-I-N on your keyboard. But it does
  • 4:11 - 4:14
    mean that you have to know MySQL or PostGreSQL
  • 4:14 - 4:19
    or SQLite syntax to get this done. You also
  • 4:19 - 4:22
    have, essentially, no syntax checking. So,
    you don't have
  • 4:22 - 4:24
    any idea, having written this string, you
    don't have
  • 4:24 - 4:28
    any idea that authors is an actual table,
    or
  • 4:28 - 4:31
    that authors dot id is a column. All you
  • 4:31 - 4:32
    know is that you have this string and that's
  • 4:32 - 4:34
    getting handed to the, to ActiveRecord, and
    then it's
  • 4:34 - 4:37
    executing it for you.
  • 4:37 - 4:38
    Let's look at the where clause here as well,
  • 4:38 - 4:40
    and I kind of alluded to these problems before,
  • 4:40 - 4:42
    but we'll go over these in detail. So, again,
  • 4:42 - 4:44
    you have to know MySQL syntax, you have to
  • 4:44 - 4:47
    know that there's an and between these two
    things.
  • 4:47 - 4:50
    And, which of course, and all these syntactical
    issues,
  • 4:50 - 4:52
    you know, these may not persist across different
    flavors
  • 4:52 - 4:56
    of databases, so maybe MySQL has this particular
    construction
  • 4:56 - 4:58
    for and, but it could be that PostGres has
  • 4:58 - 5:00
    a different one. And it probably doesn't,
    but you
  • 5:00 - 5:03
    know, you don't know that.
  • 5:03 - 5:05
    It's confusing also to match arguments with
    question marks.
  • 5:05 - 5:07
    I mean, how many of you have looked at
  • 5:07 - 5:08
    a piece of code and thought to yourself, man,
  • 5:08 - 5:10
    there are so many join conditions, or so many
  • 5:10 - 5:13
    where conditions here? And I don't know which
    argument
  • 5:13 - 5:16
    matches up to which question mark. Yeah, I
    mean
  • 5:16 - 5:19
    I've done that a bunch of times.
  • 5:19 - 5:21
    It's also not very object-oriented, right.
    You have this
  • 5:21 - 5:23
    where string and then comma, comma, comma.
    It's almost
  • 5:23 - 5:26
    functional in its style. And, again, we have
    that
  • 5:26 - 5:28
    no syntax-checking problem.
  • 5:28 - 5:30
    So, you might think to yourself, boy, I wish
  • 5:30 - 5:32
    there was a better way to do this, and
  • 5:32 - 5:34
    maybe I'll do a search, and you might come
  • 5:34 - 5:37
    across a StackOverflow article that tells
    you to use
  • 5:37 - 5:40
    strings in your queries. And you might come
    across
  • 5:40 - 5:44
    RailsCast 202, which tells you to use strings
    in
  • 5:44 - 5:47
    your queries. You might find blogs. You might
    ask
  • 5:47 - 5:50
    coworkers. You might ask friends, family members,
    or even
  • 5:50 - 5:52
    your cat, and they'll all tell you that you
  • 5:52 - 5:54
    should use strings in your queries.
  • 5:54 - 5:56
    But I'm here to tell you that no, you
  • 5:56 - 5:58
    can avoid doing that. You can keep calm, you
  • 5:58 - 6:02
    can avoid literal strings in your queries.
    Let's look
  • 6:02 - 6:04
    at the previous query, but completely, I like
    to
  • 6:04 - 6:08
    use the word, arelized. This is the query
    arelized.
  • 6:08 - 6:10
    So, notice, there's a bunch of cool things
    going
  • 6:10 - 6:11
    on here.
  • 6:11 - 6:14
    First, you don't have to know SQL syntax.
    All
  • 6:14 - 6:16
    you have to know is Ruby syntax. And, you
  • 6:16 - 6:17
    know, of course, this is a little bit of
  • 6:17 - 6:19
    a DSL, so you might have to go about
  • 6:19 - 6:21
    learning Arel as well, learning what it can
    do.
  • 6:21 - 6:24
    But that's what this talk is for. That's why
  • 6:24 - 6:25
    you're here.
  • 6:25 - 6:27
    Ruby syntax-checking, you get that for free.
    So if
  • 6:27 - 6:29
    you have an unbalanced parenthesis or you're
    referencing a
  • 6:29 - 6:31
    model that doesn't exist, it will throw an
    exception
  • 6:31 - 6:33
    and you can go and it'll fail fast, in
  • 6:33 - 6:34
    other words, you can go fix that bug before
  • 6:34 - 6:37
    you deploy to production.
  • 6:37 - 6:41
    So, because ActiveRecord 3 and 4 are chainable,
    it
  • 6:41 - 6:43
    means Arel is chainable as well. It allows
    you
  • 6:43 - 6:46
    to compose queries in kind of step-by-step
    pattern, a
  • 6:46 - 6:50
    step-by-step fashion. So it's also chainable.
    And you have
  • 6:50 - 6:52
    none of those dang question marks.
  • 6:52 - 6:55
    It's also a lot easier to read, at least
  • 6:55 - 6:58
    in my opinion, because what I'm reading is
    Ruby,
  • 6:58 - 7:00
    not SQL embedded in Ruby, which to me is
  • 7:00 - 7:04
    weird. So, here's what we're gonna cover today.
    I've
  • 7:04 - 7:06
    kind of already gone over the, the initial,
    you
  • 7:06 - 7:08
    know, diagram of, of how, kind of the state
  • 7:08 - 7:10
    of the world now, and so when I cover
  • 7:10 - 7:12
    what ActiveRecord is, what Arel is, there's
    kind of
  • 7:12 - 7:14
    a, an indistinct line separating those two,
    and I'll
  • 7:14 - 7:16
    try to separate that.
  • 7:16 - 7:19
    Talk about how to reference tables and columns.
    What
  • 7:19 - 7:22
    terminal methods are and why they're confusing
    and when
  • 7:22 - 7:25
    they happen. We'll talk about, of course,
    the meat
  • 7:25 - 7:28
    of SQL. So select, where, join, and this special
  • 7:28 - 7:31
    thing called join association, and order.
    Group is, is
  • 7:31 - 7:34
    way easy so I'm not gonna cover that. And,
  • 7:34 - 7:36
    or, greater than, less than - all these great
  • 7:36 - 7:38
    things you can do with Arel that you can't
  • 7:38 - 7:40
    necessarily find online. And then we'll also
    talk about
  • 7:40 - 7:43
    the match, which is executed SQL-like, and
    in, which
  • 7:43 - 7:46
    allows for sub-queries.
  • 7:46 - 7:49
    OK. So what is ActiveRecord exactly? Well,
    you all
  • 7:49 - 7:53
    know this, but it bears repeating here. ActiveRecord
    is
  • 7:53 - 7:56
    a database abstraction. So there's no need
    to speak
  • 7:56 - 7:58
    a dialect of SQL. It connects to queries,
    it
  • 7:58 - 8:00
    returns data back to you. And it also functions
  • 8:00 - 8:03
    as a persistence layer, you know. Database
    rows are
  • 8:03 - 8:05
    Ruby objects. You can pull them out, you can
  • 8:05 - 8:08
    put them back in. And they also encapsulate,
    in
  • 8:08 - 8:10
    many cases, your domain logic. And I know
    people
  • 8:10 - 8:13
    say no fat models, no fat controllers, but
    the
  • 8:13 - 8:15
    truth is that a lot of domain logic gets
  • 8:15 - 8:16
    shoved into those models.
  • 8:16 - 8:21
    They, they contain validations, they contain
    all that good
  • 8:21 - 8:24
    stuff. And they also define associations between
    your models.
  • 8:24 - 8:27
    So, Arel is distinct in that all that it's
  • 8:27 - 8:32
    really used for is building queries. So it's
    described,
  • 8:32 - 8:36
    on its GitHub page, as a relational algebra
    for
  • 8:36 - 8:38
    Ruby. And that didn't make any sense to me
  • 8:38 - 8:41
    because, it didn't until I started reading
    more, because
  • 8:41 - 8:43
    relational algebra, those two words really
    don't, I don't
  • 8:43 - 8:46
    know how those work put together, but that's
    how
  • 8:46 - 8:47
    they describe it.
  • 8:47 - 8:48
    Essentially what that means is that it just
    builds
  • 8:48 - 8:51
    SQL queries in an object-oriented way. It
    generates abstract
  • 8:51 - 8:54
    syntax trees, or ASTs for your queries, and
    then
  • 8:54 - 8:57
    uses the visitor pattern to put those out
    to
  • 8:57 - 8:59
    SQL strings and then hands those off to ActiveRecord
  • 8:59 - 9:02
    to execute. It also enables chaining, as we
    talked
  • 9:02 - 9:05
    about, because ASTs are kind of nicely composable,
    because
  • 9:05 - 9:07
    all you have to do is reference a subtree
  • 9:07 - 9:09
    in order to pull in, you know, some other
  • 9:09 - 9:10
    branch of query logic.
  • 9:10 - 9:15
    So, one blogger also described Arel as vexingly
    undocumented.
  • 9:15 - 9:17
    So you can look at find pieces of Arel
  • 9:17 - 9:20
    all over the place, but joining that all together
  • 9:20 - 9:23
    is a, something that took me a long time.
  • 9:23 - 9:26
    In fact, this talk came out of a project
  • 9:26 - 9:27
    that I was working on at Twitter. I work
  • 9:27 - 9:30
    on the Twitter translation center. We needed
    to implement
  • 9:30 - 9:33
    an access control system. And to do that,
    we
  • 9:33 - 9:35
    had permissions and we had groups and all
    that
  • 9:35 - 9:37
    kind of thing, and in order to join all
  • 9:37 - 9:40
    that logic together and decide whether a user
    was
  • 9:40 - 9:42
    able to see a particular phrase, it turned
    out
  • 9:42 - 9:44
    that that was a difficult thing to do. We
  • 9:44 - 9:45
    would have had to do a lot of, a
  • 9:45 - 9:48
    lot of, you know, strings in our SQL queries.
  • 9:48 - 9:50
    And we wanted to avoid that. We wanted to
  • 9:50 - 9:52
    be able to be composable and use scoping and
  • 9:52 - 9:54
    all that great stuff.
  • 9:54 - 9:56
    So, having done that project, I'm trying to,
    and
  • 9:56 - 9:58
    I'm taking that knowledge and kind of imparting
    it
  • 9:58 - 10:00
    to you. So that project was a, a direct
  • 10:00 - 10:03
    reason why this talk was created. I find it
  • 10:03 - 10:06
    very difficult to find the information I was
    looking
  • 10:06 - 10:07
    for.
  • 10:07 - 10:09
    So, as a, as a recap here, you know,
  • 10:09 - 10:11
    Arel knows nothing about your models. It knows
    very
  • 10:11 - 10:14
    little about your database. All that it knows
    is
  • 10:14 - 10:18
    that you have these queries you're trying
    to construct.
  • 10:18 - 10:20
    It doesn't care if those tables exist. It
    doesn't
  • 10:20 - 10:23
    care if those columns exist. ActiveRecord
    cares. But Arel
  • 10:23 - 10:26
    really doesn't care. And it does not store
    or
  • 10:26 - 10:29
    retrieve data. All of these are ActiveRecord's
    responsibilities. So,
  • 10:29 - 10:33
    in summary here, Arel constructs queries and
    ActiveRecord does
  • 10:33 - 10:33
    everything else.
  • 10:33 - 10:36
    If you were to look at this as a
  • 10:36 - 10:39
    hierarchy, and kind of a diagram I guess,
    you've
  • 10:39 - 10:43
    got ActiveRecord, which you would interact
    with as a
  • 10:43 - 10:46
    developer. ActiveRecord calls out to Arel
    to, to process
  • 10:46 - 10:48
    a query, form a query, and then sends that
  • 10:48 - 10:50
    query to the database, and the data comes
    back
  • 10:50 - 10:53
    and ActiveRecord will package that into nice
    Ruby objects
  • 10:53 - 10:54
    for you.
  • 10:54 - 10:58
    So, something also that bears some introduction
    here is
  • 10:58 - 11:03
    what an AST is, because as DHH so eloquently
  • 11:03 - 11:06
    put it in his keynote this morning, not everybody
  • 11:06 - 11:08
    comes from a computer science background.
    And ASTs are
  • 11:08 - 11:10
    very computer science-y. So, an AST essentially
    is, or
  • 11:10 - 11:12
    really what a tree, in this case, this is
  • 11:12 - 11:14
    a basic tree, this is actually a binary tree,
  • 11:14 - 11:16
    and what this means is that, so, it's made
  • 11:16 - 11:19
    up of nodes. Each node has left and right
  • 11:19 - 11:21
    children, unless it's the bottom. It's called
    a leaf,
  • 11:21 - 11:23
    or it's called a, yeah, a leaf node.
  • 11:23 - 11:24
    So we have left and right children. In this
  • 11:24 - 11:26
    case, it's a binary tree because you have
    two
  • 11:26 - 11:28
    children. You could have an n tree which could
  • 11:28 - 11:31
    have multiple children. So, why are trees
    important? Why
  • 11:31 - 11:33
    are they cool? Well, a tree is cool because
  • 11:33 - 11:36
    you can represent things like equations with
    them or,
  • 11:36 - 11:40
    or expressions. So five times parenthesis
    six plus three,
  • 11:40 - 11:44
    you can represent this in a tree by defining
  • 11:44 - 11:45
    a node at the root here, which is multiplication.
  • 11:45 - 11:48
    That's an operator. And then each left and
    right
  • 11:48 - 11:53
    child represents the two different operands
    of that expressions,
  • 11:53 - 11:55
    of that, of that operator. So five times six
  • 11:55 - 11:55
    plus three.
  • 11:55 - 11:58
    So the six plus three would be evaluated first,
  • 11:58 - 12:00
    because it's in parenthesis. So, notice also
    the parenthesis
  • 12:00 - 12:03
    are grouping this expression together. And
    then five times
  • 12:03 - 12:07
    that. You can kind of expand this concept
    into
  • 12:07 - 12:10
    how this would look for a SQL expression,
    by
  • 12:10 - 12:11
    looking at this example. So, you might have
    a
  • 12:11 - 12:14
    query root and then a select from, and select
  • 12:14 - 12:18
    has two columns, id and text, and from has
  • 12:18 - 12:20
    just one child post, posts. And this would
    be
  • 12:20 - 12:20
    an entry.
  • 12:20 - 12:22
    Now, I don't actually know if this is how
  • 12:22 - 12:23
    Arel does this in the background. This is
    a
  • 12:23 - 12:26
    representation that you could imagine is going
    on behind
  • 12:26 - 12:27
    the scenes.
  • 12:27 - 12:29
    OK. So let's get to some code. And, and
  • 12:29 - 12:31
    the first thing I want to talk about is
  • 12:31 - 12:33
    a little gem that I put together to kind
  • 12:33 - 12:35
    of help with this talk and do some Arel
  • 12:35 - 12:37
    things that just take kind of some of the
  • 12:37 - 12:40
    verbosity out of Arel. So it's called arel-helpers.
    Go
  • 12:40 - 12:42
    ahead and install this thing. It's got three
    kind
  • 12:42 - 12:44
    of helpers. Very small.
  • 12:44 - 12:47
    And let's also, so, let's jump on from there
  • 12:47 - 12:49
    and we'll talk about how to reference tables
    and
  • 12:49 - 12:53
    columns in Arel. So, let's pretend you have
    this
  • 12:53 - 12:57
    table. It's called post. It has many comments.
    And
  • 12:57 - 12:59
    let's say you wanted to reference the id in
  • 12:59 - 13:04
    this table. Well, post dot arel_table(:id).
    So any class
  • 13:04 - 13:07
    that inherits from ActiveRecord::Base has
    this method called Arel
  • 13:07 - 13:10
    table. And it returns to you an instance of
  • 13:10 - 13:13
    Arel colon colon table that was a, is a
  • 13:13 - 13:15
    way of referencing your table in code.
  • 13:15 - 13:17
    Now brackets :id of course gives me what's
    called
  • 13:17 - 13:19
    an Arel attribute, and I can look at, I
  • 13:19 - 13:21
    can do the same thing for text, and I
  • 13:21 - 13:24
    can even do this for columns that don't exist.
  • 13:24 - 13:25
    So if I wanted to derive a column, I
  • 13:25 - 13:27
    could also use this to reference that column.
    Now,
  • 13:27 - 13:31
    that's not always true. But that's almost
    always true.
  • 13:31 - 13:32
    So this returns an Arel::Attributes::Attribute.
  • 13:32 - 13:35
    Now you don't really care what it returns.
    You
  • 13:35 - 13:36
    just want, you care that you can use it
  • 13:36 - 13:39
    in your code. So, the arel-helpers gem kind
    of
  • 13:39 - 13:43
    makes this a little easier. If you include
    ArelHelpers::ArelTable
  • 13:43 - 13:47
    in your model, you can now reference columns
    off
  • 13:47 - 13:50
    your tables with just square brackets and
    eliminate arel_table,
  • 13:50 - 13:52
    it just delegates down to arel_table. And
    it still
  • 13:52 - 13:55
    returns an attribute to you.
  • 13:55 - 13:57
    All right. So kind of going off of that,
  • 13:57 - 14:00
    what does this statement return? It's a little
    pop
  • 14:00 - 14:04
    quiz. Does it return "Rails is Cool." Just
    an
  • 14:04 - 14:09
    array of strings. Does it return an array
    of
  • 14:09 - 14:13
    instantiated objects that are records, ActiveRecords?
    Or does it
  • 14:13 - 14:16
    return an ActiveRecord::Relation? You guys
    might know the answer
  • 14:16 - 14:19
    to this. Who thinks it's A? Who thinks it's
  • 14:19 - 14:23
    B? OK. And who thinks it's C? Nice. OK.
  • 14:23 - 14:26
    It is, in fact, C.
  • 14:26 - 14:29
    One thing that's very cool about Rails 3 and
  • 14:29 - 14:32
    Rails 4, ActiveRecord 3 and ActiveRecord 4,
    is that
  • 14:32 - 14:35
    any of these methods, these database methods,
    like select,
  • 14:35 - 14:38
    join, where, they actually return an ActiveRecord::Relation.
    They don't
  • 14:38 - 14:41
    actually execute right away. So, that means
    that they
  • 14:41 - 14:44
    are also chainable. So if I said post dot
  • 14:44 - 14:47
    select title, and I assigned that the query,
    that's
  • 14:47 - 14:51
    not actually executing anything. That's storing
    this intermediate AST
  • 14:51 - 14:53
    in the variable called query. Which means
    that I
  • 14:53 - 14:56
    can also say query equals query dot select
    another
  • 14:56 - 14:58
    column name, and then if I were to say
  • 14:58 - 15:01
    query dot to_sql, I would get out the string
  • 15:01 - 15:03
    the ActiveRecord will send to the database
    to grab
  • 15:03 - 15:04
    data from.
  • 15:04 - 15:07
    So, it's also important to notice here, I've
    kind
  • 15:07 - 15:11
    of introduced this to_sql method. Any ActiveRecord
    relation that
  • 15:11 - 15:12
    you have, you can call to_sql on it and
  • 15:12 - 15:14
    it will give you the string that it would
  • 15:14 - 15:17
    execute against the database. So that's very
    handy. And
  • 15:17 - 15:19
    I'll use that throughout the whole presentation.
  • 15:19 - 15:22
    Yeah. So relationships can be changed.
  • 15:22 - 15:23
    All right. So let's get into some of the
  • 15:23 - 15:25
    specifics here. So let's talk about select.
    We talked,
  • 15:25 - 15:29
    we looked at select a little bit. You can
  • 15:29 - 15:32
    actually select multiple columns using array
    syntax, so this
  • 15:32 - 15:34
    is actually just ActiveRecord at this point,
    we're not
  • 15:34 - 15:37
    even diving into Arel right now. And you might
  • 15:37 - 15:39
    think, OK, so we're using this to_sql again.
    We
  • 15:39 - 15:41
    get :id, :text from Post. Everything's great.
  • 15:41 - 15:43
    Let's say that we wanted to get the SQL,
  • 15:43 - 15:44
    though, for count. So we just say give me
  • 15:44 - 15:47
    all of the Posts, the counts here, and you,
  • 15:47 - 15:50
    you probably wouldn't say select(:id) and
    then count, but,
  • 15:50 - 15:52
    you know, it's an example, so. And instead
    of
  • 15:52 - 15:55
    getting back a SQL string, you instead get
    back
  • 15:55 - 15:57
    a NoMethodError.
  • 15:57 - 15:59
    So why did that happen? Well, it turns out
  • 15:59 - 16:04
    that count is a terminal method. So, let's
    dive
  • 16:04 - 16:09
    into why that happened. So. Being a terminal
    method,
  • 16:09 - 16:11
    it would, it's executing immediately and returning
    a result
  • 16:11 - 16:13
    to you instead of giving you an ActiveRecord
    relation,
  • 16:13 - 16:15
    so that's why, it's good to distinguish between,
    and
  • 16:15 - 16:16
    know which ones are going to do this to
  • 16:16 - 16:18
    you, right.
  • 16:18 - 16:21
    The way to actually get a count, and get
  • 16:21 - 16:23
    a SQL string back out for it, would be
  • 16:23 - 16:24
    to kind of dive into Arel here. Use your
  • 16:24 - 16:28
    Arel table knowledge from before and call
    count on
  • 16:28 - 16:30
    an Arel attribute. And then, of course, you
    can
  • 16:30 - 16:32
    pass that to select as an array. Pass text
  • 16:32 - 16:35
    as a symbol. And out comes this count, like
  • 16:35 - 16:35
    you're expecting.
  • 16:35 - 16:37
    So let's look at what terminal methods are.
    So,
  • 16:37 - 16:40
    terminal methods execute the query immediately.
    They do not
  • 16:40 - 16:44
    return a relation. They're mostly count, first,
    and last,
  • 16:44 - 16:46
    so first and last are kind of like array
  • 16:46 - 16:49
    accessors, right. To_a, pluck, and any of
    the enumerable
  • 16:49 - 16:54
    methods will all execute your queries immediately.
    So, for
  • 16:54 - 16:57
    example, this, each over these objects, and
    you would
  • 16:57 - 16:58
    expect this to happen, because this is not
    a
  • 16:58 - 17:01
    lazy enumerator. This is a regular enumerator
    and you're
  • 17:01 - 17:02
    iterating over all of these things and you're
    putting
  • 17:02 - 17:04
    out the text. And that's obviously going to
    execute
  • 17:04 - 17:05
    right away.
  • 17:05 - 17:07
    But, more surprising is this one. So, if I
  • 17:07 - 17:09
    say each slice here and it's a lazy enumerator,
  • 17:09 - 17:10
    in other words, I don't pass a block to
  • 17:10 - 17:15
    it, this will also execute immediately. So
    just beware
  • 17:15 - 17:16
    of that.
  • 17:16 - 17:20
    All right. So let's go actually for real into
  • 17:20 - 17:22
    select here now. So we've already seen how
    we
  • 17:22 - 17:25
    can say dot count on an attribute, on an
  • 17:25 - 17:27
    Arel attribute. We can also say dot sum, and
  • 17:27 - 17:29
    that will give us this sum method. This is,
  • 17:29 - 17:32
    these are all, by the way, MySQL examples.
    Notice,
  • 17:32 - 17:36
    also, that Arel and ActiveRecord have nicely
    added this
  • 17:36 - 17:41
    AS sum_id for you. So, any table, any, any
  • 17:41 - 17:43
    column like this will automatically get a
    derived column
  • 17:43 - 17:45
    name as well.
  • 17:45 - 17:47
    You can change that column name by adding
    an
  • 17:47 - 17:49
    as here. So just chain as right on the
  • 17:49 - 17:52
    end of sum. Post visitors sum as visitor_total,
    and
  • 17:52 - 17:54
    that will give it kind of a custom derived
  • 17:54 - 17:57
    column name for you. You can also say dot
  • 17:57 - 18:00
    maximum and dot minimum, and these will all
    use
  • 18:00 - 18:05
    kind of the built-in SQL methods that you're
    expecting.
  • 18:05 - 18:07
    Let's say, though, that you're trying to,
    you know,
  • 18:07 - 18:10
    run a function that isn't part of Arel's DSL
  • 18:10 - 18:12
    or Arel's knowledge, I guess. It's kind of
    outside
  • 18:12 - 18:17
    of Arel's abilities. You can actually define
    a function
  • 18:17 - 18:20
    call that, of any arbitrary function. This
    covers things
  • 18:20 - 18:22
    like length or, like, any of the other methods
  • 18:22 - 18:25
    that MySQL provides, or PostGres, that may
    differ from
  • 18:25 - 18:28
    database to database. So, in this case, I
    am
  • 18:28 - 18:30
    saying a named function dot new, passing in
    the
  • 18:30 - 18:32
    name of the function and then giving it an
  • 18:32 - 18:33
    array of arguments that function takes.
  • 18:33 - 18:37
    And, then again, I'm also aliasing the result
    of
  • 18:37 - 18:38
    that as length, in this case. So giving it
  • 18:38 - 18:41
    a derived column name.
  • 18:41 - 18:44
    So, we can shorten this a little bit by,
  • 18:44 - 18:46
    cause it's a little bit verbose, by including
    Arel::Nodes.
  • 18:46 - 18:49
    And then this just becomes NamedFunction dot
    new. So
  • 18:49 - 18:51
    that's kind of a little hack you can add
  • 18:51 - 18:53
    to your queries.
  • 18:53 - 18:55
    Something kind of also pretty cool about Arel,
    this
  • 18:55 - 18:56
    is kind of one of the other little cool
  • 18:56 - 18:59
    select things, you can also say Arel dot star
  • 18:59 - 19:02
    here, if you wanted to select every single
    record,
  • 19:02 - 19:04
    right, so, or every single column from the
    record.
  • 19:04 - 19:06
    So select start from post. Arel dot star exists,
  • 19:06 - 19:08
    you can use it in a bunch of different
  • 19:08 - 19:09
    places.
  • 19:09 - 19:13
    OK. So, many people also don't know that select
  • 19:13 - 19:16
    and Arel and ActiveRecord, I should say, have
    the
  • 19:16 - 19:17
    ability to also just specify a from. So you
  • 19:17 - 19:21
    want to specify a, a sub-query inside a from.
  • 19:21 - 19:22
    This is pretty common. You can do this instead
  • 19:22 - 19:23
    of a join or instead of a, a more
  • 19:23 - 19:26
    complicated query here. So I'm saying select
    ID, and
  • 19:26 - 19:29
    then I'm passing a sub-query into the from.
  • 19:29 - 19:31
    So one thing that's really important to notice
    here
  • 19:31 - 19:33
    is you'll notice that the end of the sele,
  • 19:33 - 19:37
    the second select, I say dot AST. So also
  • 19:37 - 19:39
    at any point in your construction, you can
    call
  • 19:39 - 19:41
    AST on your relation, and it will give you
  • 19:41 - 19:44
    back this massive tree that it's constructed.
    And in
  • 19:44 - 19:46
    this case, from requires you to pass it an
  • 19:46 - 19:48
    AST otherwise it gets confused. It doesn't
    know what
  • 19:48 - 19:49
    to do with a relation. It only knows what
  • 19:49 - 19:51
    to do with it if it's an AST or
  • 19:51 - 19:54
    the column or things like that. So. Or a
  • 19:54 - 19:56
    table, in this case.
  • 19:56 - 19:58
    All right. So that pretty much covers select.
    There's
  • 19:58 - 20:00
    a lot of things you can do with select.
  • 20:00 - 20:01
    I was kind of breezing past a lot of
  • 20:01 - 20:02
    it. If you have any questions, you can always
  • 20:02 - 20:05
    talk to me or, or you know, I guess,
  • 20:05 - 20:08
    go online. But there's not that many resources.
    Anyway.
  • 20:08 - 20:08
    OK. So, the wonder of where.
  • 20:08 - 20:13
    So, where is also gotten a lot better since
  • 20:13 - 20:16
    Rails 2. I can pass in a title to
  • 20:16 - 20:19
    my where here, where post dot where title
    is
  • 20:19 - 20:21
    "Arel is Cool," and this will do what you
  • 20:21 - 20:24
    expect. It will say, it'll construct the SQL
    query,
  • 20:24 - 20:26
    user dot title equals this value. So no question
  • 20:26 - 20:28
    marks here, which is very nice.
  • 20:28 - 20:30
    But what happens if you want to, you know,
  • 20:30 - 20:32
    do something a little more. Oh, here's the,
    here's
  • 20:32 - 20:33
    the Arel version of this. So, you can do
  • 20:33 - 20:35
    this with pure Arel. It's a little more verbose.
  • 20:35 - 20:37
    You wouldn't really do this. But let's say
    you
  • 20:37 - 20:39
    wanted to say not equal to or less than,
  • 20:39 - 20:41
    things like that. You can do the same thing.
  • 20:41 - 20:44
    So you can say not equal in this case,
  • 20:44 - 20:46
    and notice that it'll put the band equals
    in
  • 20:46 - 20:49
    MySQL. You can also say, kind of something
    cool
  • 20:49 - 20:51
    here, not equal to nil, and it will serialize
  • 20:51 - 20:54
    that as is not nil, which is what your,
  • 20:54 - 20:56
    what you should be expecting.
  • 20:56 - 21:00
    Some of the cool methods. Greater than, GT.
    Less
  • 21:00 - 21:02
    than. LT. All makes sense. And all of these
  • 21:02 - 21:05
    are just chained right off of this, this,
    this
  • 21:05 - 21:08
    column attribute here. Here's greater than
    equal to -
  • 21:08 - 21:13
    GTEQ, and also LTEQ.
  • 21:13 - 21:18
    So, we also, oftentimes, in our queries, need
    to
  • 21:18 - 21:21
    be able to and and or our way to
  • 21:21 - 21:23
    a kind of a multiple where. We can do
  • 21:23 - 21:25
    the same thing here. So, if you were to
  • 21:25 - 21:28
    say where multiple times just with ActiveRecord,
    it would
  • 21:28 - 21:30
    automatically give you an and. But if you
    wanted
  • 21:30 - 21:32
    to specify an or, you would need to do
  • 21:32 - 21:35
    something like this. So we have our title
    and
  • 21:35 - 21:38
    attribute from post, we're saying equal "Arel
    is Cool",
  • 21:38 - 21:40
    and the post id is equal to twenty-two or
  • 21:40 - 21:43
    twenty-three. And notice that I have actually
    injected, so
  • 21:43 - 21:45
    I have put, inside the and, I put another
  • 21:45 - 21:48
    attribute and a dot or, and this will, Arel
  • 21:48 - 21:50
    will just know, it will, the visitor pattern
    will
  • 21:50 - 21:52
    know how to add the right parenthesis to this
  • 21:52 - 21:52
    query.
  • 21:52 - 21:54
    So, notice the output query has an and and
  • 21:54 - 21:58
    then parenthesis and the or, so that no operator
  • 21:58 - 22:03
    precedence messes us up here. Now, notice
    that I
  • 22:03 - 22:06
    said equals twenty-two or twenty-three. I
    could also have
  • 22:06 - 22:10
    put an in here. So in also accepts arguments
  • 22:10 - 22:13
    here that could, essentially an array of values
    here.
  • 22:13 - 22:15
    So it's the same thing, this will have the
  • 22:15 - 22:17
    same effect.
  • 22:17 - 22:19
    You can also compose your wheres. You can
    add
  • 22:19 - 22:21
    named functions in here. So you can also do
  • 22:21 - 22:24
    dot count, dot sum, all that stuff works.
    Those
  • 22:24 - 22:27
    are aggregate functions. Here's length. Oh,
    I'm aggregating this
  • 22:27 - 22:28
    stuff together and it all, it all just kind
  • 22:28 - 22:30
    of works. It's pretty cool.
  • 22:30 - 22:33
    All right. So let's get to my favorite part
  • 22:33 - 22:37
    of Arel, and that's joins. So ActiveRecord
    makes joins
  • 22:37 - 22:40
    pretty easy, except for when you're trying
    to do
  • 22:40 - 22:41
    an outer join, and we'll just, we'll just
    see
  • 22:41 - 22:43
    how that looks in a second here.
  • 22:43 - 22:45
    So let's pretend we have this data model.
    It's
  • 22:45 - 22:49
    a basic blog, essentially, so you've got a
    post.
  • 22:49 - 22:54
    Post has_many comments. And each comment has
    an author.
  • 22:54 - 22:56
    So let's, let's see what happens when we dive
  • 22:56 - 22:57
    into some joins here. So we say join. This
  • 22:57 - 23:01
    is without any help from Arel. Joins comments,
    or
  • 23:01 - 23:03
    comment in this case, where id is forty-two.
  • 23:03 - 23:07
    So, very cool. ActiveRecord inspects this
    model. It inspects
  • 23:07 - 23:11
    the association between author and comment.
    And automatically generates
  • 23:11 - 23:16
    the right join conditions for you. Pretty
    cool. You
  • 23:16 - 23:17
    can also specify, as I mentioned, kind of
    a,
  • 23:17 - 23:19
    a through table in this case. So I want
  • 23:19 - 23:21
    to join comments, and then I also want, you
  • 23:21 - 23:23
    know, using author, I've got comments, and
    I also
  • 23:23 - 23:25
    want to find the posts of the comment reference
  • 23:25 - 23:27
    or that, that comment is part of.
  • 23:27 - 23:30
    So, this will also do what you expect. It
  • 23:30 - 23:33
    will grab all the correct attributes for the
    models.
  • 23:33 - 23:36
    Hook them all together. A lot of cool introspection.
  • 23:36 - 23:38
    But, again, what about outer joins? So, you
    notice
  • 23:38 - 23:41
    back here, it's inter-joined. And there's
    really no way
  • 23:41 - 23:43
    to specify an outer join without diving in
    to
  • 23:43 - 23:46
    Arel. At least that I'm aware of.
  • 23:46 - 23:48
    OK. So let's look at this guy again. So
  • 23:48 - 23:50
    let's say I wanted to add an outjoin again.
  • 23:50 - 23:54
    Well, first, it kind of bears mentioning here
    that
  • 23:54 - 23:57
    you can turn this stuff into, also, pure Arel
  • 23:57 - 24:00
    stuff. So, we have a joins comment. We can
  • 24:00 - 24:02
    also say comment dot joins post, and then
    call
  • 24:02 - 24:06
    this method called join sources. So, join
    sources is
  • 24:06 - 24:08
    a way of saying, I have gotten this query
  • 24:08 - 24:10
    from ActiveRecord. And this is, again, it's
    just using
  • 24:10 - 24:12
    ActiveRecord. And I just want to grab the
    join
  • 24:12 - 24:13
    sources. I don't care about the select or
    the
  • 24:13 - 24:15
    where. Just give me the join sources and pass
  • 24:15 - 24:18
    that onto whoever is wrapping. In this case,
    another
  • 24:18 - 24:19
    query.
  • 24:19 - 24:22
    You can do the same thing with first join.
  • 24:22 - 24:26
    And then you can also specify the on conditions
  • 24:26 - 24:29
    manually here. So, we have a, a join comment
  • 24:29 - 24:32
    Arel table, and we're joining that on :comment_id
    is
  • 24:32 - 24:34
    equal to author :comment_id, and then saying
    join sources.
  • 24:34 - 24:35
    And we're doing this for both.
  • 24:35 - 24:37
    Now, this is gonna get pretty verbose, but
    at
  • 24:37 - 24:39
    this point, we can finally add our outer join.
  • 24:39 - 24:43
    OK. So notice that the attribute, Arel attribute
    dot
  • 24:43 - 24:45
    join takes a third argument, or a second argument,
  • 24:45 - 24:47
    that is the join type. So you could say
  • 24:47 - 24:51
    inner join here, or outer join. And that results
  • 24:51 - 24:54
    in, essentially, the same query. But this
    time an
  • 24:54 - 24:55
    outer join.
  • 24:55 - 24:58
    You're thinking, that is a lot of code. Why
  • 24:58 - 25:01
    would I ever type that much code? And you're
  • 25:01 - 25:03
    right. That's way too much code. So, part
    of
  • 25:03 - 25:09
    the Arel-helpers library is called JoinAssociation.
    And this will
  • 25:09 - 25:12
    introspect your model, just like ActiveRecord
    would do. Hand
  • 25:12 - 25:15
    those off to what's called a select manager,
    and
  • 25:15 - 25:18
    then use the second argument Arel::OuterJoin
    to give you
  • 25:18 - 25:20
    an outer join, but without all the tedium
    of
  • 25:20 - 25:22
    specifying all these conditions.
  • 25:22 - 25:24
    So I can slide this in here. JoinAssociation,
    and
  • 25:24 - 25:27
    I supply the original model and then the association
  • 25:27 - 25:30
    name, and then Arel::OuterJoin. And I can
    do that
  • 25:30 - 25:34
    for both of these guys. Pretty cool.
  • 25:34 - 25:39
    But wait, there's more. All right. So you've
    got
  • 25:39 - 25:40
    this guy, and you're like, you know, I really
  • 25:40 - 25:47
    want to supply some extra custom on conditions.
    Fortunately,
  • 25:47 - 25:49
    join dependency or join association, rather,
    will yield a
  • 25:49 - 25:52
    block to you that has both the association
    name,
  • 25:52 - 25:56
    which is a string or symbol, and also the,
  • 25:56 - 26:00
    a join condition's intermediate query object.
    So you can
  • 26:00 - 26:02
    say join conditions and, and then pass in
    anything
  • 26:02 - 26:05
    else you would like to construct. In this
    case,
  • 26:05 - 26:06
    we're making sure that the created_at date
    is less
  • 26:06 - 26:10
    than or equal to yesterday.
  • 26:10 - 26:13
    So this will, again, in the same query, but
  • 26:13 - 26:14
    this time. And it would have been an outer
  • 26:14 - 26:16
    join except that I, I actually took out, for
  • 26:16 - 26:19
    space reason, I took out the extra OuterJoin
    call
  • 26:19 - 26:21
    there. So this, this would return inner join
    and
  • 26:21 - 26:23
    then an outer join.
  • 26:23 - 26:26
    All right, so let's talk about join tables.
    As
  • 26:26 - 26:29
    for, for, so far we've been talking about
    tables
  • 26:29 - 26:33
    that have defined associations between them,
    and those associations
  • 26:33 - 26:36
    are not has_and_belongs_to_many. So I'm trying
    to kind of
  • 26:36 - 26:38
    freshen us a little bit and say, let's talk
  • 26:38 - 26:40
    a table that, or two tables that are associated
  • 26:40 - 26:43
    but via a join table. So, you might have
  • 26:43 - 26:45
    a series of courses that are taught by a
  • 26:45 - 26:49
    series of teachers. Multiple teachers can
    teach one course,
  • 26:49 - 26:51
    and courses can have multiple teachers.
  • 26:51 - 26:55
    So, in this case, you've got three tables.
    Courses,
  • 26:55 - 26:57
    teachers, and courses underscore teachers.
    This is pretty common
  • 26:57 - 27:01
    in Rails, I think. So, it means you have,
  • 27:01 - 27:03
    again, the course table. You can refer to
    that
  • 27:03 - 27:06
    with course dot arel_table like we saw before.
    Teacher
  • 27:06 - 27:09
    table, again, reference that with Teacher
    dot arel_table. But
  • 27:09 - 27:12
    then we've got some magic in here for courses_teachers,
  • 27:12 - 27:15
    because there, there really is no model for
    this.
  • 27:15 - 27:17
    There's no constant we can use to reference
    this
  • 27:17 - 27:20
    table. So instead, we need to create an object
  • 27:20 - 27:21
    for it.
  • 27:21 - 27:23
    So we'll create an Arel::Table. This is doing
    this
  • 27:23 - 27:26
    manually. We're giving it the name courses_teachers,
    and now
  • 27:26 - 27:28
    we can use this variable ct anywhere we cant
  • 27:28 - 27:31
    to refer to this table. So, for example, this
  • 27:31 - 27:33
    query. I want to say joins, and we have,
  • 27:33 - 27:36
    you know, courses join teachers, and then
    just for
  • 27:36 - 27:39
    the purposes of illustration here I've shown,
    you know,
  • 27:39 - 27:41
    what you would do in Arel to construct the
  • 27:41 - 27:43
    join conditions here, so. You know, on this
    on
  • 27:43 - 27:45
    this and then dot join sources.
  • 27:45 - 27:47
    And I didn't show the SQL output of this
  • 27:47 - 27:49
    but I think it's pretty, pretty clear from
    the
  • 27:49 - 27:50
    previous examples.
  • 27:50 - 27:53
    OK. So that wraps up join. So let's talk
  • 27:53 - 27:57
    about order. Order is actually the simplest
    of the
  • 27:57 - 28:00
    kind of SQL constructs I'll talk about today.
    You
  • 28:00 - 28:01
    all know that you can say post dot order
  • 28:01 - 28:04
    visitors and visitors in an integer in this
    case,
  • 28:04 - 28:07
    then dot to_sql. And this will give you a,
  • 28:07 - 28:10
    a return value of, you know, all of your,
  • 28:10 - 28:13
    all of your posts ordered by the number of
  • 28:13 - 28:15
    visitors. You can also, and some people don't
    know
  • 28:15 - 28:18
    this, but this also just an ActiveRecord construction
    here.
  • 28:18 - 28:21
    Post.order, that should be (:visitors), reverse
    order. This will
  • 28:21 - 28:26
    add a descending clause or a descending keyword
    to
  • 28:26 - 28:26
    the end of your query.
  • 28:26 - 28:29
    Now, you can also do this in Arel. You
  • 28:29 - 28:32
    can say post(:views) descending to SQL here,
    and this
  • 28:32 - 28:36
    is essentially the same thing. All right.
    So let's
  • 28:36 - 28:38
    talk about sub-queries with in.
  • 28:38 - 28:40
    So we talked about sub-queries with from.
    You can
  • 28:40 - 28:42
    also do sub-queries with in, and you can use
  • 28:42 - 28:44
    that AST that we talked about before. SO here's
  • 28:44 - 28:46
    an example of that. This is a very trivial
  • 28:46 - 28:48
    example, but we say post dot where and then
  • 28:48 - 28:51
    we say Post.arel_table in and we supply it
    another
  • 28:51 - 28:53
    query. So we're adding a sub-query into this
    in
  • 28:53 - 28:54
    call.
  • 28:54 - 28:57
    So that's pretty cool. And it does the right
  • 28:57 - 29:00
    thing. It adds the correct parenthesis and
    everything's great.
  • 29:00 - 29:03
    All right. And then like queries with matches,
    you
  • 29:03 - 29:04
    can also do this with Arel. So most of
  • 29:04 - 29:06
    the time you'd probably have to say, well,
    I'd
  • 29:06 - 29:07
    like to, if I wanted to do a like,
  • 29:07 - 29:09
    in other words match, kind of fuzzy match
    against
  • 29:09 - 29:12
    a column in your database, you would need
    to
  • 29:12 - 29:14
    add a like or, I guess, what is it
  • 29:14 - 29:17
    in PostGres? Like. What is it, double ilike
    or
  • 29:17 - 29:20
    something. Anyway. So it's one of those things.
  • 29:20 - 29:21
    So and then usually you have to add wild
  • 29:21 - 29:24
    card characters to this, and this would normally
    be
  • 29:24 - 29:27
    a big string in your query. But, you can
  • 29:27 - 29:29
    fix that with Arel. So, Post dot where, and
  • 29:29 - 29:31
    we have a matches Arel and then I just
  • 29:31 - 29:32
    put my - so you do have to add
  • 29:32 - 29:34
    these. Unfortunately you have to add these,
    these wild
  • 29:34 - 29:38
    card characters, but it's just dot matches.
    Super easy.
  • 29:38 - 29:41
    And that gets serialized out into a like.
  • 29:41 - 29:42
    And, interestingly enough, I didn't know this
    when I
  • 29:42 - 29:45
    started out, but it looks like it's actually
    taking
  • 29:45 - 29:50
    that string and encoding it into Base, Base16.
    Hexadecimal.
  • 29:50 - 29:54
    All right. So we've learned all this awesome
    stuff
  • 29:54 - 29:56
    about Arel. We know that it can do joins
  • 29:56 - 29:59
    and wheres and likes and ins and all kinds
  • 29:59 - 30:01
    of awesome stuff, all without the use of,
    well,
  • 30:01 - 30:04
    mostly without the use of strings. We also
    learned
  • 30:04 - 30:07
    that it's pretty chainable. So, when you think
    about
  • 30:07 - 30:09
    something that's chainable, the thing that
    comes to mind,
  • 30:09 - 30:12
    for me at least, almost immediately, is the
    builder
  • 30:12 - 30:13
    pattern.
  • 30:13 - 30:14
    And I think, well, why would I want to
  • 30:14 - 30:17
    construct a huge query inside my model or
    controller?
  • 30:17 - 30:20
    I'd much rather have an object do this for
  • 30:20 - 30:22
    me. Now, is that always the right choice?
    No,
  • 30:22 - 30:24
    of course not. But it's, it can be nice
  • 30:24 - 30:27
    to encapsulate logic, like building a huge
    query. So
  • 30:27 - 30:29
    that's why the next step, or the next point
  • 30:29 - 30:32
    I'm gonna bring up, is how to construct a
  • 30:32 - 30:34
    query builder. And this is also part of the
  • 30:34 - 30:36
    ActiveRecord, or the arel-helpers gem that
    I mentioned before.
  • 30:36 - 30:39
    So this is pretty much the entire implementation
    of
  • 30:39 - 30:43
    query builder. You have something that can
    forward some
  • 30:43 - 30:45
    methods for you. It'll query, it'll forward
    to_a, to_sql,
  • 30:45 - 30:48
    and to_each, this variable called query inside
    your object.
  • 30:48 - 30:51
    It accepts a query and sets that as an
  • 30:51 - 30:52
    instance variable. Then it also has this kind
    of
  • 30:52 - 30:55
    funky method name reflect, and reflect just
    instantiates a
  • 30:55 - 30:57
    new instance of the class and adds the query.
  • 30:57 - 30:59
    So it's essentially implementing that chaining
    idea.
  • 30:59 - 31:01
    All right, so here's a lot of code, and
  • 31:01 - 31:03
    we're gonna go over each of these pieces individually,
  • 31:03 - 31:05
    but this is a, a post query builder. So
  • 31:05 - 31:07
    I was trying to query some posts. Let's look
  • 31:07 - 31:10
    at this first method. So, notice this first
    inherited
  • 31:10 - 31:13
    from QueryBuilder. So let's say we have a
    method
  • 31:13 - 31:16
    called with_title_matching and it accepts
    a title. Calls reflect
  • 31:16 - 31:19
    and says dot where title matches and it adds,
  • 31:19 - 31:20
    you know, you can see it adds the percent
  • 31:20 - 31:22
    signs for the wild card characters. This is
    all
  • 31:22 - 31:24
    encapsulated inside this guy. So you all you
    have
  • 31:24 - 31:27
    to do is just call with_title_matching on
    your QueryBuilder.
  • 31:27 - 31:31
    Let's go down to with_comments_by. With_comments_by(username),
    so we join
  • 31:31 - 31:36
    comments and authors, and then where(Author[:username].in(usernames)).
    So we are
  • 31:36 - 31:39
    expecting an array of usernames and this,
    again, reflects
  • 31:39 - 31:41
    this query back to a new instance, returns
    a
  • 31:41 - 31:44
    new instance to QueryBuilder, too, this time
    with. Cause,
  • 31:44 - 31:46
    again, we're chaining all these things, right,
    so now
  • 31:46 - 31:48
    we have both. If we call both these methods
  • 31:48 - 31:50
    in a row, we would have find_by_username and
    with_title_matching,
  • 31:50 - 31:50
    both together.
  • 31:50 - 31:57
    So the last method I have here is since_yesterday.
  • 31:57 - 32:01
    The post[:create_at] greater than equal to
    Date dot yesterday.
  • 32:01 - 32:04
    Which means that now I can do something like
  • 32:04 - 32:06
    this. There's not a lot of ugly syntax in
  • 32:06 - 32:10
    here. It's all pretty clean. PostQueryBuilder.new
    with_comments_by pass in
  • 32:10 - 32:13
    an array of usernames, dot to_sql. And notice
    it's
  • 32:13 - 32:14
    doing all of the joining for me, like you'd
  • 32:14 - 32:17
    expect. I can then just say dot with_title_matching("arel"),
    it
  • 32:17 - 32:20
    adds that like, and then I can say since_yesterday
  • 32:20 - 32:23
    and it will add that third condition.
  • 32:23 - 32:28
    Pretty cool. Thanks.
  • 32:28 - 32:35
    Cool. Appreciate that. But you might be sitting
    here
  • 32:35 - 32:37
    thinking, god, this is super complicated.
    There's got to
  • 32:37 - 32:40
    be something out there to help me write these
  • 32:40 - 32:42
    queries better, because, you know, it's a
    lot of
  • 32:42 - 32:43
    verbosity, it's a lot of code, it's a lot
  • 32:43 - 32:46
    of extra stuff to remember. Well, fortunately,
    there is.
  • 32:46 - 32:47
    So, I worked for a little while on a
  • 32:47 - 32:51
    project called Scuttle. It was gonna be called
    Ariel,
  • 32:51 - 32:53
    like Little Mermaid, Ariel, you know, but
    it was,
  • 32:53 - 32:57
    unfortunately, too linguistically similar
    to Arel and so I
  • 32:57 - 32:58
    thought I would have trouble saying it on
    stage.
  • 32:58 - 33:01
    So I named it after Ariel in The Little
  • 33:01 - 33:03
    Mermaid's best friend Scuttle. So if you go
    to
  • 33:03 - 33:06
    scuttle dot io - thanks - if you go
  • 33:06 - 33:08
    to scuttle dot io, you'll see a screen like
  • 33:08 - 33:11
    this. It's an editor. You can enter in a
  • 33:11 - 33:13
    SQL query, and at the bottom it will convert
  • 33:13 - 33:15
    it to Arel for you.
  • 33:15 - 33:16
    [applause]
  • 33:16 - 33:19
    Thanks. Thanks.
  • 33:19 - 33:25
    Again, it's gonna be pretty verbose, but you
    can
  • 33:25 - 33:27
    tune your queries. There's a lot of other
    kind
  • 33:27 - 33:30
    of text below explaining what it does. This
    is
  • 33:30 - 33:32
    kind of a combination of three different projects,
    and
  • 33:32 - 33:33
    you can check these out on GitHub if you're
  • 33:33 - 33:37
    curious. So, I could not find a SQL parser
  • 33:37 - 33:39
    in Ruby. I tried so hard to find one
  • 33:39 - 33:41
    of these. And there just isn't one. The only,
  • 33:41 - 33:44
    as far as I can tell, the only SQL
  • 33:44 - 33:45
    parsers that I could find were written in
    Java,
  • 33:45 - 33:48
    and using this thing called Antler, which
    is a
  • 33:48 - 33:49
    parser generator.
  • 33:49 - 33:53
    So, I finally found an Antler grammar for
    the
  • 33:53 - 33:57
    Apache incubator tajo project, and grabbed
    that, generated these
  • 33:57 - 34:01
    appropriate classes, and then wrote a JRuby
    wrapped around
  • 34:01 - 34:03
    that that allows you to parse, it allows you
  • 34:03 - 34:07
    to parse queries. So it only works currently
    with
  • 34:07 - 34:10
    select and, I think, insert, because Apache
    incubator tajo
  • 34:10 - 34:14
    apparently doesn't need to do anything else.
    So, and
  • 34:14 - 34:15
    I wasn't ready to go write a bunch of
  • 34:15 - 34:17
    other Antler stuff. So, if you guys, if somebody
  • 34:17 - 34:18
    knows Antler around here, please, help me
    out. We
  • 34:18 - 34:19
    can, we can make it better.
  • 34:19 - 34:22
    And then the third part of this is something
  • 34:22 - 34:25
    called scuttle-server, which is just a Sinatra
    app on
  • 34:25 - 34:28
    top of JRuby that uses these two components.
    And,
  • 34:28 - 34:30
    also I forgot to mention there's also this
    scuttle-Java.
  • 34:30 - 34:32
    But anyway. Anyway, it uses these components
    to, to
  • 34:32 - 34:34
    drive the website.
  • 34:34 - 34:37
    OK. So that was a whirlwind. I have, like,
  • 34:37 - 34:40
    five minutes left. Thank you guys all for
    coming
  • 34:40 - 34:40
    and for listening.
Title:
RailsConf 2014 -Advanced aRel: When ActiveRecord Just Isn't Enough
Description:

more » « less
Duration:
35:08

English subtitles

Revisions