Talks

Ten Things You Didn't Know Rails Could Do

Ten Things You Didn't Know Rails Could Do

by James Edward Gray II

In this engaging talk titled "Ten Things You Didn't Know Rails Could Do," presented by James Edward Gray II at RailsConf 2012, the audience is introduced to lesser-known features and functionalities of the Rails framework. Gray, a seasoned member of the Ruby community, emphasizes that even experienced Rails developers may not be aware of the extensive built-in capabilities available to them. Throughout the session, he covers a wide range of interesting features and best practices, demonstrating practical usage examples that illustrate how these functionalities can enhance development efficiency.

Key Points Discussed:

  • Single-file Rails Applications: Gray explains how Rails can be set up in a single file, simplifying the file structure required for an application.
  • Annotations for Code Comments: He explores special comments in the code that Rails can track, aiding in organizing tasks and notes.
  • Sandboxing the Console: The talk highlights the ability to use a sandboxed console for experimentation without affecting the database state.
  • Helper Method Access in Console: Developers can call helper methods within the Rails console, enabling testing of code snippets effectively.
  • Optimized Migrations: Gray reveals new shorthand notations for database migrations, streamlining the process.
  • CSV Support for Data Handling: He shows how Rails now supports importing/exporting data to/from CSV formats easily, enhancing data manipulation.
  • Active Record Enhancements: The session introduces optimizations in managing database queries and associations with AR enhancements like pluck and supporting associations directly in migrations.
  • Custom Error Class Handling: Gray explains that Rails can route exceptions to any Rack application, allowing for customized error handling.
  • Decoupling Controllers: The talk delves into creating more modular applications where components communicate efficiently, such as routing exceptions and utilizing background processes for tasks.
  • Static Site Generation with Rails: He concludes the session with a creative use of Rails for generating static sites, replacing the need for Jekyll by utilizing Rails' asset and caching capabilities.

By the end of the session, attendees not only gain insights into these obscured features of Rails but also learn the underlying concepts that can improve their productivity as Rails developers. The talk underscores the importance of continuous learning and exploration within the vast capabilities of the Rails framework, encouraging the audience to discover new ways to leverage these tools in their applications.

