Default Rails Application Demystified

rails-flow-mapping

Intro

When you run ‘rails new helloworld’ it creates 41 folder, 41 files and 10 gem dependencies! Below is a default Rails 4.1.6 project contents presented (comments edited for brevity) and demystified.

Simplified Flow

bin/rails > boot.rb > application.rb > environment.rb > server started

Ruby Files

Ignoring backtrace_silencers.rb, inflections.rb and mime_types.rb (empty).

/config/application.rb

Runs boot and loads all gem files. Called by the rails executable.

require File.expand_path(‘../boot’, __FILE__)
require ‘rails/all’
Bundler.require(*Rails.groups)
module HelloWorld class Application < Rails::Application end
end

/config/boot.rb

Sets environment variable to the location of the Gemfile then calls Bundler setup, which uses ENV to find the dependencies.

ENV[‘BUNDLE_GEMFILE’] ||= File.expand_path(‘../../Gemfile’, __FILE__)
require ‘bundler/setup’ if File.exist?(ENV[‘BUNDLE_GEMFILE’])

/config/environment.rb

Generic config file defining the application. Runs all initialiser classes.

require File.expand_path(‘../application’, __FILE__)
Rails.application.initialize!

/config/routes.rb

Map requests to controllers.

Rails.application.routes.draw do
end

/config/environments/development.rb

Overriding /config/application.rb settings when run in development mode.

Rails.application.configure do
# Changes don’t require a server restart
config.cache_classes = false
# Do not eager load code on boot.
config.eager_load = false
# Show full error reports and disable caching. 
config.consider_all_requests_local = true 
config.action_controller.perform_caching = false
# Don’t care if the mailer can’t send. 
config.action_mailer.raise_delivery_errors = false
# Print deprecation notices to the Rails logger. 
config.active_support.deprecation = :log
# Raise an error on page load if there are pending migrations. 
config.active_record.migration_error = :page_load
# Debug mode disables concatenation and preprocessing of assets. 
config.assets.debug = true
# Adds additional error checking when serving assets at runtime. 
config.assets.raise_runtime_errors = true
# Raises error for missing translations # 
config.action_view.raise_on_missing_translations = true
end

/config/environments/production.rb

Overriding /config/application.rb settings when run in production mode.

Rails.application.configure do
# Code is not reloaded between
requests. config.cache_classes = true
# Load most of Rails and app object in memory for performance 
config.eager_load = true
# Full error reports are disabled and caching is turned on. 
config.consider_all_requests_local = false 
config.action_controller.perform_caching = true
# Disable Rails’s static asset server (Apache or nginx will already do this). 
config.serve_static_assets = false
# Compress JavaScripts and CSS. 
config.assets.js_compressor = :uglifier
# Do not fallback to assets pipeline if a precompiled asset is missed. 
config.assets.compile = false
# Generate digests for assets URLs. 
config.assets.digest = true
# Log level not debug to keep file size down. 
config.log_level = :info
# Send deprecation notices to registered listeners. 
config.active_support.deprecation = :notify
# Use default logging formatter so that PID and timestamp are not suppressed. 
config.log_formatter = ::Logger::Formatter.new
# Do not dump schema after migrations. 
config.active_record.dump_schema_after_migration = false
end

/config/environments/test.rb

Overriding /config/application.rb settings when running test suites.

Rails.application.configure do 
# Code is not reloaded between requests. 
config.cache_classes = true
# Do not eager load code on boot. 
config.eager_load = false
# Configure static asset server for tests with Cache-Control for performance. 
config.serve_static_assets = true 
config.static_cache_control = ‘public, max-age=3600'
# Show full error reports and disable caching.
config.consider_all_requests_local = true 
config.action_controller.perform_caching = false
# Raise exceptions instead of rendering exception templates. 
config.action_dispatch.show_exceptions = false
# Disable request forgery protection in test environment. 
config.action_controller.allow_forgery_protection = false
# Tell Action Mailer not to deliver emails to the real world. 
config.action_mailer.delivery_method = :test
# Print deprecation notices to the stderr.
config.active_support.deprecation = :stderr
end

