Skip to content

Instantly share code, notes, and snippets.

@Epigene
Created May 21, 2021 07:39
Show Gist options
  • Save Epigene/fbb5ac90d80f85c78b84034b684a6fc0 to your computer and use it in GitHub Desktop.
Save Epigene/fbb5ac90d80f85c78b84034b684a6fc0 to your computer and use it in GitHub Desktop.

Revisions

  1. Epigene revised this gist May 21, 2021. 1 changed file with 0 additions and 2 deletions.
    2 changes: 0 additions & 2 deletions Code of Code.md
    Original file line number Diff line number Diff line change
    @@ -2,8 +2,6 @@

    ## Follow community and company code guidelines, facilitate collaboration

    [Avoiding common mistakes](https://jetruby.com/expertise/common-rails-mistakes-ruby-way)

    ### Adhere to [Ruby Style Guide](https://github.com/rubocop-hq/ruby-style-guide)

    #### Special mentions agreed to on 2018-07-20
  2. Epigene created this gist May 21, 2021.
    551 changes: 551 additions & 0 deletions Code of Code.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,551 @@
    # Dev team standarts

    ## Follow community and company code guidelines, facilitate collaboration

    [Avoiding common mistakes](https://jetruby.com/expertise/common-rails-mistakes-ruby-way)

    ### Adhere to [Ruby Style Guide](https://github.com/rubocop-hq/ruby-style-guide)

    #### Special mentions agreed to on 2018-07-20
    __1. Use no spaces around curly braces around `Hash` parentheses__
    ```rb
    # good
    {one: 1, two: 2}
    ```

    __2. Use 'bad' example of spaces around interpolables__
    ```rb
    # bad - Using this
    "From: #{ user.first_name }, #{ user.last_name }"

    # good, not using this
    "From: #{user.first_name}, #{user.last_name}"
    ```

    __3. Indent conditional by one when assigning it ot a variable.__
    ```rb
    # bad
    kind = case year
    when 1930..1939 then 'Swing'
    when 1940..1950 then 'Bebop'
    else 'Jazz'
    end

    # good (and a bit more width efficient)
    kind =
    case year
    when 1930..1939 then 'Swing'
    when 1940..1950 then 'Bebop'
    else 'Jazz'
    end

    result =
    if some_cond
    calc_something
    else
    calc_something_else
    end
    ```

    __4. Use the 'leading dot' multi-line chaining style__ arguments for [here](https://github.com/testdouble/standard/issues/75)
    ```rb
    # bad
    one.two.three.
    four

    # good
    one.two.three
    .four
    ```

    __5. Align the parameters of a method call if they span more than one line.__
    ```rb
    # bad
    Mailer.deliver(to: '[email protected]',
    from: '[email protected]',
    subject: 'Important message',
    body: source.text)

    # good
    Mailer.deliver(
    to: '[email protected]',
    from: '[email protected]',
    subject: 'Important message',
    body: source.text
    )
    ```

    __6. Use percent syntax for Regexps__
    ```rb
    # bad (requires escaping of forwardslashes, a common symbol in web-dev)
    my_regexp = /\Ahttps?:\/\/(.*)\z/

    # good
    my_regexp = %r'\Ahttps?://(.*)\z'
    ```

    ```rb
    # too pro
    my_regexp = %r'\A
    https? # scheme
    :// # delimiter
    (?<host_and_path>.*) # host and path
    \z'x

    # then
    "http://example.com".match(my_regexp)[:host_and_path]
    #=> "example.com"
    ```

    __7. Prefer keyword args to positional args__
    ```rb
    # so 90s
    def my_method(user, options={})
    end

    # yay, 2010s
    def my_method(user:, options: {})
    end
    ```

    __8. Use double quoted strings__
    ```rb
    # bad because needs change if suddenly interpolation needed
    name = 'Bozhidar'
    interpolation = "I like #{fave}."
    ```

    ```rb
    # good
    name = "Bozhidar"
    interpolation = "I like #{fave}."

    # can use single if string will contain double quotes
    sarcasm = 'I "like" it.'
    ```

    __9. Do not use parentheses if there are no arguments__
    ```rb
    # bad
    def method_name()
    end

    method_name()

    # good
    def method_name
    end

    method_name

    # or rarely
    super()
    ```

    __10. When in doubt, use parentheses, especially if there are arguments__
    ```rb
    # bad
    def method_name argument1, argument2

    end

    method_name argument1, argument2

    # good
    def method_name(argument1, argument2)
    end

    method_name(argument1, argument2)
    ```

    __11. Use the 'bad' example of no spaces around = operator in method definitions__
    ```rb
    # bad, but use this because it has superior readability.
    def some_method(arg1=:default, arg2=nil, arg3=[])
    end

    # good, do not use this
    def some_method(arg1 = :default, arg2 = nil, arg3 = [])
    end
    ```

    __12. Use the 'bad' example and indent `protected` and `private` methods by one__
    ```rb
    # use the 'bad' example, indenting Protected and Private methods because, again, it's more readable. It's also "Rails" style.
    class Person
    def public_method
    end

    protected

    def protected_method
    end

    private

    def private_method
    end
    end
    ```

    __13. Use `return` only as flow control__
    ```rb
    # bad, explicit, but verbose
    def return_hash
    hash = {
    test: true
    }

    # binding.pry here, just before return line allows easy debugging.

    return hash
    end

    # good, uses implicit return of the last evaluated expression, terse
    # debugging will require effort
    def return_hash
    {test: true}
    end
    ```

    __14. Use return to terminate method execution quickly (i.e. use guard clauses)__
    ```rb
    # bad
    def expensive_processing
    if should_not_preform_the_expensive_processing_on_this_record?
    nil
    else
    expensive_processing_result
    end
    end

    # good
    def expensive_processing
    return nil if should_not_preform_the_expensive_processing_on_this_record?

    expensive_processing_result
    end
    ```

    __15. Return `nil` when you only care about side-effects__

    ```rb
    # bad
    def sideffects_only
    change_db_record1
    change_db_record2
    update_cache

    true
    end

    # good
    def sideffects_only
    change_db_record1
    change_db_record2
    update_cache

    nil
    end
    ```

    __16. Avoid `self`__
    ```rb
    # bad, Sometimes will just not work
    def set_field_value(value)
    field = value
    end

    # bad, will work, but is ugly
    def set_field_value(value)
    self.field = value
    end

    # good, avoids self and gotchas entirely
    def set_field_value(value)
    assign_attributes(field: value)
    end
    ```

    __17. Seperate guard cluases and return values from method body with newlines__
    ```rb
    # bad, pancake
    def my_method
    return true if Config[:somekey]
    prepare_private_admin
    admin = Admin.somescope.last
    admin.auth_code
    end

    # good, newline after guard cluse and before return value
    def my_method
    return true if Config[:somekey]

    prepare_private_admin
    admin = Admin.somescope.last

    admin.auth_code
    end
    ```

    ### Adhere to [Rails Style Guide](https://github.com/bbatsov/rails-style-guide)
    #### Special mentions agreed to on 2018-07-20

    __1. Prefer `if` to `unless` in combination with appropriate predicate__
    ```rb
    # bad
    unless user.present?
    end

    # good
    if user.blank?
    end
    ```

    __2. Split long scopes__
    ```rb
    # bad
    scope :registered, -> { long.scope.code }

    # good
    scope :registered, -> {
    long.scope.code
    }
    ```

    __3. Use named bind variables in SQL snippets__
    ```rb
    # ok
    where("created_at < ?", 1.year.ago)

    # bad
    where("created_at < ? AND updated_at <= ?", 1.year.ago, 1.day.ago)

    # good
    where(
    "created_at < :wayback AND updated_at <= :recently",
    wayback: 1.year.ago, recently: 1.day.ago
    )
    ```

    __4. Use simple service instance memoization__
    ```rb
    class SomeService
    attr_reader :user

    def initialize(user:)
    @user = user
    end

    # == bad ==
    def call
    # some code that will get memoized
    end

    memoize_method :call
    # =========

    # == good ==
    def call
    return @result if defined?(@result)

    intermediate = User.registered.find_by(name: "Bob")

    @result = intermediate&.last_name
    end
    # ===========
    end
    ```


    __5. Prefer `exists?` to `any?` on query object presence__
    ```rb
    # BAD, will instantiate all records
    user.applications.late.any?

    # GOOD, performs a blazing fast SQL query only
    user.applications.late.exists?
    ```

    __6. Do not use instance variables and always use explicit and full render calls__
    In controllers
    ```rb
    # bad
    def index
    @registration_containers
    end

    # good
    def index
    registration_containers = ::Registration::Container.all

    render template: "admin/registration/containers/index", {registration_containers: registration_containers}
    end
    ```

    In views
    ```rb
    # bad
    = render 'admin/_partials/registration/containers/form'

    # good
    = render partial: 'admin/_partials/registration/containers/form', locals: {local_variable: value}
    ```

    __7. Write working database migrations__

    ```rb
    # bad, index: true just does not work!
    add_column(:user, :favorite_number, :integer, null: false, default: 0, index: true) unless column_exists?(:user, favorite_number)

    # good
    add_column(:user, :favorite_number, :integer, null: false, default: 0) unless column_exists?(:user, favorite_number)
    add_index(:user, :favorite_number) unless index_exists?(:user, :favorite_number)
    ```

    __8. Use a consistent tool for HTTP requests__
    `RestClient` seems good.

    __9. Prefer `private` to `protected`__
    ```rb
    # bad
    protected

    def some_modularized_internal_method
    end

    # good
    private

    def some_modularized_internal_method
    end
    ```

    __10. Always specify a gem's version in Gemfile, prefer [pessimistic locking](https://robots.thoughtbot.com/rubys-pessimistic-operator)__

    ```rb
    # specify a locked version of ruby for app to use, try not to change once deployment has happened
    ruby "2.1.2"

    # hard version for key gems that should not change ever
    gem 'rails', '4.1.4'

    # a safe way to allow gems to get patch-level bugfixes without risk of API changes
    gem 'rails-assets-flag-icon-css', "~> 2.8.0" # will grow to 2.8.999, but never 2.9

    # a risky way of specifying 'any version after N will do'.
    # Bundler is smart enough to throttle super advanced versions if other gems (like Rails) depend on an older version
    gem 'uglifier', '>= 1.3.0'

    # if a gem comes from git, specify a commit ref or tag to use
    gem 'rails-i18n', github: 'svenfuchs/rails-i18n', branch: 'master', ref: '6fe4bfa8f6f3' # For 4.x
    ```

    __11. Sort methods by scope and then alphabetically__

    __Known scopes:__
    1. Class method, aka. singleton method, usually defined with `def self.method_name`,
    2. Instance method, usually defined simply with `def method_name`,
    3. Pure getter, a method with zero side-effects and state changes, only a return value
    4. Setter, a method that changes state of at least one object. May have relevant return value (since Ruby has no void)
    5. Private-only callback, a method, usually an instance setter, that is defined as private/protected, has at least one side-effect and is called by Rails model callbacks.
    6. Class private/protected methods
    7. Instance private/protected methods

    __Sort methods thusly:__
    1. Class methods, pure getters, alphabetically
    2. Class methods, setters, alphabetically
    3. Instance methods, pure getters, alphabetically
    4. Instance methods, setters, alphabetically
    5. Class private/protected methods
    6. Private-only callbacks, alphabetically
    7. Instance private/protected methods

    ## Adhere to [RSpec guidelines](http://www.betterspecs.org/)

    __1. Use an explicit, named `subject` for each method's `describe` block__
    ```rb
    describe "#befriend_people(*people)" do
    # bad
    let(:user) { create(:user, :with_friends, friend_count: 100) }

    let(:args) { ["Star1", "Star2"] }

    it "returns the number of friends" do
    expect(user.befriend_people(*args)).to eq(true)
    end

    # good
    subject(:befriending) { user.befriend_people(*args) }

    let(:user) { create(:user, :with_friends, friend_count: 100) }

    let(:args) { ["Star1", "Star2"] }

    it { is_expected.to eq(true) }
    end
    ```

    __2. Keep a consistent expectation format__
    2.1 Use spaces around subject block

    ```rb
    # bad
    subject(:eating_pizza){ user.eat_pizza }

    # good
    subject(:eating_pizza) { user.eat_pizza }
    ```

    2.2 Do not put a space before `expect` and `change` blocks, shorter.
    ```rb
    # bad
    it "makes changes" do
    expect { eating_pizza }.to change { user.pizzas_eaten.size }.by(1)
    end

    # good
    it "makes changes" do
    expect{ eating_pizza }.to change{ user.pizzas_eaten.size }.by(1)
    end

    # good multiline with `and`
    it "makes many changes and returns a value" do
    expect{ eating_pizza }.to(
    change{ user.pizzas_eaten.size }.by(1).
    and change{ Pizza.uneaten.size }.by(-1)
    )

    expect(eating_pizza).to be_a(Pizza)
    end
    ```

    __3. Prefer a single factory with traits to many factories__

    ```rb
    # bad
    FactoryBot.define do
    factory :agreement do
    field { value }

    factory :agreement_expired do
    field { "expired" }
    end
    end
    end

    create(:agreement_expired)

    # good
    FactoryBot.define do
    factory :agreement do
    field { value }

    trait :expired do
    field { "expired" }
    end
    end
    end

    create(:agreement, :expired)
    ```