GithubHelp home page GithubHelp logo

rubocop / rails-style-guide Goto Github PK

View Code? Open in Web Editor NEW
6.5K 219.0 1.1K 593 KB

A community-driven Ruby on Rails style guide

Home Page: http://rails.rubystyle.guide

ruby rails styleguide rubocop

rails-style-guide's People

Contributors

abotalov avatar alexvpopov avatar andyw8 avatar arbox avatar bbatsov avatar bdewater avatar darkside73 avatar dependabot[bot] avatar drenmi avatar drewda avatar et avatar eugeneius avatar harrykiselev avatar kalkov avatar koic avatar luqipan avatar markevich avatar maurogeorge avatar pirj avatar pointlessone avatar rousisk avatar satour avatar semanticart avatar shpakvel avatar silva96 avatar syndbg avatar tejasbubane avatar toao avatar velobuff avatar ydah avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rails-style-guide's Issues

For people stuck on Rails 3

Would a Rails 3 version of the style guide make sense? Or maybe the style guide could mention which versions of Rails each style guide item applies to.

What's the best way use/render partials?

What's the best way to use/render partials?

render partial: 'partial_name'
render 'partial_name'

And with variables

render 'partial_name', locals: { var: 'something' }
render 'partial_name', var: 'something'

Sorry if it is a stupid question

when to use parentheses

I like the guideline around "Omit parentheses around parameters for methods that are part of an internal DSL". This is pretty straight forward for most of the main app code. What have people found for the following....

testing:

views:

  • link_to
  • form_for / form_tag
  • submit_tag
  • etc.

Order of includes and scopes?

In the case of a model with a default_scope, includes, and other scopes, what should the order be? Includes, then default scope, then scopes below in the 'other macros' section would be my guess. Also, in my opinion, scopes should be first-class citizens in the macro-ordering section, along with attribute macros, validations, etc.

Internationalization keys naming conventions

What names do you give to the keys in the I18n yaml files? For example:

  1. A link to a products index. products_link?
  2. A link that destroys a product? destroy_link? link_to_destroy?
  3. What if the destroy action has a confirm prompt? Do you add 2 keys to the key destroy_link like text (the actual text of the link) and confirm (the text of the confirm prompt)?
  4. Paragraphs in an about section. p1, p2 and so on?

Validation Inconsistancy

You give this as an example:

validates_presence_of :name
validates_format_of :email, :with => /\A[-a-z0-9_+.]+@([-a-z0-9]+.)+[a-z0-9]{2,4}\z/i
validates_length_of :content, :maximum => 500

but then say you shouldn't use those and instead use
validates :name, :presence => true

where().first vs. find_by

What do you think, what should I prefer in Rails: where(field: value).first or find_by_field(value) ?

Never construct raw HTML and use Rails helpers if necessary

I noticed that you construct raw HTML in your example here https://github.com/bbatsov/ruby-style-guide#concat-strings

I think you should use a different example and add a section about constructing HTML. In the section, it should explain that you must construct HTML, use helpers such as content_tag and avoid writing strings of raw HTML. Not only does this save a bit of time for the programmer, but it also doesn't encourage the use of html_safe, which can create an XSS vulnerability if they happen to wrap the html around unclean data.

Original issue rubocop/ruby-style-guide#594, but this issue concerns Rails so moving it here.

Unexpected behavior of query attribute methods

Query attribute methods behave unexpectedly for the majority of developers. Everybody thinks they are just shortcuts for attribute.present?. It the reality the logic is more complex and inconsistent. They return false for zero values if the attribute's table column is numeric and false for values like 0, '0', 'off', etc. if the attribute has no table column.

Since changing the behavior of these methods can break applications, we can either add a deprecation warning into rails or add a cop here.

See issue: rails/rails#28438
Implementation: https://github.com/rails/rails/blob/v4.2.6/activerecord/lib/active_record/attribute_methods/query.rb#L10-L31

And some articles about query attribute methods:
https://rails-bestpractices.com/posts/2010/10/03/use-query-attribute/
http://blog.plataformatec.com.br/2012/05/active-record-attribute-method/

The order of macro-style methods could be problematic for before_destroy callbacks

Due to rails/rails#3458, we may want to consider putting callbacks before associations (or maybe just before_destroy?). Take the following example.

def Person
  has_many :roles, :dependent => :destroy

  before_destroy :check_deletable

  def :check_deletable
    fail "Cannot delete super admin." if super_admin?
  end