/config/initializers/assets.rb

# Version of your assets, change this if you want to expire all your assets.
Rails.application.config.assets.version = ‘1.0'

/config/initializers/cookies_serializer.rb

# Serialise cookies in JSON.
Rails.application.config.action_dispatch.cookies_serializer = :json

/config/initializers/filter_parameter_logging.rb

# Configure sensitive parameters which will be filtered from the log file.
Rails.application.config.filter_parameters += [:password]

/config/initializers/session_store.rb

# Cookie and session name based of app name.
Rails.application.config.session_store :cookie_store, key: ‘_hello-world_session’

/config/initializers/wrap_parameters.rb

Wraps the parameters hash into a nested hash allowing POST requests without having to specify any root elements.

# Enable parameter wrapping for JSON.
ActiveSupport.on_load(:action_controller) do wrap_parameters format: [:json] if respond_to?(:wrap_parameters)
end

YAML Files

/config/database.yml

Define which database, locations and basic settings.

# Default settings
default: &default adapter: sqlite3 pool: 5 timeout: 5000
development: <<: *default database: db/development.sqlite3
test: <<: *default database: db/test.sqlite3
production: <<: *default database: db/production.sqlite3

/config/secrets.yml

Security-related configs such as signed cookie validation. Never push this file to a public Git repository!

development: secret_key_base: randomlygeneratedkeyatleast30characterslong
test: secret_key_base: randomlygeneratedkeyatleast30characterslong
# Read production key from environment (can’t start app if doesn’t exist)
production: secret_key_base: <%= ENV[“SECRET_KEY_BASE”] %>

/config/locales/en.yml

Internationalisation (i18n) properties file.

en: hello: “Hello world”

Other Files

Ignoring .gitignore, Gemfile.lock and README.rdoc.

/config.ru

Rackup file for running the Rack-compliant (adheres to a spec) web server.

require ::File.expand_path(‘../config/environment’, __FILE__)
run Rails.application

/Gemfile

Dependencies for the project. Managed by Bundler.

source ‘https://rubygems.org'
# Rails
gem ‘rails’
# Use sqlite3 as the database for Active Record
gem ‘sqlite3'
# Use SCSS for stylesheets
gem ‘sass-rails’, ‘~> 4.0.3'
# Use Uglifier as compressor for JavaScript assets
gem ‘uglifier’, ‘>= 1.3.0'
# Use CoffeeScript for .js.coffee assets and views
gem ‘coffee-rails’, ‘~> 4.0.0'
# Use jquery as the JavaScript library
gem ‘jquery-rails’
# Turbolinks makes following links in your web application faster.
gem ‘turbolinks’
# Build JSON APIs with ease.
gem ‘jbuilder’, ‘~> 2.0'
# bundle exec rake doc:rails generates the API under doc/api.
gem ‘sdoc’, ‘~> 0.4.0', group: :doc
# Speed up development by keeping your application running in the background.
gem ‘spring’, group: :development

/Rakefile

Load web application when rake is called.

require File.expand_path(‘../config/application’, __FILE__)
Rails.application.load_tasks

/bin/bundle

Bundler gem management executable.

ENV[‘BUNDLE_GEMFILE’] ||= File.expand_path(‘../../Gemfile’, __FILE__)
load Gem.bin_path(‘bundler’, ‘bundle’)

/bin/rails

Main Rails executable.

begin load File.expand_path(“../spring”, __FILE__)
rescue LoadError
end
APP_PATH = File.expand_path(‘../../config/application’, __FILE__)
require_relative ‘../config/boot’
require ‘rails/commands’

/bin/rake

Build tool executable (Ruby equivalent to Make).

begin load File.expand_path(“../spring”, __FILE__)
rescue LoadError
end
require_relative ‘../config/boot’
require ‘rake’
Rake.application.run

/bin/spring