00:00:25.699 This is "Ten Things You Didn't Know Rails Could Do". Hopefully, Dr. Nick is not questioning my authority on that subject, but I'll do my best. For those who don't know me, I'm James Edward Gray II. I've been in the Ruby community for a long time, and these days, most people know me as a regular panelist on the Ruby Rogues podcast. I've actually seen some tweets referring to me as Mr. Ruby Rogues or the Chief Ruby Rogue, which is a bit odd since it's Chuck's show. I guess that means I'm the most opinionated one or something like that. I don't know, but either way, it probably doesn't reflect well on me.
00:00:46.229 This topic probably needs some explanation because you're likely to find it bizarre and quirky. A lot of times, when people watch over my shoulder while I'm programming, they say, "I didn't know you could do that!" or "How do you do that?" So, I thought it would be cool to take some of those things and put them together in a talk to show a bunch of people this kind of stuff.
00:01:09.799 I figured that ten items would give a good, well-rounded set, but then I realized they stuck me in the big room. I thought, ten is kind of weak. If I had known they were going to stick me in the big room, I would have written a better talk. So, how about we aim for 42? You didn't know Rails could do 42 things. Oh, yeah, stop clapping; we're in a hurry!
00:01:38.460 I tested all of these things on the current stable release of Rails, so they do work on that. If you try on anything else, you're on your own. So, let's get started. First of all, the number one reason everybody should use Rails: you can get a hug every single Friday! If you see Aaron Patterson, he does these tweets with virtual hugs, and people tweet him hugs back. There’s a site that collects all of them. Some of these were awesome! Here's fellow Ruby Rogues member looking pretty nice, giving a hug. It was nice of these ladies to participate! If you get a hug from an alien, how cool is that? Or from Boba Fett? Pretty cool! But this one can't be beat: the underwater Friday hug!
00:02:20.180 Okay, I'm going to poke around Rails in different places. We'll start by looking at the interface and see what we can find. One of the things you can do in Rails now is to actually have a Rails application in one file, a pretty small file. All you need to do is require the right ingredients, build a Rails application, set some routes, and write controllers as normal, and you're off and running. This is a complete Rails application so you no longer have to have that massive file structure anymore. It's kind of a testament to how much they've componentized things.
00:04:03.430 Another thing Rails knows about is these special comments like 'TODO', 'FIXME', and 'OPTIMIZE'. There's a rake action that takes these comments and tells me about them. You can ask for just 'TODO' or just 'FIXME'. You can even make your custom ones if you want to leave a note to someone, allowing Rails to search for that particular note. TextMate users, there's an I-See-A bundle that understands these comments, at least the 'TODO' and 'FIXME'. You can just bring up that bundle and get to them that way, and they're hyperlinked.
00:05:02.820 Another fantastic feature is the ability to sandbox the console. If you want to play around and have your database changes reverted later on, you can do that. You can see here that I don't have anything in the database, and then I fire up a console in sandbox mode, add some entries, and now we have quite a bit in the database. But as soon as I exit, it’s all undone! Behind the scenes, it wraps everything in a transaction. It doesn't roll back when you exit, so you can play around with database stuff without committing.
00:06:12.669 I didn't know about this one until recently. You can call helper methods in the Rails console, which gives you this helper object, and you can just put any method you want on the end of that. This is a neat way to play around with that code. I like using non-Webrick servers in development; it’s pretty easy to do. Just mention your gemfile and specify the server you want, and then pass it as an argument to the Rails server. In this case, I'm using Puma. I find it's usually a little faster or more full-featured.
00:07:04.819 Here's a neat one that Josh Susser pointed out to me: a lot of times when creating a plugin or something, we do configuration in a file in config initializers to set some things up. A better way is in your Railtie; you just assign this object using support ordered options to some setting on the config object. Then in a Rails application, as long as that's been loaded before you configure your application, you can just use 'config.your_library.setting' and assign it like that. Basically, you can configure your plugins the same way Rails configures itself.
00:08:04.740 If you want to see a good real-world example of this, Sass works this way, so you can check it out. This one’s great; this is my new favorite website. You guys like this? Awesome! This is my favorite part right here! I'm sure that PHP guys don’t have any drama; yeah, no way they do! Let’s look at the database. You guys know you can do migrations on the database, right? Passing arguments, strings, and stuff like that. Rails now understands a shorthand form for this.
00:09:14.180 So you can do it a little shorter by leaving off the typesetting, which gets you a string automatically. You can also give limits if you want in curly braces, which can limit the field to a certain size. And more on migrations: you can specify indexes now directly in the command line, which is pretty cool. You can do normal indexes or even specify 'unique' if you want a unique index; you just put those parameters after the type.
00:10:05.240 Another thing you can do is, I'm embarrassed to admit it, but until recently I always created the user ID column myself in the migration. It turns out migrations know how to do associations. You can say that you have an article and it references a user. Doing this sets up the column, which creates the user ID; it also adds an index for better performance. If you generate the model as well, it will insert the association in the model for you! This is a nice shortcut!
00:10:49.880 If you don't like the word 'references', it also understands 'belongs_to', which is the same thing. So, I'm a little embarrassed to admit that when people ask me if a migration has been applied, I would log into the database and try to find the fields. But now Rails can tell you if a migration has been applied! You just run this rake task, and it will tell you which ones have or haven't been applied; it’s kind of handy.
00:11:38.960 This next one isn’t for you; it's for me. This is the most common question I get emailed, so I figured I’d put it in here and throw it on the internet to stop answering those emails. If you have some data you'd like to load into your Rails database, it's pretty easy with the standard CSV library. Just require CSV, loop over the data, and the two things you probably want to do are turn on header parsing and switch the headers to symbols. This will make them look like what Rails expects for your ActiveRecord fields, and then just convert each row to a hash as you pass it along, allowing you to load your database efficiently.
00:12:21.210 Rails can also write CSV directly to your database now! Aaron Patterson added this option to serialize, where if the object you pass understands load and dump, Rails will use that to serialize the content and bring it back. So here, I'm delegating to the CSV library, turning content into CSV, and then turning it back again. You can serialize objects because I put an array in and get it right back, but if you look at the database content, it's actually CSV. I think that reads a little better in the database; it's usually easier to query than that jumbled data.
00:13:05.240 Here’s a good one I didn’t know about until I saw a blog comment from Ryan Bates recently: a lot of times, I just want a specific field from the database. Previously, I would pick that field and then map over it to pull them out. Rails now has a method for this called 'pluck', so you can just pluck them right out of the database as a set of fields. You can also add 'distinct' to it if you want to get unique items from the database. So, if you want to see all the different statuses, you can just pluck them out.
00:13:52.480 I always seem to use this one on accident when I'm using code in the console, but then I actually tried it on purpose the other day and thought it was pretty cool. So, say you have some events, and they have different triggers. You can count on that count; you get 13. However, if you use 'group' in front of that count, it will count the individual groups; so you can see how many of each type you have.
00:14:34.710 This was a patch that Josh Susser did for Rails not too long ago that allows you to override the association methods ActiveRecord creates for you. If you have a car as in an example, tracking the owner of the car, and when a new owner gets set, maybe you want to keep track of the old one. You can write the new method and then just delegate it up to the method that Rails gave you. You couldn’t do that easily without some trickery or aliasing, so that’s handy!
00:15:22.960 This trick is somewhere between cool and evil; I think it’s my favorite kind of trick. Sometimes, I want to instantiate an ActiveRecord object and tell it what it is, like its ID and other properties. You can do that with the instantiate method. Trying to use the standard constructors won’t let you set the ID and things like that, but you can do this. This method comes in handy for testing and similar scenarios.
00:16:16.420 There’s another tip from Josh’s blog post: Postgres doesn’t have to have a limit on its strings like MySQL does. Unfortunately, Rails enforces these limits. However, you can use a little bit of code to disable that limit, allowing you to have limitless strings in Postgres. This way, you can store, say, 10,000 characters without a problem. Of course, you can always use a text field for that, but a good reason—given in Josh’s blog—is that maybe you're using a form that’s going to decide on text fields or text areas based on the field type, so you can still gain that benefit.
00:17:06.000 Okay, this is one of my favorites, and it’s a big reason for using Postgres now! With Postgres, you get full-text search. Say we have an article with a subject and body. We’re going to index these in search on in a migration; you can just add a column, tell the concatenated search content, set the type to text using Postgres, and you need to use a little SQL here. This is one of the biggest points of confusion I think between Postgres and MySQL.
00:17:40.770 MySQL has a kind of crappy full-text search engine, whereas Postgres has a real full-text search engine. You can just set that as your database to index it, then you can create a trigger to keep it updated whenever the data changes. You need to use some Postgres syntax for the actual searching, but one trick you can do is to use the TS query function; you just need to pre-process the user's input. But if you use a plain TS query, it’s a straightforward way to perform the search, making it very easy to implement!
00:18:30.440 You can see that I can create a bunch of records here, and you can search by matching fields with that content. It doesn’t care about capitalization, and I told it was English, so it's doing some stemming for me as well. If you search for 'stemming', you'll get this stemmed result right. This is a real bulletproof search feature—great stuff and a good reason to use Postgres.
00:19:44.880 This one doesn't apply to every scenario, but if you can use it, it’s kind of cool. If you have an application where all your users are manipulating their own data, and it's not like site-wide data, you can actually put each user in their own database. This doesn’t take much code. It works great with something like SQLite, where each database is in a separate file. You just load some configuration, swap out the database name, and then reset ActiveRecord's connection.
00:20:25.600 I stuck this in a library and ensured it was required by my application, then built migrations for adding new databases and for migrating all existing databases. When applying the migrations, you don’t really need to know all this code; I basically stole it right out of Rails’ own migrations, except for one part where I'm just calling that method to switch databases when I need to.
00:21:26.360 You can see this in practice as I add a couple of databases and migrate them all up. If you look at my migrations, they’re happening multiple times—once for each database—so it copies across. And then the same thing for the console here; I can use that method to switch databases at will, and notice if I pump a bunch of records in here, the IDs respect because they’re in totally different databases.
00:22:34.220 To set this up in the application controller, I just use that method based on whatever means I need to switch databases. In this case, I’m using the subdomain. The end result is that you can look at the same page, but depending on the subdomain you pass, you see totally different results because it's pointing at a different database. This can be extremely efficient, by the way, because your database doesn’t grow infinitely larger. You only have your users' records in that database, and you’re not all fighting over the same pipe.
00:23:27.210 A note: when you have one database, everything's got to go through that one pipe, but when each set of data is in a separate file, you're just fighting for access to that resource, which isn’t a big deal. Let’s take a break here for a second! I didn’t think I could deliver 42 things straight, so we’re taking a break!
00:24:30.300 We’re going to discuss why I do this. It seems weird to throw a bunch of random ideas at Rails, but I read a cool study recently comparing old school education techniques. When I was in school, they made you cram—memorizing facts—whereas nowadays they focus more on freestyle thinking, as if you could get away with only thinking and not knowing the facts. However, tests have shown that all that memorization is pretty handy!
00:25:04.230 It turns out that having a strong foundation of knowledge allows you to build ideas more effectively; if you have a bunch of knowledge, you have more confidence and points to leap from. I hope this gives you some new things in Rails that you haven't seen before and motivates you to play around more. Also, I had a lot of fun reading through Rails' source code to figure some of this stuff out. If you go through and play with these features, I promise you'll learn something too!
00:26:56.530 No more breaks; we’ve got 21 more to go! Let’s get back to it! We should probably talk about how Rails affects Ruby, as it modifies several aspects of it. I can't show you all of ActiveSupport; it's massive, but I'll pick out some tidbits. One great feature is that you can write some data to the filesystem atomically. If you just want a file to pop out fully formed, you can call a method, give it a path, and where you want it to appear, write some data to it.
00:27:49.470 Behind the scenes, it creates a temporary file, dumps the data there, and then moves it into place at the end, which is an atomic operation on most filesystems. Be careful if your NFS drive is involved, but otherwise, it probably works fine. There’s no danger of getting a half-completed file—it will appear all at once! You can see that this is useful if you have some process watching directories for files to appear in.
00:28:59.110 You probably know about the merge method in Ruby, where you can join two hashes. But if you’re messing with nested hashes, the inner hashes might just get replaced because it only works at the top level. Rails has a deep merge method that will traverse all levels to merge all hashes down the tree—handy with parameters since they often come as big nested hashes.
00:29:56.190 Another trick I use for parameters is using the except method to knock things out of a hash if I want to pass all parameters except for specific ones—such as the controller or the action, which doesn't apply at this point.
00:30:31.890 Here’s another one that’s a bit tricky to explain, but it relates to reverse merging. If an item isn't in a hash, it behaves like normal merging, but if the item is already there, merge will replace it. This makes it hard to set defaults for like a parameters hash because in Ruby, if you want to do defaults, you create a hash and put your defaults in, then merge in the real data. But since params already exist, you can use reverse merge for that, allowing you to add defaults only to items not already in there.
00:31:48.490 This last trick involves Rails’ ability to handle validations cleverly. Say we had a status on our articles, and I want to ensure it has a value. Rails allows you to use inquiry methods—so you can ask if you’re in development, test, or production environments. You can do something similar with status fields by adding inquiry methods based on a field based on constants defined for all its available states.
00:32:51.810 So if you check the statuses of an article, you could simply inquire whether it’s a draft or published without too much hassle. This would make your articles and any valid fields really user friendly and easy to maintain.
00:34:02.470 Now let's get to the views. A lot of these tips come from the 'Rails View' book I've been reading, and it’s very good for anyone looking to expand their Rails knowledge. Here's a good one—HTML comments can always be used but ERB has its own comments and tags inside your ERB template. The benefit is that they disappear at compile time, meaning users never see them.
00:35:11.730 I think it’s great. I’m the one who wrote the ERB documentation originally, so I’ve been familiar with this mode forever. Nobody else seems to use it, but if you want to remove unnecessary symbols like double angle brackets, you can replace the ERB implementation with one that supports this behavior.
00:36:17.370 You can then use a single % at the beginning of a line to signify that it’s all purely Ruby code. The great thing about this is that it gets removed from the final output, making for cleaner processing. This approach is great for if statements and iterators, as it keeps the code clear and concise.
00:37:05.370 Another common technique involves displaying a subtotal and tax total in views. You need to calculate the tax, but remember to do it within the block to avoid awkward variable assignments that can disrupt the logic flow. Using blocks makes the code cleaner and helps limit the active scope to specific calculations.
00:38:24.960 This leads us to rails helper methods. For example, the content_tag helper creates a div and can be used to generate HTML segments with given IDs and classes, making CSS styling much easier. If you want to loop without manually handling the internal for loop mechanics, Rails now understands passing an array directly to create multiple elements effortlessly.
00:39:09.730 First, when you render an object as a partial, Rails queries the class to decide how to render it, whereas now it allows the object to dictate this. This means you can utilize partial rendering based on specific properties of the objects. Additionally, this makes it simpler to maintain context between rendered views by using flags to specify what type of variable you want to maintain in that specific context.
00:40:42.580 Also, an important point is to avoid overly complex selection menus when presenting users with many options. Rails has a helper for this now! You can use group_options_for_select, which allows you to group items into subcategories, creating a more manageable and user-friendly experience.