Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Rails recommendations to styleguide #131

Merged
merged 2 commits into from
Oct 26, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions STYLEGUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ This is GitHub's Ruby Style Guide, inspired by [RuboCop's guide][rubocop-guide].
1. [Conditional keywords](#conditional-keywords)
2. [Ternary operator](#ternary-operator)
17. [Syntax](#syntax)
18. [Rails](#rails)
1. [content_for](#content_for)
2. [Instance Variables in Views](#instance-variables-in-views)

## Layout

Expand Down Expand Up @@ -893,4 +896,129 @@ result = hash.map { |_, v| v + 1 }

Refactoring is even better. It's worth looking hard at any code that explicitly checks types.

## Rails

### content_for

Limit usage of `content_for` helper. The use of `content_for` is the same as setting an instance variable plus `capture`.

``` erb
<% content_for :foo do %>
Hello
<% end %>
```

Is effectively the same as

``` erb
<% @foo_content = capture do %>
Hello
<% end %>
```

See "Instance Variables in Views" below.

#### Common Anti-patterns

**Using `content_for` within the same template to capture data.**

Instead, just use `capture`.

``` erb
<!-- bad -->
<% content_for :page do %>
Hello
<% end %>
<% if foo? %>
<div class="container">
<%= yield :page %>
</div>
<% else %>
<%= yield :page %>
<% end %>
```

Simply capture and use a local variable since the result is only needed in this template.

``` erb
<!-- good -->
<% page = capture do %>
Hello
<% end %>
<% if foo? %>
<div class="container">
<%= page %>
</div>
<% else %>
<%= page %>
<% end %>
```

**Using `content_for` to pass content to a subtemplate.**

Instead, `render layout:` with a block.

``` erb
<!-- bad -->
<% content_for :page do %>
Hello
<% end %>
<%= render partial: "page" %>
<!-- _page.html.erb -->
<div class="container">
<%= yield :page %>
</div>
```

Pass the content in a block directly to the `render` function.

``` erb
<!-- good -->
<%= render layout: "page" do %>
Hello
<% end %>
<!-- _page.html.erb -->
<div class="container">
<%= yield %>
</div>
```

### Instance Variables in Views

In general, passing data between templates with instance variables is discouraged. This even applies from controllers to templates, not just between partials.

`:locals` can be used to pass data from a controller just like partials.

``` ruby
def show
render "blob/show", locals: {
:repository => current_repository,
:commit => current_commit,
:blob => current_blob
}
end
```

Rails implicit renders are also discouraged.

Always explicitly render templates with a full directory path. This makes template callers easier to trace. You can find all the callers of `"app/view/site/hompage.html.erb"` with a simple project search for `"site/homepage"`.

``` ruby
def homepage
render "site/homepage"
end
```

#### Exceptions

There are some known edge cases where you might be forced to use instance variables. In these cases, its okay to do so.

##### Legacy templates

If you need to call a subview that expects an instance variable be set. If possible consider refactoring the subview to accept a local instead.

##### Layouts

Unfortunately the only way to get data into a layout template is with instance variables. You can't explicitly pass locals to them.

[rubocop-guide]: https://github.com/rubocop-hq/ruby-style-guide