For development, keeps a copy of the running app in the background. Injects code into rails and rake executables.

unless defined?(Spring) require “rubygems” require “bundler”
if match = Bundler.default_lockfile.read.match(/^GEM$.*?^ spring \((.*?)\)$.*?^$/m) ENV[“GEM_PATH”] = ([Bundler.bundle_path.to_s] + Gem.path).join(File::PATH_SEPARATOR) ENV[“GEM_HOME”] = “” Gem.paths = ENV
gem “spring”, match[1] require “spring/binstub” end
end

References

Ruby observations from a Java guy

rails
About

After a decade of Java experience currently learning Ruby/Rails because it’s the Hot Shit™.

Overall

  • Fun. A cowboy language (see: monkey patching).
  • Rapid development great for new projects, startups, messing about. Productive.
  • Low enterprise adoption in Australia yet lots of smaller companies looking for developers.
  • Lots of freedom for developers (patching, overriding) relies on discipline of programmers, and since all humans make mistakes, eventual loss of control — especially for large projects. Mitigate risk with comprehensive regression testing and small modular components.
  • ERB (Embedded RuBy) reminds me of JSP on pages in Java — a practice frowned upon because it mixes languages (used HTML-valid taglibs).
  • Lots of files generated with a new project (41 folders, 41 files)!
  • Lots of objects per request (Rails3 ‘hello world’ ~8.5k objects, GC every ~6 requests)!
  • MRI (Matz’s Ruby Interpreter, CRuby) was replaced by YARV (Yet Another Ruby Vm, KRI) in Ruby 1.9 as the default executable. Other implementations include JRuby (runs on Java’s JVM), Rubinius, RubyMotion etc.
  • Ruby doesn’t support true concurrency (Global Interpreter Lock). Use JRuby threads or fibres and non-blocking IO. Headaches of dealing with code and gems that aren’t thread-safe.

Conventions

  • Convention over configuration (“The Rails Way”), lean principles.
  • No service folder/layer by default? Rails way to put business logic in models — can get messy.
  • GDD (Gem-Driven-Development): Before coding, check what gems exist, don’t reinvent the wheel. For serious projects, can lead to security and performance (i.e. object creation) issues.
  • Brackets for method parameter/arguments? No consensus.
  • Symbols don’t get GC’d so don’t use for dynamic data or else memory leak.
  • “Fat model, skinny controller.” Only logic for sessions, cookies, request parameters, model selection, rendering/redirecting.
  • Don’t put too much logic in the view — leverage partials, decorators, helper classes.
  • Don’t put too much logic in the model. Only logic for ActiveRecord relations/validation, database interaction, property helpers (e.g. fullname), sophisticated queries. Use POROs (Plain Old Ruby Objects) instead (i.e. services, SOA).

Production

  • Define secret_key_base in secrets.yml.
  • Set config.assets.compile to true in production.rb (otherwise CSS not included).

Heroku

  • No support for SQLite, must add ‘pg’ (PostgreSQL) gem to Gemfile for production. Pain in the ass adding the pg gem (requires install of Postgres locally even though not used…WTF).
  • Very easy to deploy.

Testing

  • Huge necessity for BDD/TDD and regression testing to ensure confidence. Poor static code analysis due to loosely-typed, difficult to enforce design patterns with interfaces/abstract/access modifiers/encapsulation.
  • Minitest or RSpec? Both good.
  • Domain Specific Language (spec) notation (e.g. “describe calling foo it should return bar”), matches user stories, easier for non-programmers to understand, personal preference.
  • Minitest supplied with Ruby, replacement for older Test::Unit gem, unit testing (Ruby or spec), mocking, benchmarks, lightweight, BDD requires configuration (capybara = start browser), require ‘test_helper’ for web application context (loads initialisers/configs, slower).
  • RSpec many ways to do the same thing, advanced features (before all, around each, shared groups/contexts, powerful mocks, verifying doubles = mock checks methods exist…awesome), BDD out of the box.
  • Additional gems: Mocha (mocking), Shoulda (matchers).