end

This won't work as expected. Instead it will destroy a user's roles whenever destroy is called regardless if the user actually gets deleted. The fix is to list the callback first:

def Person
  before_destroy :check_deletable

  has_many :roles, :dependent => :destroy

  def :check_deletable
    fail "Cannot delete super admin." if super_admin?
  end
end

It might be a best practice to just list callbacks before associations--or just before_destroy?

EDIT: Thanks to @smudge (see below), here's a better workaround:

def Person
  has_many :roles, dependent: :destroy

  before_destroy :check_deletable, prepend: true

  def :check_deletable
    fail "Cannot delete super admin." if super_admin?
  end
end

Remove dependent error for has_many through

I'm getting hit with a linting error when I use has_many with the through option.

A trivial example:

has_many :users, dependent: :destroy
has_many :comments, through: :users # wants dependent: :destroy

I don't see a point to adding the dependent option in this case. Am I missing something? If I was using a join table, it might be helpful, but that is not the case.

Priceless Gem: better_errors

  • better_errors - Better Errors replaces
    the standard Rails error page with a much better and more useful error page. It is also
    usable outside of Rails in any Rack app as Rack middleware.

Thoughts on ActiveRecord enum

tldr; Specifying the stored value rather than the ordinal is a much safer approach and I'd like to see it added to this style guide.

I really like the idea of ActiveRecord enums, but feel relying on the ordinal value is quite dangerous.

Reordering or removing an enum value would be catastrophic to a system with a significant amount of data. It's also very unlikely it would be caught by a unit or functional tests which typically start with a clean db state.

I don't want to use the hyperbolic example of someone alphabetizing a list to suddenly reverse permissions for the system:

enum status: [ :enabled, :disabled ] -> enum status: [ :disabled, :enabled ] #FTFY

It's more likely it would result in adding mangos to a system and forcing strawberry lovers to have their favorite fruit misrepresented:

enum favorite_fruit: [ :apples, :bananas, :strawberries ]

to

enum favorite_fruit: [ 
    :apples, 
    :bananas, 
    :mangos, 
    :strawberries 
] #Take that red fruit eaters!

Regardless it's just to easy to want to add mangos to this list alphabetically. Some might even argue it's bad form to have it out of order. Consider an unordered list of 50'ish values.

For a few more characters, the explicit nature of specifying the enum's value forces the developer to acknowledge they are actually mapped to an underlying value.

enum favorite_fruit: { 
   apples: 1, 
   bananas: 2, 
   mangos: 4,
   strawberries: 3
} #Strawberry lovers are people too!

While you still have the option to screw up the value (it even accepts duplicate values), I feel like the chances of an innocent style based reordering or requirement based refactoring are more easily understood at face value.

I'd even like to see a rule for Rubocop to enforce this in my projects, but it could just be like, you know, my opinion man...

Thoughts?

Rails app name convention?

Hey, I'm not sure if this is mentioned anywhere - but is there any naming conventions of a rails app?

For example:

rails new blogapp vs rails new blog_app

Question - why db:schema:load instead of db:setup

The only difference I've seen so far between these rake tasks is that db:setup will run db:create, db:schema:load and db:seed.

Is there a specific reason to sugges db:schema:load on an empty database, instead of db:setup from a dropped database?

Using Steak

Should probably add the Steak gem to the RSpec section and show some real easy examples.

Clarify Configuration Guidance

I think the guidance for configuration could do with some clarification. It is unclear from the guidance how gem initialisation code that is environment-specific should be handled. Obviously in an ideal world, there would be a single initialiser per gem and any differences would be supplied via envs, but in cases where there are more substantive differences, for example a gem should only be initialised in a certain environment or requires a different kind of initialisation, adding some guidance would be helpful.

Gemfile styling

There is a section for bundler, and I think there could be some more explicit rules for the Gemfile, though I don't know what the conventions are or what they should be. Some questions / suggestions below:

Whitespace for version locking, use the minimal amount of whitespace needed:

# bad
'short-gem',                                       '~> 2.0.1'
'really-long-gem-name-that-takes-forever-to-type', '~> 1.0.0'

# good
'short-gem', '~> 2.0.1'
'really-long-gem-name-that-takes-forever-to-type', '~> 1.0.0'

Ordering of gems and groups:

  1. Should the non-grouped gems always be listed first, or last?
  2. Should the gems in the same group be ordered alphabetically, or put related gems together?
  3. Should the groups themselves be alphabetical?

