-
Don't add Ruby version to Gemfile because it's impossible to say, e.g. use Ruby 2.1 or any higher version. Instead, put preferred ruby version in README.md file.
In case you use
heroku-build-packruby version in Gemfile is required. You can use ENV to temporarily use another version:# Gemfile source "https://rubygems.org" gem "bundler", ">= 1.7.0" ruby ENV["GEMFILE_RUBY"] || "2.2.2" # shell $ export GEMFILE_RUBY=2.1.2; rails s -
Use service objects for decomposing application Try not to use for example observers or filters.
-
Group gems in meaningful groups (not alphabetically).
-
When using an unofficial version of a gem (from a fork or different branch/revision) always include a short comment explaining the reasons. The idea is to know when the declaration can be switched back to using official version. E.g.
# We need feature X that is available only in this branch gem 'activeadmin', github: 'gregbell/active_admin', branch: 'master'
-
Use CoffeeScript instead of plain JavaScript.
-
When making time-based statistic use the midnight of next day as upper limit.
-
Split upload path into subdirectories.
# partition_uid("1234567890") # => "123/456/789/0" def partition_uid(uid, size) uid.gsub(/(.{#{size}})/, "\\1/") end
-
If converting images, optimize them for web. imagemagick options:
-strip +profile "exif" -quality 80. -
If using whenever, set absolute paths.
set :output, File.join("log", "cron.log") job_type :rake, "cd :path && RAILS_ENV=:environment /usr/local/bin/bundle exec rake :task :output"
-
In non-SPA applications render URLs for JavaScript on server side. If you need to add them to custom JavaScript component, just print the links and iterate through them:
$('a').each (index, el) -> carousel.add(index, el)
-
Use I18n kes instead of plain text in views. In order to keep the reusability, the translation strings shouldn’t contain punctuation at their end, because those belong to the very UI.
-
Use attr_accessible instead of attr_protected.
-
Occasionally run
rails_best_practicescommand, and follow the hints. -
For more advanced apps, setup vagrant along with puppet provisioner. The puppet file shoud be kept in
manifests/site.pp. -
If using
strong_parametersgem, turnwhitelist_attributesoff, otherwise leave it enabled. -
Use unicorn server in production.
-
Secure secure token in public projects.
Put it in settings.yml on production and generate once during initial setup, change when needed.
-
Use
rails-timeagogem by default. Don't render "ago" dates on server-side.The reason is they need to be often updated in real-time on browser side. For example 3 minutes after staying on page "1 minute ago" should say "4 minutes ago".
-
Use
bin/setupfile as thoughtbot describes (for example for git hooks). -
Never ever ever use natural keys in your database.
-
Do not add files to
vendor/assets. Find proper gem or create a new one in rails-assets. -
Use InnoDB database format instead of MyISAM
-
Use lograge to improve Rails default logging format.
-
If you want your model to be compatible with ActiveModel,
include ActiveModel::Modelin Rails 4, andinclude ActiveAttr::Modelin Rails 3. If not, useVirtusinstead -
Add
db/schema.rbto.gitignore -
Use single file extension for default cases. Instead of
file.html.slim,file.css.sass,file.js.coffeeusefile.slim,file.sass,file.coffee -
Use
\Aand\zto validate user input instead of^and$
username = "<script>alert(1)</script>\nsheerun"
!!username.match(/^[a-z]+$/) # => true
!!username.match(/\A[a-z]+\z/) # => false
- If you think about using mysql - use postgresql
- If you think about using mongodb - think again, spend more time thinking if you really need mongodb features, if not - use postgres
# config/application.rb
config.generators do |g|
g.helper false
g.stylesheets false
g.javascripts false
g.view_specs false
endclass CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.integer :post_id
t.integer :user_id
# ...
end
add_index :comments, :post_id # <= this
add_index :comments, :user_id # <= and this
end
endgem 'yajl-ruby', require: 'yajl'
gem 'strong_parameters'
gem 'slim-rails'
gem 'sidekiq'
gem 'devise-async'
gem 'decent_exposure'
gem 'schema_plus'
gem 'coffee-rails-source-maps'
gem 'no_more_pending_migrations'
group :test do
gem 'rspec-rails'
# gem 'rspec-fire' # anytime you use mocks or stubs
end
group :assets do
gem 'rails-timeago', '~> 2.0'
end
group :development do
gem 'letter_opener'
gem 'rails_best_practices'
gem 'commands'
endSee also useful gems.
# config/application.rb
config.cache_store = :redis_store, "redis://localhost:6379/0/app_name:#{Rails.env}:cache"# config/initializers/session_store.rb
Rails.application.config.session_store :redis_store, :redis_server => { :namespace => "app_name:#{Rails.env}:session" }# config/environments/production.rb
config.action_dispatch.rack_cache = {
:metastore => "redis://localhost:6379/0/app_name:#{Rails.env}:rack-cache:metastore",
:entitystore => "redis://localhost:6379/0/app_name:#{Rails.env}:rack-cache:entitystore"
}# config/initializers/sidekiq.rb
Sidekiq.configure_server do |config|
config.redis = { :namespace => "app_name:#{Rails.env}:sidekiq" }
end
Sidekiq.configure_client do |config|
config.redis = { :namespace => "app_name:#{Rails.env}:sidekiq" }
end# faye.ru
faye_server = Faye::RackAdapter.new(
:mount => '/faye',
:timeout => 30,
:engine => {
:type => Faye::Redis, # or Faye::PersistentRedis
:namespace => "app_name:#{ENV["RACK_ENV"]}:faye:"
}
)# Do not compress assets
config.assets.compress = false
# Expands the lines which load the assets
config.assets.debug = true
# Enable sources maps
config.sass.debug_info = true
config.sass.line_comments = falseUse raw: true option when using redis as a cache store.
Example:
class MyController
def index
render_cached_json("api:foos", expires_in: 1.hour) do
Foo.all
end
end
def render_cached_json(cache_key, opts = {}, &block)
opts[:expires_in] ||= 1.day
expires_in opts[:expires_in], :public => true
data = Rails.cache.fetch(cache_key, {raw: true}.merge(opts)) do
block.call.to_json
end
render :json => data
end
endThis will store rendered json in redis, plain json, as string, as you should it should always do. Notice where is to_json and raw: true option. And you get HTTP cache headers for free.
According to issue in redis-rails - which don't set expire time properly https://github.com/redis-store/redis-rails/issues/10
Please use https://github.com/roidrage/redis-session-store for storing rails session in redis.
Remember to include
gem "bson_ext"when using mongodb (you know, for speed)
Raven.configure do |config|
config.dsn = 'https://some-dsn-here'
config.tags = { environment: Rails.env }
endAdd sample configuration.yml file to repository and write about it in README. The file should be named using the following convention:
FILE_NAME.EXT -> FILE_NAME.sample.EXT, so database.yml becomes database.sample.yml. This is better than FILE_NAME.EXT.example because it keeps the file extension in place.
Files that should have samples:
config/database.yml- obviousconfig/mongoid.yml- if using mongo...config/application.yml- for lovely catconfig/settings.*.yml,config/settings/*.ymlmadness - rails_config you bustard
RESERVED_SUBDOMAINS = %w(
about abuse account accounts admin admins administrator
administrators anonymous api assets billing billings board calendar
contact copyright e-mail email example feedback forum
hostmaster image images inbox index invite jabber legal
launchpad manage media messages mobile official payment
picture pictures policy portal postmaster press privacy
private profile search sitemap staff stage staging static
stats status support teams username usernames users webmail
webmaster login use jars main data user img css stylesheets
cdn gallery info system www
)
validates :domain, presence: true,
subdomain: { reserved: RESERVED_SUBDOMAINS }Avoid switching from DELETE to GET for signout. It should be considered as a potential security hole.
gem "ssl_routes", :github => "monterail/ssl_routes"instead of
gem "ssl_routes", :github => "sheerun/ssl_routes"Bus factor++
For apps running rails version < 4 set
config.action_controller.perform_caching = trueRails will automagicly add Rack::Cache middleware to the top of stack. This will cause request with cache headers to be cached which can break e.g. authentication when you want to send cache headers but also always require http basic auth. It is also much better to use nginx or varnish as cache in case auth is not an issue. See this pull request for reference.
- When using
localeapp pullalways do that on the same branch (prefereblymaster). - Do not commit translation files in feature branches. If you do you gonna have a baaaad time when merging it to master.
Prevent weird bugs when using roar representers modules with extend.
# config/initializers/roar.rb
# When accidentally `extend`ing `nil` singleton with
# representer module it will be added to every `nil`
# (since it's a singleton).
# Representers override various methods that will cause bugs
# in weird places until next restart of ruby process
class NilClass
def extend(*args)
raise ArgumentError.new("Can't extend nil:NilClass")
end
endOverride sentry log level to distinguish between production and other (e.g. staging) environments
# config/initializers/raven.rb
class Raven::Event
class << self
def from_exception_with_level_override(exc, options = {}, &block)
options[:level] ||= ENV["SENTRY_LOG_LEVEL"]
from_exception_without_level_override(exc, options, &block)
end
alias_method_chain :from_exception, :level_override
end
endENVied ensure presence and type of app's ENV-variables. To make the bootstraping of the app a quick process, it allows to assign default values to them.
'Easily bootstrap' is quite the opposite of 'fail-fast when not all ENV-variables are present', hence it should be explicitly stated when defaults values are allowed:
# Envfile
development_or_test = ->{ ENV.fetch("RACK_ENV", "development").match(/development|test/) }
enable_defaults!(&development_or_test)
variable :HOST, :string, default: 'host.dev'
variable :SENTRY_DSN, :string, default: ''
variable :SSL_ENABLED, :boolean, default: false
variable :API_KEY, :string, default: "api_key"
variable :CONSUMER_KEY, :string, default: "consumer_key"
group :production do
variable :FORCE_SSL, :boolean
endThe config enables the defaults in development or test environments, but still fail-fast in any other stage if any required ENV-variable is not set up.
Without the fallback to development in development_or_test rake tasks won't work.
