Created
May 21, 2021 07:39
-
-
Save Epigene/fbb5ac90d80f85c78b84034b684a6fc0 to your computer and use it in GitHub Desktop.
Revisions
-
Epigene revised this gist
May 21, 2021 . 1 changed file with 0 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -2,8 +2,6 @@ ## Follow community and company code guidelines, facilitate collaboration ### Adhere to [Ruby Style Guide](https://github.com/rubocop-hq/ruby-style-guide) #### Special mentions agreed to on 2018-07-20 -
Epigene created this gist
May 21, 2021 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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) ```