RubyMine

  • Generated test classes do not work out of the box.
  • Can’t get Heroku deployment to work (terminal only).
  • Poor code completion and IDE errors due to loosely-typed. Not knowing off-by-heart end up Googling (read: Stack Overflow) a lot. BDD/TDD guesswork because not sure if even syntax is correct until at the implementation stage — run and see if it breaks!

Conclusions

  • Very enjoyable and ideally suited for non-critical projects/modules with manageable numbers of developers (80/20 rule).
  • BDD/TDD is essential.
  • Standard Rails may have performance issues due to high object creation and inefficient GC.

References

Man Crush: David Heinemeier Hansson

About

Notes and thoughts from David’s homepage. Inspirational, he has a great outlook and perspective on business and lifestyle.

Note: Considering consolidating the notes from the three videos in the future (as they overlap quite a bit).

At a glance

  • Created Ruby on Rails
  • Partner at 37signals (known for Basecamp)
  • Author or co-author of REWORK and Getting Real
  • Public speaker
  • Race-car driver (WTF?!)

The Secret To Making Money Online

“Great achievement comes from solving simple problems.”

  • Charge for your product
    • Subscription, pay per use or pay to own are all tried-and-true business models
    • If the product is good enough, people will pay for it
    • Scaling problems are GREAT as it linearly correlates to revenue (unlike YouTube…)
  • Don’t focus on trying to “catch the next wave” (e.g. Facebook, YouTube)
    • Odds of success are microscopic (exception rather than the rule)
    • The jump from nothing to $1m is a lot harder than the jump from $1m to $1b
    • People are too eager to release and take VC money
    • Sensationalised in the media along with plane crashes
  • Try and build an “Italian restaurant” (doesn’t have to be the best Italian food in the world, just convenient and slightly better than local competitors)
    • Most businesses will still fail
    • $1m is a hard but achievable target (e.g. 2,000 customers @ $40/month, with 5% conversion rate need 40,000 customers or 110 a day)
    • Be happy with $1m…it’s a lot of money
    • Only 2,000 customers? Attack a niche!
  • Choose sustainable over viral growth
    • Start small and be patient
  • Target Fortune 5,000,000 companies!
    • No business too small
    • Just want their problem solved at a reasonable price
  • Many small customers over a few large customers
    • No corporate bureaucracy
    • Not reliant on a few customers (bend over backwards, feature coercion)
    • Evenly distributed customers = less fragile
  • Passion over money
    • If you can earn a million a year and do what you enjoy, why give that up for a pay day? What would you do next?
    • After a point (comfortable living, freedom), money provides diminishing returns and even negatives (email, meetings, reputation, more to lose)
  • Work smarter, not harder
    • Only 2/3 really productive hours a day
    • Working less hours a week forces you to focus and not waste time
    • The work ethic (hours, patterns, practices) at the start will never ease off — make sustainable choices

TWiST #46 Interview

Facebook to startups is equivalent to lottery tickets for a wage — it only works for a precious few. Is a lottery a good way to run a business?

  • Rule #1: Just build a profitable business
    • Don’t try and get hits then figure it out later
    • Charging for your product is better than begging for advertising crumbs
    • Social, traffic, hype (e.g. TechCrunch), VCs and acquisitions are a fake game, a bubble
    • Lots of spinning the wheels while going nowhere
  • Startups don’t need VC
    • Nothing easier than spending someone else’s money
    • Too much runway, easy to goof around, no fire up your ass!
    • Besides the top tier, overall VCs have lost money
  • Acquisition (i.e. exit strategy) will likely make your company worse
    • Successful acquisitions are the exception, not the rule
    • Keeps happening because of delusions of grandeur and risk taking (i.e. “this time it will be work”)
  • Profit is king
    • Best feedback mechanism for measuring the success of a idea/company
    • Revenue is irrelevant
    • Strong profit margins is the best position to be in
    • Who cares about market share if profits are strong (see: Apple)? Only matters if it leads to profits.
  • Selling out
    • Work on your best idea right now. Why would you sell to work on your second best idea?
    • It’s possible you won’t have a better idea in the future (e.g. Microsoft’s best ideas of Windows/Office are decades old)
  • Big != Better
    • Overvaluations, going public, massive acquisitions…
    • Loss of control and dealing with bullshit associated with being “big”
    • Reduced quality of life
  • Misc
    • If established and profitable, it’s OK to sell non-controlling shares for a personal payout (i.e. take money ‘off the table’)
    • Urgency is overrated
    • Meetings suck! Make decisions quickly, fail fast and iterate
    • Working harder (i.e. more hours) does not equal success
    • Revenue per employee is a good measure of efficiency (beware the bloat)
    • Web allows for many small businesses — not just Wallmarts

