RailsConf 2013

You've got a Sinatra on your Rails

You've got a Sinatra on your Rails

by José Valim

In the video titled "You've got a Sinatra on your Rails," presented by José Valim at Rails Conf 2013, the speaker explores the intriguing intersection of Sinatra and Rails, particularly how to bring Sinatra-like features into a Rails application. The central theme revolves around experimenting with and hacking Rails for educational purposes, focusing on understanding its internals while building functional elements.

Key points discussed throughout the talk include:

- Single File Rails Applications: Valim demonstrates how to create a single-file Rails application similar to a Sinatra app. He emphasizes the importance of understanding the Rails boot process, explaining how the configuration files interact to initiate the application.
- Rack and the Rails Router: The speaker details how Rails utilizes Rack under the hood for routing, showing how routes in Rails correspond to Rack applications. He illustrates how to convert common Rails routes into Rack-style applications seamlessly.
- Middleware: By explaining middleware stacks in Rails, Valim mentions that requests pass through multiple middleware layers, allowing for modifications of incoming requests and outgoing responses. He demonstrates how to inject custom middleware into various layers of a Rails application.
- Rendering Stack: Valim elaborates on the differences between how Sinatra and Rails handle rendering within their frameworks. He highlights how to modify Rails to adopt a more Sinatra-like approach to rendering templates directly within the application's context, thus allowing for instance variables and methods to be shared between the controller and views.

Valim illustrates each point with practical examples, such as building a "Rails natural" end-point that incorporates both Rails and Sinatra styles, allowing developers to define routes using Sinatra-style syntax while still employing Rails' rendering capabilities. This discussion is supported by insights into Rails' internal structure, including the breakdown of the application's various components and how they communicate.

The talk concludes with several key takeaways:

- Experimentation with Rails internals can shed light on its design and improve coding practices.

- Customizing middleware and rendering stacks can enhance the functionality of Rails applications, making them more flexible and powerful.

- Valim promotes an upcoming book titled "Crafting Rails 4 Applications" which provides further in-depth exploration of these concepts.

This insightful talk encourages developers to think critically about their frameworks, experiment freely, and ultimately refine their skills in building robust web applications.