Rails 5 updates

I'm not sure what kind of changes would be necessary for Rails 5, but is there any intention to update this style guide for Rails 5?

|| / or

CarrierWave initializer contains line:

if Rails.env.development? or Rails.env.production?

Use &&/|| for boolean expressions, and/or for control flow.
-- Ruby style guide

I assume, Rails style guide is build on top of Ruby style guide. So lets be consistent and use double pipe operator for booleans.

db:schema:load vs rake db:migrate

Use rake db:schema:load instead of rake db:migrate to initialize an empty database.
https://github.com/bbatsov/rails-style-guide#db-schema-load

Why use one over the other ? Only info I found was going against using db:schema:load as running it on a production server would simply wipe existing database data. (http://stackoverflow.com/a/5905958)

Is it only useful for projects with deleted old migrations ? Is it good practice to delete old migrations you know won't ever be needed again ?

Thanks !

`read_attribute(:attr)` and `self[:attr]` not equivalent

My team is using RuboCop with Rails cops enabled as a first step in cleaning up a legacy codebase. We found some code like this:

  ...
  after_initialize :set_variations

  def set_variations
    if read_attribute(:page_id) && !self.page_id.nil?
      ...

Among many others, we got this warning from RuboCop:

C: Prefer self[:attr] over read_attribute(:attr).
    if read_attribute(:page_id) && !self.page_id.nil?
       ^^^^^^^^^^^^^^

which refers to this recommendation in the style guide.

And our specs blew up. :( We got several of these:

     ActiveModel::MissingAttributeError:
       missing attribute: page_id
     # activerecord-4.0.13/lib/active_record/attribute_methods/read.rb:95:in `block (2 levels) in read_attribute'
     # activerecord-4.0.13/lib/active_record/attribute_methods/read.rb:94:in `fetch'
     # activerecord-4.0.13/lib/active_record/attribute_methods/read.rb:94:in `block in read_attribute'
     # activerecord-4.0.13/lib/active_record/attribute_methods/read.rb:84:in `fetch'
     # activerecord-4.0.13/lib/active_record/attribute_methods/read.rb:84:in `read_attribute'
     # activerecord-4.0.13/lib/active_record/attribute_methods.rb:345:in `[]'

It turns out that ActiveRecord::AttributeMethods::Read#read_attribute and ActiveRecord::AttributeMethods#[] are not exact synonyms: the latter will raise an ActiveModel::MissingAttributeError if the attribute is not found, whereas the former (I think) just returns nil and fails silently.

It's true that our example is especially gnarly code (read_attribute in an after_initialize callback), but I thought I should at least leave a note here that the two methods can't always be swapped safely. Would it be helpful to reword the style recommendation to make that more clear?

Thoughts on the "Flawed gems" section

I think we should get rid of it. Instead of negative recommendations, let's add positive ones.

  • A negative list is hard to mantain. A positive list too, but it's definitely easier to list the stuff one considers good, than everything one considers bad. For example, if there was a debugging section, one would say here "don't use ruby-debug, don't use debugger either" where one can just say "use byebug".
  • A negative list is too subjective. For example, I consider minitest-autotest (autotest successor) to be as good as guard.
  • A negative list doesn't have a good vibe. Let's focus on good stuff. Gems like guard or simplecov only get mentioned in this section, let's encourage their use instead of discouraging the alternatives.

Defaulting boolean attributes to true

I was wondering what the best way is to make an Active Record attribute default to true.

This guide recommends setting defaults in the model, not the migration
https://github.com/bbatsov/rails-style-guide#migrations

But this technique fails when defaulting a boolean to true.

def allow_robots
  self[:allow_robots] or true
end

In the code above, allow_robots will always evaluate to true, even if set to false in Active Record. At the very least, there should be a short disclaimer in the guide about the technique not working in this case. But I'd prefer to arrive at a recommended solution.

This variation of the method will get the desired result.

def allow_robots
  !self[:allow_robots].nil? ? self[:allow_robots] : true 
end

At this point, the code's purpose is much less obvious, and defaulting in the migration is starting to look pretty by comparison.

def change
  add_column :settings, :allow_robots, :boolean, :default => true
end

Any thoughts? I feel like there's probably a better way to default true that I just haven't thought of yet.

Standard for order of class / model constructs

It would be nice to have a recommendation for the order of constructs in a class and especially in a (ActiveRecord) model.

So we know where to find specific constructs.

E.g.:

    include ...
    plugin_calls like translates ...
    associations
        belongs_to
        has_one
        has_many
        has_and_belongs_to_many
        cache associations
        optional / helper associations 
    constants
    scopes
    validations
    state_machine
    attr_accessible
    attr_*
    methods
        public
            self.*
            other 
        protected
        private 

Thoughts about forbidding after_find and after_initialize

In my company I inserted it as our own standard because it creates all sorts of issues:

  1. Performance issues in case of bulk reading. Especially because .includes only loads associations after the after_find done.
  2. Unintuitive - when you read line like Post.last you expect it to always work, run 1 quick query and return the post or nil. Instead it might run many queries or even break if there's exception in after_find.
  3. Extremely hard to debug if there's a bug inside after_find (even more in after_initialize).

Here's one issue that I had lately as example to it:
Rails has default functionality of returning 404 in case of ActiveRecord::RecordNotFound exception.
Someone did SomeThing.find(invalid_id) in after_find.
Result - totally correct page always results in 404 without any reason. And it doesn't get recorded as exception because of this 404 handler.

Other Classes and Modules

The thing I have the most trouble with trying to figure out how to do it "the rails way" is where to put a custom Class or some utility methods that aren't helpers and don't belong in a model or controller.

The best example I can offer off the top of my head is a rather complex rake task that makes sense to implement in its own class or module. I currently place these classes in the util folder, but is that the best location? Furthermore, how should I structure the directories in this folder? Should I use a java like package hierarchy like util/com/foo/bar/MyClass?

In general, what's the recommended practice for code that's not a model, view, controller, or helper?

Suggestion for deprecated RSpec matchers

Hi there! I've been going through your stellar primer here, and it's been a huge help; thank you so much for your work maintaining it. The one thing I had some trouble with was the bit on model tests. This section prescribes the use of RSpec's have(x).errors_on :attribute matcher; and while discouraging the less specific be_valid seems wise indeed, the current betas of RSpec emit deprecation warnings for have. (One maddeningly vague paragraph-long lecture for each time it appears, to be precise.)

The best solution I've come up with to specifically target individual attributes' validations looks like this:

describe User do
  it 'requires a name' do
    expect(build(:user, name: nil).error_on(:name).size).to eq(1)
  end

  it 'requires an e-mail address' do
    expect(build(:user, email: nil).error_on(:email).size).to eq(1)
  end

  # ...etc.
end

This achieves the same granularity (I think), and without targeting anything as brittle as the error string's actual content, but it's something of an ugly duckling. I wasn't confident enough that it was the best solution to make a pull request out of it, but thought I'd toss it out there anyway.

Thanks again, and happy new year!

Models in migrations

Hey, according to "Don't use model classes in migrations", what about using models in migrations as just named interface to table in database (without any callbacks, validations, etc.):

Role = Class.new(ActiveRecord::Base)

class ChangeRoleNames < ActiveRecord::Migration
  def change
    reversible do |dir|
      dir.up do
        Role.find_by(name: 'dog').try(:update, name: 'wolfhound')
        Role.find_by(name: 'admin').try(:update, name: 'god')
      end

      dir.down do
        Role.find_by(name: 'wolfhound').try(:update, name: 'dog')
        Role.find_by(name: 'god').try(:update, name: 'admin')
      end
    end
  end
end

How to avoid "Share no more than two instance variables"

I agree with this but it makes confusing me.
How to reduce variables or some links if I have a blog app like this?

class PostsController < ApplicationController
  def index
    @posts = Post.all.paginate(params[:page])
    @posts_shown_at_sidebar = Post.first(5)
    @recent_comments = Comment.first(5)
    @pages_shown_at_sidebar = Page.all
  end
end

Capybara in View spec?

As far as I know Capybara is for integration specs which stored in ./spec/requests/ directory so a bit confused when seeing this in View spec section:

Prefer the capybara negative selectors over should_not with the positive.

# bad
page.should_not have_selector('input', type: 'submit')
page.should_not have_xpath('tr')

# good
page.should have_no_selector('input', type: 'submit')
page.should have_no_xpath('tr')

Or am I missing something?

incorrect view spec example

The example presented:

describe 'articles/edit.html.haml' do
it 'renders the form for a new article creation' do
  assign(
    :article,
    mock_model(Article).as_new_record.as_null_object
  )
  render
  rendered.should have_selector('form',
    method: 'post',
    action: articles_path
  ) do |form|
    form.should have_selector('input', type: 'submit')
  end
end

is compliant to only webrat, not capybara.
(jnicklas himself explains it here: teamcapybara/capybara#384)
Since you recommend using capybara and there is no indication to the reader that it is a webrat compliant example, it seems to be problematic.

Name custom validation methods with prefix 'validate_'

Unless you include the word 'validate' in your method name for a custom validation method, you pretty much ensure that it has a weird name, or an unexpected side effect.

For example:

# Bad
class User < ActiveRecord::Base
  validates :email, presence: true
  validate :email_is_not_gmail_account
  #...
  def email_is_not_gmail_account
     # This method name implies that (maybe?) it returns true/false,
     # not that it mutates the errors attribute of this model.
  end
end

# Good
class User < ActiveRecord::Base
  validates :email, presence: true
  validate :validate_email_is_not_gmail_account
  #...
  def validate_email_is_not_gmail_account
     # This method name tells you exactly what it does
  end
end

before_destroy with prepend: true in Rails 4.1 is no longer needed

In Rails 4.1 following setup does no longer destroy child records when you call #destroy on parent and in before_destroy callback return false.

class Car < ActiveRecord::Base
  has_many :wheels, dependent: :destroy

  before_destroy do
    false
  end
end

class Wheel < ActiveRecord::Base
  belongs_to :car
end

I don't know where it got fixed but now the destroy process is either in one big transaction or in nested transactions with rollbacks properly propagating up. It can be seen in SQL logs.

Hoewer you still need to use prepend: true if you want to e.g. log destroyed childs in parent, because without it, the dependent: :destroy callback still gets executed before before_destroy callback so within before_destroy callback childs are already destroyed and can't be reached.

I also guess that prepend: true could has an performance advantage because when you destroy parent with it, no destroying of childs(and rollback then) even takes place.

I don't know if the prepend: true should stay in guides or not but the reason for it # bad (roles will be deleted automatically even if super_admin? is true) is no longer valid.

Usage of write_attribute?

The style guide covers read_attribute but not write_attribute.

Prefer self[:attribute] over read_attribute(:attribute).

Is self[:attribute] = value generally preferred to write_attribute(:attribute, value)?

Best way to open config files?

In my config/deploy.rb I have some configuration variables stored in a YAML file.

What's the best way to load this in? Currently I have:

CONFIG = YAML.load(File.open('config/deploy/deploy.yml'))

# >> File.open('config/deploy/deploy.yml')
# => #<File:config/deploy/deploy.yml>

This doesn't load a full path though, so I could do:

CONFIG = YAML.load(File.open(Rails.root.join('config/deploy', 'deploy.yml')))

# >> File.open(Rails.root.join('config/deploy', 'deploy.yml'))
# => #<File:/Users/gareth/Code/repo/config/deploy/deploy.yml>

Is there a "standard" way of doing this? What do you guys do?

Thanks

Revisit Numeric#inquiry rule

There is an example against Numeric#inquiry methods in https://github.com/bbatsov/rails-style-guide#inquiry that goes like:

# bad - Numeric#inquiry
0.positive?
0.negative?

# good
0 > 0
0 < 0

The problem is that the positive? and negative? predicates for Numeric got elevated to the Ruby core feature. It is also a default behaviour for rubocop to use those.

Briefly looking at http://api.rubyonrails.org/classes/Numeric.html doesn't give me a clear replacement for the example so maybe the part about NumericInquiry should be removed

rails best practice for validating user submitted params?

I have often seen rails controllers have dozens of lines of code related to cleaning and validating search params.

For example if a CMS is toggling between draft and published posts:

all_types = ["draft", "published"]

view_param = params[:status].to_s

params[:status] = if view_param.present? && view_param.in?(all_types)
                    view_param
                  else
                    "draft"
                  end

@posts = Post.where("lists.status = ?", params[:status])

Your user wants to be able to toggle back and forth with a url param, and you want to have a default.

Would be great to see a discussion about how people do this consistently with different patterns and multiple developers.

`therubyracer` is never used in production

You have therubyracer listed as a flawed gem because "the use of this gem in production is strongly discouraged as it uses a very large amount of memory." It's my understanding that Rails uses the JS runtime only for minification, meaning it's never used in production. That would be why in the Gemfile of a new Rails project it's included (commented out) in the :assets group. So it seems it really doesn't matter what runtime you choose. Am I mistaken?

Peace,
Fuzzy

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.