Transitioning from Sinatra to Rails

We made our first foray into Rails this week. Because we wrote our Sinatra apps in the MVC model, the transition was jarring mostly in the simplicity of it — how many things Rails does for me, that I had to create for myself in Sinatra. These are just a few of the most notable differences.

Routes, Controllers, and Views

This in Rails:

Rails.application.routes.draw do 
resources :tasks
end

Replaces this, in Sinatra:

class TaskManagerApp < Sinatra::Base 
  get "/tasks" do 
@tasks = task_manager.all
erb :index
end
  get "/tasks/new" do 
erb :new
end
  post "/tasks" do 
task_manager.create(params[:task])
redirect '/tasks'
end
  get "/tasks/:id" do |id| 
@task = task_manager.find(id.to_i)
erb :show
end
  get "/tasks/:id/edit" do |id| 
@task = task_manager.find(id.to_i)
erb :edit
end
  put "/tasks/:id" do |id| 
task_manager.update(params[:task], id.to_i)
redirect "/tasks/#{id}"
end
  delete "/tasks/:id" do |id| 
task_manager.delete(id.to_i)
redirect "/tasks"
end

That’s not quite accurate. The first snippet automatically creates all seven of the routes shown above and automatically links them to their controller, action, and view, but doesn’t actually create the code that fulfills the logic side of things. The views still need to be rendered in the controllers with their accompanying logic. To see that these routes actually exist and where they point, run rake routes in your command line, and you’ll get this for the above example:

The far right column tells me I now need to create the teachers_controller file that has actions for index,create, new, edit, show, update, and destroywithin it. The far left column shows a path helper that can be used when linking to another route, with _path appended.

Forms and HTTP Verbs

Because many browsers only recognize GET and POST, as HTTP verbs, we had to do something like this in our Sinatra app to get PUT and DELETE functionality:

class Server < Sinatra::Base 
  set :method_override, true 
#other things omitted
end
#Inside the form, include a hidden input, as follows 
# (the value field specifies the verb to use):
<input type="hidden" name="_method" value="put">

In Rails, in the case of PUT/PATCH, you can just put the form in the view corresponding with the action (the edit view) as you would for GET or POST, and it automatically knows which verb to apply to the request. This typically means you can use the same form in a partial for both create and edit, and Rails will apply the correct verb. In the case of DELETE to avoid having to use a form at all, you can do the following in a link, anywhere in your app (assuming the route and ivar have been defined):

<%= link_to "Delete", article_path(@article), method: :delete %>

Testing and Errors

The initial setup for tests is a little different in Rails, but it’s not notable, conceptually. The verbosity of the errors is notable. Errors like the below are everywhere, and tell me exactly what I need to do next. Even if I know where to go next, this is TDD at it’s best (errors in the console when running tests are similarly helpful.) Whereas I understood previously the idea of using tests to drive out development and as documentation, I was mostly using them to test functionality. The idea of writing user stories and feature tests before implementing any code actually makes sense for the first time, and I find myself doing it.

Conclusion

I may never write another Sinatra app, but the learning was invaluable in the deeper understanding it gave me about what Rails is doing behind the scenes. If Rails were the first framework I had learned, it seems like it would be possible to successfully build apps without understanding the HTTP request/response cycle, or even much about database design. I hope that deeper understanding will allow me to transfer this knowledge to other frameworks and languages in the future.