Ruby on Rails: Coding Guideline Md. Masud Rana
Agenda ● Ruby, Variable naming(type definition their placement in the code) ● Ruby, Formatting (Carriage return, space, indentation etc) ● Rails, Configuration management ● Rails, Routing ● Rails, Controllers ● Rails, Models ● Rails, Migrations ● Rails, Views ● Rails, Logging ● Rails, Userful Gems
Ruby, Variable and naming conventions 1. Use snake_case for variable and method names declartion. 2. Use ```CamelCase`` for classes and modules declaration. 3. User all upcase SCREAMING_SNAKE_CASE for other constants declaration. 4. Variable name should be Noun. Example- day, month, year, subjects etc. 5. Method name should be Verb. Example- calculate_cgpa, login etc.
Code formatting Don't use ; to separate statements and expressions. <!-- bad --> puts 'foobar'; # superfluous semicolon puts 'foo'; puts 'bar' # two expressions on the same line <!-- good --> puts 'foo' puts 'foobar' Avoid using single-line methods. <!-- bad --> def too_much; something; something_else; end <!-- good --> def some_method body end
Code formatting cont.. Use spaces around operators, after commas, colons and semicolons, around { and before } for better readability. <!-- bad --> sum= 1+2 a,b= 1,2 [1, 2,3].each{|e| puts e} <!-- good --> sum = 1 + 2 a, b = 1, 2 [1, 2, 3].each { |e| puts e } class FooError < StandardError; end
Code formatting cont.. Align the parameters of a method call if they span more than one line. When aligning parameters is not appropriate due to line-length constraints, single indent for the lines after the first is also acceptable. <!-- starting point (line is too long) --> def send_mail(content) Mailer.deliver(to: 'bob@example.com', from: 'us@example.com', subject: 'Important message', body: content.text) end <-- good (normal indent) --> def send_mail(source) Mailer.deliver( to: 'bob@example.com', from: 'us@example.com', subject: 'Important message', body: source.text ) end
Code formatting cont.. if vs unless while vs until each vs for
Rails, Configuration management 1. Put custom initialization code in config/initializers. The code in initializers executes on application startup/boot. 2. Keep initialization code for each gem in individual file followed by gem name. For example- carrierwave.rb, active_admin.rb, etc. 3. Environment specific configuration should goes under each environment ex - development, test and production etc. files under app/config/environments/... 4. Mark additional assets for precompilation (if any) in config/environments/production.rb and precompile additional assets (ex- application.js, application.css those are already added). config.assets.precompile += %w( rails_admin/rails_admin.css rails_admin/rails_admin.js ) 5. Keep configuration that's applicable to all environments in the config/application.rb file. 6. Create an additional staging environment that closely resembles the production one except the database.
Rails, Routing When you need to add more actions to a RESTful resource (do you really need them at all?) use member and collection routes. <!-- bad --> get 'subscriptions/:id/unsubscribe' resources :subscriptions <!-- good --> resources :subscriptions do get 'unsubscribe', on: :member end <!-- bad --> get 'photos/search' resources :photos <!-- good --> resources :photos do get 'search', on: :collection end
Rails, Routing cont.. Use nested routes to express better the relationship between ActiveRecord models. <!-- Model post.rb --> class Post < ActiveRecord::Base has_many :comments end <!-- Model comment.rb --> class Comments < ActiveRecord::Base belongs_to :post end <!-- Route routes.rb --> resources :posts do resources :comments end
Rails, Routing cont.. Use namespaced routes to group related actions. namespace :admin do # Directs /admin/products/* to Admin::ProductsController # (app/controllers/admin/products_controller.rb) resources :products end Never use legacy wild controller route. This route will make all actions in every controller accessible via GET requests. <!-- very bad --> match ':controller(/:action(/:id(.:format)))'
Rails, Controllers 1. Keep the controllers skinny - they should only retrieve data for the view layer and shouldn't contain any business logic (all the business logic should naturally reside in the model). 2. Each controller action should (ideally) invoke only one method other than an initial find or new. 3. Share no more than two instance variables between a controller and a view.
Rails, Models 1. Introduce non-ActiveRecord model classes freely. 2. Name the models with meaningful (but short) names without abbreviations. 3. If you need model objects that support ActiveRecord behavior(like validation) use the ActiveAttr gem. class Message include ActiveAttr::Model attribute :name attribute :email attribute :content attribute :priority attr_accessible :name, :email, :content validates :name, presence: true validates :email, format: { with: /A[-a-z0-9_+.]+@([-a-z0-9]+.)+[a-z0-9]{2,4}z/i } validates :content, length: { maximum: 500 } end
Rails, Migrations 1. Keep the schema.rb (or structure.sql) under version control. 2. Use rake db:schema:load instead of rake db:migrate to initialize an empty database. 3. Never edit an existing migration file for changing existing column. Always create a new migration to update the DB schema. 4. Enforce foreign-key constraints. As of Rails 4.2, ActiveRecord supports foreign key constraints natively. 5. When writing constructive migrations (adding tables or columns), use the change method instead of up and down methods. # the old way class AddNameToPeople < ActiveRecord::Migration def up add_column :people, :name, :string end def down remove_column :people, :name end end # the new prefered way class AddNameToPeople < ActiveRecord::Migration def change add_column :people, :name, :string end end
Rails, View 1. Never call the model layer directly inside a view file. <!-- bad (users/index.html.erb) --> <table> <tr> <td>ID</td> <td>Name</td> <td>Email</td> </tr> <tr> <% User.all.each() do |user| %> <td><%= user.id %></td> <td><%= user.name %></td> <td><%= user.email %></td> <% end %> </tr> </table> <!-- good and preferred way --> <!-- users_controller.erb --> def index @users = User.all.paginate(:page => params[:page]).order_by_name() end <!-- users/index.html.erb --> <table> <tr> <td>ID</td> <td>Name</td> <td>Email</td> </tr> <tr> <% @users.each() do |user| %> <td><%= user.id %></td> <td><%= user.name %></td> <td><%= user.email %></td> <% end %> </tr> </table> <%= will_paginate @users %> 2. Never make complex formatting in the views, export the formatting to a method in the view helper or the model. 3. Mitigate code duplication by using partial templates and layouts.
Rails, Logging 1. By default, each log is created under Rails.root/log/.. and the log file is named after the environment in which the application is running. 2. The default Rails log level is :info in production mode and :debug in development and test mode. 3. Available log levels are: :debug, :info, :warn, :error, :fatal, and :unknown corresponding to the log level numbers from 0 up to 5, respectively. 4. To change the default log level, use config.log_level = :warn in any environment initializer or Rails.logger.level = 0 at any time.
Rails, Useful gems Code Quality ● rubocop is a tool for analyzing and formatting Ruby static code. ● overcommit is a gem for configuring Git hooks. It is excellent for keeping the code quality high. It allows tuning git hooks for linters launch before every commit. Testing ● RSpec suggests Behaviour Driven Development for Ruby. ● capybara is an acceptance test framework for web applications. ● simplecov is one of the useful gems for testing. It shows the percentage of code covered with unit-tests. ● faker is a library for generating demo data such as names, addresses, and phone numbers. Authentication & Authorization ● devise, Is a flexible authentication solution for Rails based on Warden. ● ruby-jwt, gem is a simple Ruby implementation of the RFC 7519 OAuth JSON Web Token (JWT) standard. ● cancancan, Is a super-convenient authorization gem. ● pundit, Object oriented authorization for Rails applications
Rails, Useful gems cont.. Many More ● sidekiq, Is a simple and efficient background processing tool for Ruby. ● friendly_id, Provides great assistance when working with permalink plugins for ActiveRecord. ● dotenv-rails, Is a Ruby gem to load environment variables from a dotfile which can be outside the main project folder. This way, Dotenv allows safely - storing the app configuration data. ● slim, Is a template language attempting to reduce the syntax to the essential code elements without becoming cryptic. ● impressionist, Allows counting page views. It's purpose is to give customizable stats, making it instantly available in the application, in contrast to Google Analytics and pulling data with their API. ● brakeman, Brakeman detects security vulnerabilities in Ruby on Rails applications via static analysis. ● whenever, Clean ruby syntax for writing and deploying cron jobs. ● bullet, Help to kill N+1 queries and unused eager loading
Thank you!

Ruby on Rails: Coding Guideline

  • 1.
    Ruby on Rails:Coding Guideline Md. Masud Rana
  • 2.
    Agenda ● Ruby, Variablenaming(type definition their placement in the code) ● Ruby, Formatting (Carriage return, space, indentation etc) ● Rails, Configuration management ● Rails, Routing ● Rails, Controllers ● Rails, Models ● Rails, Migrations ● Rails, Views ● Rails, Logging ● Rails, Userful Gems
  • 3.
    Ruby, Variable andnaming conventions 1. Use snake_case for variable and method names declartion. 2. Use ```CamelCase`` for classes and modules declaration. 3. User all upcase SCREAMING_SNAKE_CASE for other constants declaration. 4. Variable name should be Noun. Example- day, month, year, subjects etc. 5. Method name should be Verb. Example- calculate_cgpa, login etc.
  • 4.
    Code formatting Don't use; to separate statements and expressions. <!-- bad --> puts 'foobar'; # superfluous semicolon puts 'foo'; puts 'bar' # two expressions on the same line <!-- good --> puts 'foo' puts 'foobar' Avoid using single-line methods. <!-- bad --> def too_much; something; something_else; end <!-- good --> def some_method body end
  • 5.
    Code formatting cont.. Usespaces around operators, after commas, colons and semicolons, around { and before } for better readability. <!-- bad --> sum= 1+2 a,b= 1,2 [1, 2,3].each{|e| puts e} <!-- good --> sum = 1 + 2 a, b = 1, 2 [1, 2, 3].each { |e| puts e } class FooError < StandardError; end
  • 6.
    Code formatting cont.. Alignthe parameters of a method call if they span more than one line. When aligning parameters is not appropriate due to line-length constraints, single indent for the lines after the first is also acceptable. <!-- starting point (line is too long) --> def send_mail(content) Mailer.deliver(to: 'bob@example.com', from: 'us@example.com', subject: 'Important message', body: content.text) end <-- good (normal indent) --> def send_mail(source) Mailer.deliver( to: 'bob@example.com', from: 'us@example.com', subject: 'Important message', body: source.text ) end
  • 7.
    Code formatting cont.. ifvs unless while vs until each vs for
  • 8.
    Rails, Configuration management 1.Put custom initialization code in config/initializers. The code in initializers executes on application startup/boot. 2. Keep initialization code for each gem in individual file followed by gem name. For example- carrierwave.rb, active_admin.rb, etc. 3. Environment specific configuration should goes under each environment ex - development, test and production etc. files under app/config/environments/... 4. Mark additional assets for precompilation (if any) in config/environments/production.rb and precompile additional assets (ex- application.js, application.css those are already added). config.assets.precompile += %w( rails_admin/rails_admin.css rails_admin/rails_admin.js ) 5. Keep configuration that's applicable to all environments in the config/application.rb file. 6. Create an additional staging environment that closely resembles the production one except the database.
  • 9.
    Rails, Routing When youneed to add more actions to a RESTful resource (do you really need them at all?) use member and collection routes. <!-- bad --> get 'subscriptions/:id/unsubscribe' resources :subscriptions <!-- good --> resources :subscriptions do get 'unsubscribe', on: :member end <!-- bad --> get 'photos/search' resources :photos <!-- good --> resources :photos do get 'search', on: :collection end
  • 10.
    Rails, Routing cont.. Usenested routes to express better the relationship between ActiveRecord models. <!-- Model post.rb --> class Post < ActiveRecord::Base has_many :comments end <!-- Model comment.rb --> class Comments < ActiveRecord::Base belongs_to :post end <!-- Route routes.rb --> resources :posts do resources :comments end
  • 11.
    Rails, Routing cont.. Usenamespaced routes to group related actions. namespace :admin do # Directs /admin/products/* to Admin::ProductsController # (app/controllers/admin/products_controller.rb) resources :products end Never use legacy wild controller route. This route will make all actions in every controller accessible via GET requests. <!-- very bad --> match ':controller(/:action(/:id(.:format)))'
  • 12.
    Rails, Controllers 1. Keepthe controllers skinny - they should only retrieve data for the view layer and shouldn't contain any business logic (all the business logic should naturally reside in the model). 2. Each controller action should (ideally) invoke only one method other than an initial find or new. 3. Share no more than two instance variables between a controller and a view.
  • 13.
    Rails, Models 1. Introducenon-ActiveRecord model classes freely. 2. Name the models with meaningful (but short) names without abbreviations. 3. If you need model objects that support ActiveRecord behavior(like validation) use the ActiveAttr gem. class Message include ActiveAttr::Model attribute :name attribute :email attribute :content attribute :priority attr_accessible :name, :email, :content validates :name, presence: true validates :email, format: { with: /A[-a-z0-9_+.]+@([-a-z0-9]+.)+[a-z0-9]{2,4}z/i } validates :content, length: { maximum: 500 } end
  • 14.
    Rails, Migrations 1. Keepthe schema.rb (or structure.sql) under version control. 2. Use rake db:schema:load instead of rake db:migrate to initialize an empty database. 3. Never edit an existing migration file for changing existing column. Always create a new migration to update the DB schema. 4. Enforce foreign-key constraints. As of Rails 4.2, ActiveRecord supports foreign key constraints natively. 5. When writing constructive migrations (adding tables or columns), use the change method instead of up and down methods. # the old way class AddNameToPeople < ActiveRecord::Migration def up add_column :people, :name, :string end def down remove_column :people, :name end end # the new prefered way class AddNameToPeople < ActiveRecord::Migration def change add_column :people, :name, :string end end
  • 15.
    Rails, View 1. Nevercall the model layer directly inside a view file. <!-- bad (users/index.html.erb) --> <table> <tr> <td>ID</td> <td>Name</td> <td>Email</td> </tr> <tr> <% User.all.each() do |user| %> <td><%= user.id %></td> <td><%= user.name %></td> <td><%= user.email %></td> <% end %> </tr> </table> <!-- good and preferred way --> <!-- users_controller.erb --> def index @users = User.all.paginate(:page => params[:page]).order_by_name() end <!-- users/index.html.erb --> <table> <tr> <td>ID</td> <td>Name</td> <td>Email</td> </tr> <tr> <% @users.each() do |user| %> <td><%= user.id %></td> <td><%= user.name %></td> <td><%= user.email %></td> <% end %> </tr> </table> <%= will_paginate @users %> 2. Never make complex formatting in the views, export the formatting to a method in the view helper or the model. 3. Mitigate code duplication by using partial templates and layouts.
  • 16.
    Rails, Logging 1. Bydefault, each log is created under Rails.root/log/.. and the log file is named after the environment in which the application is running. 2. The default Rails log level is :info in production mode and :debug in development and test mode. 3. Available log levels are: :debug, :info, :warn, :error, :fatal, and :unknown corresponding to the log level numbers from 0 up to 5, respectively. 4. To change the default log level, use config.log_level = :warn in any environment initializer or Rails.logger.level = 0 at any time.
  • 17.
    Rails, Useful gems CodeQuality ● rubocop is a tool for analyzing and formatting Ruby static code. ● overcommit is a gem for configuring Git hooks. It is excellent for keeping the code quality high. It allows tuning git hooks for linters launch before every commit. Testing ● RSpec suggests Behaviour Driven Development for Ruby. ● capybara is an acceptance test framework for web applications. ● simplecov is one of the useful gems for testing. It shows the percentage of code covered with unit-tests. ● faker is a library for generating demo data such as names, addresses, and phone numbers. Authentication & Authorization ● devise, Is a flexible authentication solution for Rails based on Warden. ● ruby-jwt, gem is a simple Ruby implementation of the RFC 7519 OAuth JSON Web Token (JWT) standard. ● cancancan, Is a super-convenient authorization gem. ● pundit, Object oriented authorization for Rails applications
  • 18.
    Rails, Useful gemscont.. Many More ● sidekiq, Is a simple and efficient background processing tool for Ruby. ● friendly_id, Provides great assistance when working with permalink plugins for ActiveRecord. ● dotenv-rails, Is a Ruby gem to load environment variables from a dotfile which can be outside the main project folder. This way, Dotenv allows safely - storing the app configuration data. ● slim, Is a template language attempting to reduce the syntax to the essential code elements without becoming cryptic. ● impressionist, Allows counting page views. It's purpose is to give customizable stats, making it instantly available in the application, in contrast to Google Analytics and pulling data with their API. ● brakeman, Brakeman detects security vulnerabilities in Ruby on Rails applications via static analysis. ● whenever, Clean ruby syntax for writing and deploying cron jobs. ● bullet, Help to kill N+1 queries and unused eager loading
  • 19.