00:00:16.960 Okay, thanks everyone for being here to watch this talk. My talk is called 'You've Got a Sinatra on Your Rails.' I am José Valim, and this talk is about my outstanding Photoshop skills and my amazing naming abilities. Today, we will literally write some code that will create a Rails natural application, and the goal is to hack Rails for fun and profit. The idea is that we are going to build some Sinatra features into Rails, using it as an excuse to better understand Rails internals.
00:00:38.000 A little bit about me: I am a co-founder of Platform Attack, a consulting company in Brazil where we build mission-critical software. You may have already heard of us because of open source projects like Simple Form, a form DSL for Rails, Devise, which is the authentication solution for Rails, and Alexa, which is a programming language that runs on the Erlang VM. I joined the Rails core team back in 2010. I’d also like to acknowledge my two colleagues from Platform Attack who could not be here today, José Valim and Carlos Antonio. I'm sending my regards to them. This talk reflects my perspective, so since I'm on the Rails core team, I know a couple of things about Rails internals and how it works, and that’s what I want to share with you. We are going to go through a lot of code—about 150 lines—and we will discover how Rails operates.
00:01:36.799 Today, we are going to cover four main topics: 1. Single-file Rails applications 2. Rack and the Rails router 3. Middleware stacks 4. The Rails rendering stack Let's get started with single-file Rails applications. If you look at the Sinatra website, they provide a simple example for running a Sinatra application. The standard approach involves creating a `config.ru` file, requiring some dependencies, defining your Sinatra application, setting routes, and specifying what each route returns. All the code we write today will be in this `config.ru` file. So, what is `config.ru`? It’s not related to Russia! It’s a rackup file containing the instructions necessary to start your Rack application. Let's start with a small example. In the `config.ru` file, we call the `run` method, passing a lambda function that acts as our Rack application. A Rack application is any Ruby object that responds to the `call` method, accepting one argument: the environment hash.
00:02:39.760 This environment hash holds all the information about the request. The Rack application needs to return an array with three elements: the response status, the headers (which is a hash), and the body (which can be any Ruby object responding to `each`). For our Rack application, we can return a status of 200, a headers hash indicating the content type as 'text/plain', and a body of 'Hello, World!'. If we place this code in our `config.ru` file and start the application, we can make a request to the specified port, and it will return 'Hello, World!'. This demonstrates how Rack allows us to develop more complex applications. In addition to the `run` method, Rack provides a `map` method to define different behavior based on the requested URL path. For example, if we map requests starting with `/hello`, we can run a specific Rack application that returns 'World'. Everything else can return an empty response.
00:04:08.080 Let’s go back to our Sinatra example. The simple Sinatra application requires Sinatra and inherits from `Sinatra::Base`, allowing us to create a class instance functioning as a Rack application. This instance can process the request and return a response. Now the question is: can we create a single-file Rails application? Let’s try that by reverse engineering Rails. When you create a Rails application since Rails 3, there is a `config.ru` file, which contains two lines: requiring the `config/environment` file and running the Rails application as a Rack application. In this example, we will create an application called 'sample.' The first line requires the environment file, and the second line executes our Rails applications, which acts like a Rack application.
00:05:13.440 Next, we need to analyze what the environment file does. It essentially has two lines: the first line requires the application file, and the second line initializes the application. The application file, which we are used to configuring, requires the boot file and defines the Rails application. The boot file sets up Bundler, which handles dependencies. By examining how Rails applications initialize, we can see that when you boot your Rails application, you first load the `config/boot` file, which sets up the load paths. Then it goes to the application file to define the application and initialize it through the environment file, which integrates the application with the database and loads necessary code. Finally, we reach the `config.ru` file, which sets up Rack.
00:07:10.160 You may wonder why we have so many files to boot Rails. The reason is that different parts of Rails need to hook into various stages of the initialization process. Additionally, there is a Rake file included in a new Rails application that loads the application tasks. The Rake file requires the application file but does not initialize the whole environment, allowing for faster feedback when running Rake tasks without the overhead of a full initialization. For example, tasks like `rake db:migrate` require the database connection and full application initialization. The first time we learned Rails, we were instructed to depend on the environment task, which initializes the application. This separation allows us to hook into different points during initialization and have quickly retrievable feedback without running the entire application.
00:09:02.560 Now let's see how a single-file Rails application might look. I considered live coding, but it would be painful for you to watch me type, so I have this ready. The single-file Rails application is about 27 lines of code, which is the equivalent of the boot file setting up load paths, using Ruby gems. Then, the next lines resemble the configuration of a typical Rails application. Instead of requiring all Rails components, we explicitly include only the dependencies we need—such as ActiveSupport and ActionController—since we are not using ActiveRecord for database connections or ActionMailer for email functionalities. We also define our single-file Rails application class inheriting from `Rails::Application` and set production configurations, such as eager loading and secret key base. The final lines include defining routes and running the application.
00:10:46.720 Now, does it work? After running the single-file Rails application with Rack, a request to the root route returns 'Hello, World!' This demonstrates that we can effectively organize everything within a single file like Sinatra. However, what are practical use cases for a single-file Rails application? It serves educational purposes, making it easier to understand how Rails boots. If you have a gem that depends on a Rails application running, you can place the whole application in a single file for testing. A single-file Rails application is also useful in bug reporting, where you can share a single-file example with your controllers instead of pushing the entire codebase to GitHub. But, of course, I would not recommend using it in production.
00:12:45.440 Now that we have the single-file Rails application working, let’s discuss Rack and the Rails router. In the previous example, we had a route that pointed to a Rack application, which returns an array with three elements: the status, headers, and body. Rack applications also serve as the foundation for every HTTP method you call within your Rails router, including GET, POST, PUT, and DELETE. When Rails sees a route represented as a string, it converts the string into a corresponding controller action. So when you write something like `post '/articles'`, Rails interprets it into an action that is a Rack application, which helps maintain performance and routing efficiency.
00:13:48.720 At this stage, we are ready to dive into writing a Rails natural application building on what we have discussed. I want to demonstrate how Rails natural works in comparison to standard practices. First, we will require the Rails natural file, which implements the Rails natural routes and functionality. When defining our own endpoints through Rails natural, we can use Sinatra-style routes, allowing us various HTTP methods (GET, POST, DELETE). Rails natural enables us to utilize the Rails render DSL, thus facilitating both Sinatra convenience and Rails’ robust capabilities. This allows us to merge the simplicity of Sinatra with the rich Power of Rails.
00:15:20.720 Let's take a look at the implementation of Rails natural. The initial lines will include requiring various dependencies to set up the Rails natural framework. The `RailsNatural` class definition will hold all the routing information, allowing the creation of inline routes, which will store details about each route in a hash structure. Each time you call a routing method such as get, it registers this information efficiently for later use. We will also define the necessary request/response methods, ensuring that Rails natural responds correctly to the routes we define. A method called `call` will be implemented to process incoming requests, retrieving them based on their defined routes and ensuring the correct responses are issued.
00:16:42.720 When we run the code we created for Rails natural, we can see our endpoint mounts successfully and responds exactly as expected. A request to `localhost/hello` will return the content specified by our Rails natural application. Furthermore, while using Rails natural, we can examine the matching process. Unlike regular routes that require exact matches, Rails natural utilizing `mount`, allows paths to be matched based on prefixes. This flexibility means a request like `/hello/world` is processed without requiring the entire path match, enabling a more dynamic handling of requests.
00:18:28.800 This difference in matching enables significant interoperability. For example, we can even use Rack applications from Sinatra or other frameworks inside our Rails routers, provided they follow the same structural principles. Now, let's pause briefly and recap what we've learned so far.
00:19:11.600 To summarize, we discussed four main areas: 1. **Single-file Rails Applications**: We explored how condensing a Rails application into a single file can reveal the essential components involved in booting Rails and provide an educational aspect to understanding its structure. 2. **Rack and the Rails Router**: We examined how Rack underpins Rails routing and serves as a base for all incoming web requests. 3. **Middleware Stacks**: We delved into various Middleware stacks within a Rails application, demonstrating effective techniques for hooking into the middleware and explaining their functions. 4. **Rendering Stacks**: We looked into how we could customize the rendering process in Rails, showcasing how Rails applications significantly evolve over time.
00:20:58.560 Finally, to illustrate how rendering stacks can function creatively, we discussed the change between Rails and Sinatra in handling views. Together, we explored how rendering stacks in Rails require isolating their functionalities, including template management and view paths allowing for enhanced performance. By breaking down responsibilities, it becomes easy to change the underlying operations, creating a powerful rendering process that supports many customizations and advanced features without excessive complexity.
00:22:34.160 In closing, my goal today was to showcase how experimenting with seemingly crazy ideas, such as integrating Sinatra-style thinking in Rails, can foster innovation and deeper understanding of framework internals. It’s all about having fun while learning! This talk is based on insights from my book, 'Crafting Rails Applications,' which covers many of the concepts we discussed today. As a final note, the second edition of the book is launching soon, so keep an eye out for that! It includes updated discussions on modern Rails functionalities and insights into best practices. Follow me on Twitter for updates and slides I’ll be sharing shortly. Thank you all very much for attending!
00:25:00.160 Thank you for your time. I appreciate your participation!