Having worked in web application development, Rails quickly felt familiar. Rails employs many common application design principles. One example is the MVC (Model, View, Controller) pattern, a common organization practice for separating business logic and presentation layers. Rails distinguishes itself by strictly enforcing MVC out of the box. Data models, controllers, views, and database migrations all have a place where they belong. Rails creates these directories when a new project is initialized, and knows exactly where to look for each type. It also provides powerful generators for getting boilerplate files in place quickly.
Every new Rails application will generate the following directories (simplified to save space):
app/ ├── controllers/ ├── models/ ├── views/ └── …
Rails generate will create files in the appropriate directories. Using a todo list application as an example, the following generator will create a model, view and controller for a Todo resource:
>_ rails generate resource Todo name:string done:boolean
The following files would be generated:
app/ ├── controllers/ │ └── todos_controller.rb ├── models/ │ └── todo.rb └── views/ └── todos
Another common pattern utilized by Rails is the use of model objects to reflect database tables. Rails takes this practice a step further than most. Many times in Java I’ve created classes having attributes with the same names as database columns in order for a framework to recognize those names and link them up to the database. Rails works in the other direction, and without the developer writing a single line of code in the model’s class. When a class shares a name with a database table, all the columns in that table are automatically available as attributes of the model.
Given a Todos table with the following structure:
A class named Todo will have all the attributes from the todos tables in addition to several helpers provided by Rails.
# app/models/todo.rb class Todo < ApplicationRecord end
>_ rails console => todo = Todo.new(name: 'Do a thing', done: false) => #<Todo name: "Do a thing", done: false, ...>
Databases are a crucial part of application development and as an application grows, data changes are inevitable. In my experience, keeping a consistent database schema across development, staging, and production environments can be a source of frustration. Rails removes this frustration by having excellent database schema and migration tools built-in. Rails can quickly facilitate creating a new table or altering existing tables in a way that keeps all environments consistent. Just like models, views, and controllers, database migration files already have a place to belong as soon as the project is created and can also be generated by Rails.
Even when it was first introduced Rails was not a bold new way to do things, but a meticulous curation of established good practices. Many of it’s conventions may be familiar to someone who has worked with web applications.
Convention over configuration is one of the key doctrines of Rails. It’s an acquired taste, but is a major factor in the productivity boosts Rails can provide. Years ago I explored Rails and I recall struggling with this particular Rails-ism at the time. Admittedly I can be a control freak, and this Rails philosophy takes a great deal of control away from the developer. I like to make decisions about how my app is structured and organized. However, in reality those decisions are quickly forgotten or re-evaluated in my next project. Coming back to a project that utilized my own ingenious configuration means trying to step back into a former version of myself. Likewise when working on a team, it’s likely someone else made all the configuration decisions, forcing me to get into their head to understand the structure of the application. I've learned there's always a convention, it's just a matter of whose convention. Rails speeds up that configuration stage of development and introduces consistency. Every project will organize controllers, views, services, models, even database migrations in the exact same place. To shift from one Rails project to another takes little to no mental adjustment. Not everyone will love the way Rails organizes its projects — I can think of some things I’d do differently. But I've come to appreciate consistency over the desire for total control.
My favorite Rails utility has been the console command. I’ve never experienced anything quite like it in other application frameworks. Hop into any Rails project and run `rails console`. Suddenly, you’ll find yourself immersed in that application's runtime with access to every part of the application in a REPL environment. For me this feels like being one with the application, as if it’s now an extension of myself. The console can do anything the application can do. This can be useful for tinkering around with various parts of the application to how it works. I’ve used Rails console for debugging or discovering how certain classes work.
In a Java application, when I need to exercise a class or method, the fastest way would be to create a unit test. For difficult-to-unit-test functionality or general tinkering, I’d create a throw-away static final main (Java folks know the pain) and execute that. The Rails console gives me the tools to exercise my code myself when I need to.
Rails console is one of the best programming tools I’ve used. I realize it’s just an extended irb REPL. Once again Rails is good at curation. Simple tools that already exist, like irb, can be made to feel magical with just a little something extra.
I’m still new to Rails and learning new things all the time. Already I can attest to its sharp focus on productivity and getting things done. I accepted a job here at Known Decimal back in April. Working in Rails, I was quickly able to get my bearings and start delivering features for one of our major clients. I didn’t have to ask many questions because in Rails when I’m working with a resource named User, I know with a good deal of certainty where to find the views, models, and business logic related to Users.
Every layer of the software stack from the database to the front-end has unique challenges. As software has evolved, the pieces have grown even further apart making it more and more challenging to understand the full breadth of an application. Rails was created with the full stack in mind and despite the changing tides, it still provides a solid framework that empowers single developers to deliver entire applications.