Rails tricks you may not know
Tags: ruby, rails
Published: 2022-10-03 05:57:39

---
title: Rails tricks you may not know
published: true
description: I see a lot of new Rails developers struggling with some of the basics of the framework. I've been using Rails for a while now, and I've picked up a few tricks along the way. I thought I'd share them with you.
tags: ruby, rails
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/unvnoa3plt3u4hs29dhm.png
---

I see a lot of new Rails developers struggling with some of the basics of the framework. I've been using Rails for a while now, and I've picked up a few tricks along the way. I thought I'd share these tricks with you.

## One action, many methods

Sometimes it is usefull to have one action, but make it work with both `get` and `post` (or any other). For example, if you have a page where user can generate a report. User requires to select some dates in range, and then click button to generate report. And what I often see is that people create two actions inside the controller:

```ruby
# app/controllers/reports_controller.rb
class ReportsController < ApplicationController
  def super_report
    # render form
  end

  def super_report_post
    # generate report
  end
end

# config/routes.rb
get 'reports/super_report', to: 'reports#super_report'
post 'reports/super_report', to: 'reports#super_report_post'
```

But there is a better way to do this. Choose the style you like:

```ruby
# config/routes.rb

# Option one
get 'reports/super_report', to: 'reports#super_report'
post 'reports/super_report', to: 'reports#super_report'

# Option two
match 'reports/super_report', to: 'reports#super_report', via: [:get, :post]
```

And in controller:

```ruby
# app/controllers/reports_controller.rb
class ReportsController < ApplicationController
  def super_report
    if request.post?
      # generate report
    else
      # render form
    end
  end
end
```

Now we don't split our logic into two actions, but we have one action that works with both `get` and `post`. It looks nicer, easier to read and maintain.

## Namespacing

Namespacing is a way to group controllers and routes. It is usefull when you have many controllers and you want to group them. For example you have controllers: `PostsController`, `Admin::UsersController` and `Admin::PostsController`. You want different views for admin and want authentication for it.

```ruby
# config/routes.rb
resources :posts

namespace :admin do
  resources :users
  resources :posts
end
```

Now you can access `Admin::UsersController` with url `/admin/users` and `Admin::PostsController` with url `/admin/posts`. You should also use namespace inside the controller:

```ruby
# app/controllers/admin/users_controller.rb
class Admin::BaseController < ApplicationController
  layout "admin" # special view layout for admins

  before_action :authenticate_admin!
end

class Admin::UsersController < Admin::BaseController
  # ... Only admin can work with it
end

class Admin::PostsController < Admin::BaseController
  # ... Only admin can work with it
end
```

```
# Views structure
app/views/layouts/admin.html.erb
app/views/posts/
app/views/admin/posts/
app/views/admin/users/
```

## Scoped associations

Let's say we have two models: `User` and `Post`. And we want to get all posts for user. We can do this:

```ruby
class User < ActiveRecord::Base
  has_many :posts
end

class Post < ActiveRecord::Base
  belongs_to :user
end

user = User.first
user.posts
```

But what if we don't ever want to include posts for user which are published? Often I see people doing this:

```ruby
class User < ActiveRecord::Base
  has_many :posts
end

class Post < ActiveRecord::Base
  belongs_to :user

  scope :published, -> { where(published: true) }
end

user = User.first
user.posts.published
```

And this is fine, but you can somehow forget to add `published` scope to your query. And then you will get all posts, even unpublished. And this is not what you want. Some people use `default_scope` for this, but it is not good idea. So what we can do? We can use `scope` in association:

```ruby
class User < ActiveRecord::Base
  has_many :posts, -> { published }
  has_many :not_published_posts, -> { not_published }
end

class Post < ActiveRecord::Base
  belongs_to :user

  scope :published, -> { where(published: true) }
  scope :not_published, -> { where(published: false) }
end

user = User.first
user.posts # only published posts here
```

## Migrations are not only for schema building

You can use migration to add some data to your database. For example you have model `User` and you want to create admin with id -1 to database, so anywhere, in any environment you want to be sure, that such user exists. You can do this:

```ruby
class AddAdmin < ActiveRecord::Migration
  ADMIN = User.find_by(id: -1)

  def up
    User.create(name: 'Admin', id: -1, admin: true) unless ADMIN
  end

  def down
    ADMIN.destroy if ADMIN
  end
end
```

## Wrapping up

I hope you will find these tricks useful. If you have any other cool examples, please share them in the comments.


P.S. Cover image is generated using DALL·E 2