Unlearn Your MBA

“Take the time, at a reasonable sustainable pace, and own your company in the end — or strap the [VC] time bomb on your back and see if that’s going to make you run faster; and see if you’ll be happier in the end.”

  • Planning is harmful guessing
    • Most decisions are temporary (i.e. not building factories)
    • Decisions don’t matter that much, pick and get started
    • Constraints force new ideas in new markets
  • Sustainable and scalable
    • You can’t pay your bills with eyeballs (i.e. page hits)
    • Should be no linear correlation between revenue and the org chart (more revenue doesn’t mean more people)
    • Sustainable businesses built on word of mouth
    • Generally don’t make big splashes in the media (not sexy)
  • Working
    • Being a workaholic is not a requirment or guarantee for success
    • Only 5-10% of effort matters
    • Less execution through a well rested mind (i.e. work smarter)
    • Startups need people who contribute direct value, not ideas or management
    • Entrepeneurs should force themselves to work for a shit company for 6 months to learn from mistakes and get the motivation to be better
  • VC is a time bomb
    • Seems like a good idea, probably the most harmful thing you can do
    • Push for big returns in short timeframes — have no interest in slow, sustained growth
    • No constraints and no urgency…until the the time and money runs out (then you’re fired)
    • Spending your own money is a strong driving force
    • Either have the pain now (struggle with no money) or later (wasted time, pushed out, nothing to show)
    • Wasted time and effort getting the next round of funding (addicted, next hurdle)
    • Accelerated growth is a farce — it takes a long time to get something good
    • Killing industry by crushing ideas/companies and wasting people’s time
  • Big leagues via the small leagues
    • Even small companies can make millions per year
    • Creating a small company is like playing poker: it takes skill and luck, but at least you have a fighting chance. Creating the next YouTube is like the lottery.
    • Very few companies go straight to big, but that’s what VCs are searching for (optionality, low investment for a potential huge payoff)
    • Most ‘overnight successes’ took 10-years
  • Existing business lessons
    • Easiest way to destroy a successful company is to deviate away from what make it successful
    • Idle managers are the worst as they need to make things up (e.g. processes) to justify their job
    • Opportunities exist when times are tough and business shift to more cost-effective alternatives
    • Salesforce has terrible margins (~6% profit)…and they sell software!
    • Bad decisions a product of the environment more than the people (e.g. no accountability, perspective, training, familiarity with business, culture)

Jason Calacanis

TWiST host. The Bill O’Reilly of technology (maybe not that bad…maybe).

Greedy, forceful, egotistical, selfish, devious and arrogant. Don’t care who you think you know, what you did and how much you made.

Derives pleasure through belittling and condescending behaviour. Relentless comparisons to tech giants (e.g. Google, Facebook) in an effort to poke holes in business comfortable where it is. Not every basketball player in the NBA can be (or needs to be) LeBron James.

Afraid (“are you a communist?”) of a person who is perfectly content in the ‘middle’ rather than at the ‘top’. Confused and offended at the very notion that you wouldn’t trade happiness and purpose for a pile of money!

A manifestation of everything that is wrong with capitalism.

References