The Renegade’s Guide  to Hacking Rails      Internals      Pradeep Elankumaran         Michael Bleigh         Intridea, Inc.
vendor/rails   (or edge rails)
why hack the internals?
you’ll learn a lot
know your tools
you may have to
you can do some very cool stuff
asynchronous  goodness
keeps your app     DRY
keeps multiple  apps DRY
effective ownage
hack when you   need to
internals are dangerous
cover your ass  with tests.
enough talk. code time
Part 1Initialization
initializer recap
require ‘config/environment.rb’
Part 2The Class Loader
require v. load
const_missing( )
has_many :users  :users => User (not loaded yet)  ActiveSupport::Dependencies.load_missing_constant(:User)    (searches th...
let’s talk about     plugins.
app-slice plugins?
there are a few options         engines          desert        tiny_apps
what ours is gonna do • controllers, models and views • routes • all easily overridden by RAILS_ROOT/app
plugins recap
first lesson: don’t book flight to arrive 1.5 hrs   before conference
I don’t understand alot of Rails internals
approach itpractically
find anentry point
trial and error,   and error,   and error.
Part 3Rails Routing
case study:subdomain-fu
SubdomainFu To-Do1) add subdomain to helpers2) add condition to routes3) profit
Route Helpers
the entry point:CODE SPELUNKING
start withfamiliar turf:   url_for
selectivebe the spider       ^
know when  to quit
code time.
Route Conditions
the entry point: JAMIS BUCK
extra credit:EXISTING PLUGIN
great coders   steal.
code time.
for more route
Part 4ActionView
case study:  uberkit
What’s In The Kit1) site navigation generator2) DRYer forms
built to bebuilt upon
Helpers on Steroids
the entry point:   form_for
The Goalmenu ‘nav’ do |m|  m.action Home, home_path  m.action My Profile, user_path(user)end          Becomes (when at /ho...
collect and buildfor max control.
code time.
Form Builders
FormBuilder puts the  f in f.text_field
the entry point:EXISTING PLUGIN
code time.
wrapping up
rails is just ruby.
know your advanced ruby        (great book, not affiliated)
never replace the   rails code    directly
take it slow
test a lot[seattle.rb atRailsConf 2008]
keep your code  beautiful   concise    clear
Renegades Guide to Hacking Rails Internals
Upcoming SlideShare
Loading in …5

Renegades Guide to Hacking Rails Internals


Published on

Published in: Technology
  • Be the first to comment

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide
  • good morning guys!\nthis is the renegade’s guide to hacking rails internals\n(introduce ourselves)\n
  • we’re from Intridea\nproducts & services\nscalr, crowdsound, mediaplug & socialspring\n
  • how many of you have opened up vendor/rails to take a look inside?\nhow many have written a plugin?\nhow many have written something in lib/ that has overridden rails internals?\n
  • so why would you even want to hack the internals? there are quite a few compelling reasons...\n
  • you will learn a lot.\nthe rails internals are a veritable treasure trove of excellent Ruby code. you learn the internals and you will pretty much learn all the ins and outs of Ruby syntax and the way different pieces fit together within the language\n
  • rails is a tool built on ruby. if you use it everyday, it only makes sense to know its limits and the way it’s put together.\n
  • you will always run into instances where \n
  • learning the internals lets you do lots of cool stuff -- it’s an investment\n
  • we can offload stuff to queues (example starling)\nwe can run separate queue processors independent of the rails app that leverage the code you have written for the app\n\n \n \n\n \n
  • you can really keep things DRY within your codebase by knowing how the Rails class loader works.\n\n \n
  • knowing the internals will let you extract full components of apps into their own separate, shareable rails apps. we will be attemping this soon.\n
  • if you know how the Rails stack operates, you can effectively create ANYTHING using the Rails code you have already written. \nthis is when Rails actually fulfills its promise of being a swiss-army knife. \n\n \n \nkeeping this in mind....\n
  • a few words of advice. you should only hack the internals when you really need to....\n
  • you can screw things up that you didn’t even mean to, and that’s why you should always...\n
  • rails has quite an extensive test suite. if you’re modifying a piece of core code, please make sure you look through the test suite to learn how it’s expected to behave. then make sure you write specs or tests to make sure your changes work. then write more tests to make sure your edge cases work. \n
  • first thing we’re going to do is to run through how rails actually loads itself.\n
  • \n \n
  • let’s do a short recap of the important stuff\n
  • there’s a Rails::Initializer class that’s used to setup all the necessary classes and initialize Rails.\n
  • you can edit your app’s settings by modifying the Rails::Configuration instance that’s available in the config/environment.rb file\n
  • the process method contains all the startup steps, in the same order that Rails loads (how convenient!)\n
  • is your best friend. require config/environment.rb in any file and it’ll load up the Rails stack for you.\n
  • Now that we know how Rails loads itself, let’s see how the Rails class loader operates.\n
  • the rails class loader is completely contained within the ActiveSupport::Dependencies module. it’s a really dense piece of code when you first look at it, and it leverages a lot of Ruby magic to get stuff working. \n
  • Rails uses require, which only loads each specified file once in production mode. \nrails uses load() which loads the same file multiple times in development mode.\n
  • the dependencies module uses the const_missing hook to identify classes to load.\nThis is a standard Rails hook method that’s called whenever a constant that hasn’t been loaded is found. The behavior of the Dependencies module goes a little something like this...\n
  • here’s what a typical load trace looks like.\ncode walkthroughs are a little boring -- so let’s do a short exercise. \n
  • rails’ plugin interface is a great way to re-use code across multiple apps. however the default implementation has one big limitation. there are no app-slice plugins. \n
  • these are plugins that have their own controllers, models, helpers, views, routes and migrations. IE -- self-contained “apps” that can be extracted out into their own quite easily, and also integrated with multiple rails apps with the option of being heavily customized. \n
  • the good thing is that Rails’ plugin loading code is quite extensible, and slice handling is not impossible to implement. the engines and the desert plugins are a few released plugins that help with this.\nIntridea’s SocialSpring social networking platform uses tiny_apps, a custom classloader that allows us to put together custom social networks very, very quickly.\n\n \n \nbut for the sake of learning, let’s roll our own.\n
  • so let’s code ours up -- first we’re going to run through the plugin loader, then we’ll code up our own version\n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • let’s take a 10 minute break. when we come back, michael’s gonna run through hacking actionview and routes!\n
  • \n \n
  • I’m going to be presenting with a bit of a different approach. Honestly, I don’t understand a lot about Rails internals. To be even more honest, I don’t really want to understand everything. I’m happy to have a little bit of magic in my life.\n
  • I don’t hack internals because I think it’s a lark, I do it because I see the potential for real benefit to the projects I’m working on. If you don’t see yourself saving a lot of time and effort down the road for your efforts in working with internals, you probably shouldn’t be doing it.\n
  • The way to manage that initial intimidation factor is to find an entry point, a place that you can get your bearings and go from there. It could be a blog post, an existing plugin, or if you’re in uncharted waters, you might just have to read the code yourself.\n
  • When you’re monkey-patching, don’t expect things to go right the first time. It takes time and patience and a whole hell of a lot of restarting your environment.\n
  • I’m going to go over some of the Rails Routing internals both in terms of how it works and where it’s easy to extend.\n
  • My knowledge in this area comes from writing the SubdomainFu plugin, which allows Rails apps to easily handle subdomains.\n
  • So I had a couple of goals for SubdomainFu. One, I wanted to be able to use a subdomain option on any of my route helpers and it would “just work.” Two, I wanted to add a new condition to routes that would allow me to specify things about subdomains in the routing DSL itself.\n
  • So first up, lets tackle the route helpers.\n
  • Unfortunately, I wasn’t really able to find a good blog post or existing plugin that worked with adding a universal option to all URL generating methods. I had to do the legwork myself and read through the internals to learn how it worked.\n
  • I started with something that I was familiar with. I knew from various stack traces that url_for was called pretty much every time a route helper was used; this was my entry point.\n
  • Once you have a starting place, you have to begin to understand the area. Act like a Google bot, expanding your knowledge by understanding a method, then understanding the methods that that method calls. If a method’s purpose is self-evident and you don’t need to modify it for the task at hand, go ahead and skip past that.\n
  • You have to know when to quit. It’s a good idea to spelunk for code for a bit until you get a fuzzy general idea of what’s going on, then tackle it from a practical perspective and learn the rest while writing code.\n
  • OK, so that’s how the general process works when you don’t really have an idea of what you’re dealing with, let’s see some of the fruits of this labor! [LIVECODE TIME]\n
  • So we’re done with step one, now it’s time to move on to step two! We want to add a new condition to the routes that allows us to specify subdomain-based conditions.\n
  • Here, I was able to get a lot more help from the early settlers. Jamis Buck’s blog has a couple of really nice looks at routing internals that gave me a general idea of what I was doing.\n
  • Even better, there was already a subdomain_routing plugin available! The easiest way to hack Rails internals is to have someone else do it for you, but I wanted things to work a little differently so there was still work to be done.\n
  • The greatest thing about Ruby is that there is just a ton of open source out there. No matter what you’re doing, one of the most helpful things you can do to learn more is track down the most similar open-source project to what you’re working on and read their code. It’s really invaluable experience, even if they aren’t doing things the same way you want to or are working towards a different goal.\n
  • Ok, so let’s take a look at the route conditions code and how we hook into it. [LIVECODE]\n
  • So we’ve really only scratched the surface of routing here. To really get into the nuts and bolts I really recommend reading the posts on Jamis Buck’s blog; there’s enough there to be an entire tutorial session on its own.\n
  • \n \n
  • Once again my experience with this comes from writing a plugin, this time just to make my life easier when making Rails apps! UberKit provides some UI helpers that make it really easy to build navigation menus and forms in Rails apps.\n
  • The UberKit includes UberMenus, which is a set of helpers designed to make creating navigation menus super-easy and UberForms, which provides a custom form builder to automatically generate label HTML etc.\n
  • These things highlight a great aspect of Rails: much of the functionality is built to be extended, modified, and improved. We’ll look at some of the ways this is exposed as we go through each of the parts of creating the UberKit.\n
  • Helpers are usually used for little methods to dry up little parts of your code. But with the right approach, they can be used as powerful interface building blocks that can be re-used from application to application.\n
  • So when I started trying to learn about block-enabled helpers, the most obvious place to look was the one that all Rails developers use: form_for.\n
  • The CaptureHelper is invaluable in advanced helper writing. It allows you to grab the output of arbitrary blocks of view code and store it in a variable. It’s how content_for works, but it can be repurposed to make some powerful helpers for us!\n
  • What we basically want is a really readable way to write out a menu so that it automatically becomes well formed state-aware semanticHTML that we can easily style.\n
  • The way we’ll do this is we’ll create a custom builder class that collects input from the user, then builds output. By doing this we give ourselves greater control over the construction of the output than simply outputting as we go.\n
  • So let’s make it happen! [LIVECODE]\n
  • Form Builders are a part of Rails that is built to be extended, but not really well documented in that way.\n
  • So a form builder is a class that generates the components of a form. Most commonly it’s the class instantiated in the form_for helper.\n
  • So this isn’t exactly a brand new idea. I learned the basics of extending form builders from a tabular form builder plugin.\n
  • Here we’ll really benefit more from going straight into the code so let’s get started!\n
  • \n \n
  • and ruby’s a great language to work with and extend. however\n
  • most of rails is advanced ruby, and you’re advised to learn the language in and out before you modify the internals. you should have the ruby hook methods at your fingertips, and should be comfortable rolling your own classes and modules.\n
  • never, ever override the source code directly, always create new files and modules and include them judiciously -- this will help you upgrade later. \n
  • whatever bit of code you’re modifying, make sure you know it IN AND OUT. don’t touch code that you have no idea what its doing, it’ll save you a lot of hassle. \n
  • test your changes a lot, and in the same vein, read the tests for whatever code you’re modifying so you know what it’s REALLY about.\n
  • and finally -- i’m preaching to the choir, but keep your code beautiful, concise and clear -- all three of these characteristics are not mutually exclusive \n
  • Renegades Guide to Hacking Rails Internals

    1. 1. The Renegade’s Guide to Hacking Rails Internals Pradeep Elankumaran Michael Bleigh Intridea, Inc.
    2. 2. vendor/rails (or edge rails)
    3. 3. why hack the internals?
    4. 4. you’ll learn a lot
    5. 5. know your tools
    6. 6. you may have to
    7. 7. you can do some very cool stuff
    8. 8. asynchronous goodness
    9. 9. keeps your app DRY
    10. 10. keeps multiple apps DRY
    11. 11. effective ownage
    12. 12. hack when you need to
    13. 13. internals are dangerous
    14. 14. cover your ass with tests.
    15. 15. enough talk. code time
    16. 16. Part 1Initialization
    17. 17. initializer recap
    18. 18. Rails::Initializer
    19. 19. Rails::Configuration
    20. 20. Rails::Initializer#process
    21. 21. require ‘config/environment.rb’
    22. 22. Part 2The Class Loader
    23. 23. ActiveSupport::Dependencies
    24. 24. require v. load
    25. 25. const_missing( )
    26. 26. has_many :users :users => User (not loaded yet) ActiveSupport::Dependencies.load_missing_constant(:User) (searches through Dependencies.load_paths) (finds the first file that matches => app/models/user.rb) require_or_load(‘app/models/user.rb’)
    27. 27. let’s talk about plugins.
    28. 28. app-slice plugins?
    29. 29. there are a few options engines desert tiny_apps
    30. 30. what ours is gonna do • controllers, models and views • routes • all easily overridden by RAILS_ROOT/app
    31. 31. plugins recap
    32. 32. Rails::Plugin
    33. 33. Rails::Plugin::Loader
    34. 34. Rails::Plugin::Locator
    35. 35. Dependencies#const_missing
    36. 36. break
    37. 37. first lesson: don’t book flight to arrive 1.5 hrs before conference
    38. 38. I don’t understand alot of Rails internals
    39. 39. approach itpractically
    40. 40. find anentry point
    41. 41. trial and error, and error, and error.
    42. 42. Part 3Rails Routing
    43. 43. case study:subdomain-fu
    44. 44. SubdomainFu To-Do1) add subdomain to helpers2) add condition to routes3) profit
    45. 45. Route Helpers
    46. 46. the entry point:CODE SPELUNKING
    47. 47. start withfamiliar turf: url_for
    48. 48. selectivebe the spider ^
    49. 49. know when to quit
    50. 50. code time.
    51. 51. Route Conditions
    52. 52. the entry point: JAMIS BUCK
    53. 53. extra credit:EXISTING PLUGIN
    54. 54. great coders steal.
    55. 55. code time.
    56. 56. for more route
    57. 57. Part 4ActionView
    58. 58. case study: uberkit
    59. 59. What’s In The Kit1) site navigation generator2) DRYer forms
    60. 60. built to bebuilt upon
    61. 61. Helpers on Steroids
    62. 62. the entry point: form_for
    63. 63. ActionView::Helpers::CaptureHelper
    64. 64. The Goalmenu ‘nav’ do |m| m.action Home, home_path m.action My Profile, user_path(user)end Becomes (when at /home)<ul id=nav> <li class=current><a href=/home>Home</a></li> <li><a href=/users/mbleigh>My Profile</a></li></ul>
    65. 65. collect and buildfor max control.
    66. 66. code time.
    67. 67. Form Builders
    68. 68. FormBuilder puts the f in f.text_field
    69. 69. the entry point:EXISTING PLUGIN
    70. 70. code time.
    71. 71. wrapping up
    72. 72. rails is just ruby.
    73. 73. know your advanced ruby (great book, not affiliated)
    74. 74. never replace the rails code directly
    75. 75. take it slow
    76. 76. test a lot[seattle.rb atRailsConf 2008]
    77. 77. keep your code beautiful concise clear