629 snippets – displaying 1–30

How to use Rails Engines

Create a new engine named x:

$ rails plugin new x --mountable

Load the engine by modifying Gemfile:

gem 'x', path: 'engines/x'

Mount the engine as root in config/routes.rb:

mount X::Engine => ”/”, as: ’root’

Optionally, allow the engine to override views and assets by adding this to config/application.rb:

ApplicationController.prepend_view_path Rails.root.join('engines', 'x', 'app', 'views')

%w(stylesheets javascripts images).each do |dir|
  Rails.configuration.assets.paths.unshift Rails.root.join('engines', 'x', 'app', 'assets', dir)
end

Resources

https://github.com/shageman/the_next_big_thing
http://pivotallabs.com/migrating-from-a-single-rails-app-to-a-suite-of-rails-engines/
http://pivotallabs.com/unbuilt-rails-dependencies-how-to-design-for-loosely-coupled-highly-cohesive-components-within-a-rails-application/
http://pivotallabs.com/experience-report-engine-usage-that-didn-t-work/
http://assets.pivotallabs.com/1439/original/2012_06_12_wrangling_large_rails_codebases.pdf
http://microservices.io/patterns/monolithic.html

Web Microframework Benchmark

Results on an old MacBook Air:

Ruby 2.1.0 + Hobbit + Hat + puma (-t 8 -w 2) - ~100-150 req/s (Hat = Hobbit app template with i18n, asset pipeline, etc)
Ruby 2.1.0 + Hobbit + Hat custom + puma (-t 8 -w 2) - ~1500 req/s (Hat without asset pipeline)
Ruby 2.1.0 + Hobbit + puma (-t 8 -w 2) - ~1600 req/s
Ruby 2.1.0 + rack + puma (-t 8 -w 2) - ~1600 req/s
Golang 1.3.1 + net/http - ~2700 req/s
Elixir 1.0.0 + Phoenix 0.4.1 - ~1300 req/s
Clojure 1.6.0 + ring 1.3.1 - ~5000 req/s
Clojure 1.6.0 + ring 1.3.1 + slim - ~270 req/s

YMMV.

Cuba Hello World

rubycubachristianVersion 2

http://cuba.is/

app.rb:

require "cuba"
#require "rack/protection"

#Cuba.use Rack::Session::Cookie, :secret => "__a_very_long_string__"
#Cuba.use Rack::Protection

Cuba.define do
  # only GET requests
  on get do
    # /
    on root do
      res.write "Home"
    end
  end
end

config.ru:

require "./app"
require 'sprockets'

map '/assets' do
  env = Sprockets::Environment.new
  env.append_path 'assets/javascripts'
  env.append_path 'assets/stylesheets'
  env.append_path 'assets/images'
  run env
end

run Cuba

Gemfile:

source 'https://rubygems.org'
gem 'cuba'
gem 'sprockets'
gem 'puma'

Anonymous hello world benchmark:

Node.js - 27 000 req/s
Ruby cuba - 17 000 req/s
Ruby hobbit - 17 000 req/s
Ruby rack - 25 000 req/s

Yes, Ruby is very slow.

How to prepend to directory to the view path in Rails (3, 4, etc)

Put this in an initializer e.g. config/initializers/parameterization.rb to load different views for different apps that use the same codebase:

APP_NAME = ENV['APP']
ApplicationController.prepend_view_path Rails.root.join('app', 'views', APP_NAME)

prepend_view_path can also be called e.g. once per request before rendering the view to have different view templates for each subdomain, user, cat, etc.

How to Implement Client-Cert Authentication with Apache (Smart Cards, HST-kortti, FINEID)

The goal is to automatically sign in users who have an SSL client-certificate issued by a known certificate authority, e.g. Finnish Väestörekisteri (VRK).

First, we’re going to install and configure Apache 2.2 for client-cert authentication.

Install Apache 2.2

$ brew install -v httpd22.rb 2>&1

Download VRK Certificates

In this example we’re using VRK’s root test certificate. Make sure you pick the right certificate for your purposes, e.g. the one for healthcare professionals.

VRK certificates can be downloaded here:
http://fineid.fi/default.aspx?docid=2293&site=10&id=597

$ cd /usr/local/etc/apache2/2.2/
$ mkdir ssl
$ wget http://vrk.fineid.fi/certs/vrktestc.crt
$ mv vrktestc.crt ssl/
# Convert from DER to PEM format
$ openssl x509 -in ssl/vrktestc.crt -inform DER -outform PEM -out ssl/vrktestc.pem

Download VRK Revocation List

Revocation lists for VRK’s client certificates can be found here:
http://fineid.fi/default.aspx?docid=2330&site=10&id=588#whereistherevocationlist

$ cd /usr/local/etc/apache2/2.2/
$ wget http://proxy.fineid.fi/crl/vrkcqcc.crl
$ mv vrkcqcc.crl ssl/
# Convert from DER to PEM format
$ openssl x509 -in ssl/vrkcqcc.crl -inform DER -outform PEM -out ssl/vrkcqcc.pem

I was unable to get openssl or Apache to read the revocation list, so the other option is to query VRK’s LDAP servers for revoked certificates.

Generate Self-Signed Certificate for Apache

If you have a real certificate, skip this part.

$ cd /usr/local/etc/apache2/2.2/
$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ssl/server.key -out ssl/server.crt

Configure Apache

<VirtualHost xxx:443>

ProxyPreserveHost On
ProxyPass / http://0.0.0.0:8888/

SSLCertificateFile "/usr/local/etc/apache2/2.2/ssl/server.crt"
SSLCertificateKeyFile "/usr/local/etc/apache2/2.2/ssl/server.key"
SSLCACertificateFile "/usr/local/etc/apache2/2.2/ssl/vrktestc.pem" 
SSLVerifyClient optional # none, optional, require and optional_no_ca
SSLVerifyDepth 2 # Root certificate requires depth >= 2

# Let Rails know we’re using HTTPS
RequestHeader set X_FORWARDED_PROTO 'https'

#
# Forward certificate information to application
# See http://httpd.apache.org/docs/2.2/mod/mod_ssl.html#envvars
#
# Subject's certificate
RequestHeader set SSL_CLIENT_S_DN "%{SSL_CLIENT_S_DN}s"
RequestHeader set SSL_SERVER_S_DN_OU "%{SSL_SERVER_S_DN_OU}s"
# Issuer's certificate
RequestHeader set SSL_CLIENT_I_DN "%{SSL_CLIENT_I_DN}s"
# Verification status: NONE, SUCCESS, GENEROUS or FAILED:reason
RequestHeader set SSL_CLIENT_VERIFY "%{SSL_CLIENT_VERIFY}s"


# Optional settings
#
# Require issuer of certificate to have a specific O and OU:
<Location />
    SSLRequire %{SSL_CLIENT_I_DN_O} eq "Vaestorekisterikeskus TEST" and \
           %{SSL_CLIENT_I_DN_OU} eq "Terveydenhuollon testiammattivarmenteet"
</Location>

# Export SSL and certificate variables
# SSLOptions +ExportCertData +StrictRequire +StdEnvVars

# Revocation list
# NOTE: disabled because of “Unable to configure X.509 CRL storage for certificate revocation”
#SSLCARevocationFile "/usr/local/etc/apache2/2.2/ssl/vrkcqcc.crl"

Troubleshooting & FAQ

How can I verify that the SSL-certificate is set up properly?

$ openssl s_client -connect localhost:443 -showcerts

Acceptable client certificate CA names
/C=FI/ST=Finland/O=Vaestorekisterikeskus TEST/OU=Certification Authority Services/OU=Varmennepalvelut/CN=VRK TEST Root CA

Why am I getting SSL handshake failures?

Invalid self-signed certificate?

Removing the smart card or disconnecting the smart card reader will close the browser

This means you need to expire the session when the browser is closed. In a Rails application you need to remove expire_after from session_store.rb.

Browsers issues

Browers might perform client-cert authentication in different ways. Many browsers have bugs related to client-cert authentication.

Inserting the smart card after starting the browser might mean the SSL client certificate’s information is not sent to the server.

If you’re having issues, the best solution is usually to restart the browser.

Apache is unable to read the revocation list (CRL)

VRK’s revocation certificate is not in the format required by Apache’s mod_ssl, i.e. the file doesn’t begin with:
-BEGIN X509 CRL——-

The file is binary, so the CRL list is probably in DER format. However, converting the file to PEM format doesn’t work:

$ openssl x509 -in ssl/vrkcqcc.crl -inform DER -outform PEM -out ssl/vrkcqcc.pem

Reading the CRL file seems to work fine:

$ openssl crl -in ssl/vrkcqcc.crl -text -noout -inform DER

The solution for getting revocation to work is to use an LDAP search for revocation. Apache doesn’t support this.

Implementing Client-Cert Authentication in Web Applications

An application that needs to support client-cert authentication should implement the following system and user stories:

  • Enable client-cert authentication (user)

As a user I want to enable client-cert authentication, so that I don’t need to sign in manually.

  • Disable client-cert authentication (user)
  • Sign in user with client certificate (system)

In Ruby on Rails you can access the client-certification information through the headers set by Apache:

request.headers['HTTP_SSL_CLIENT_S_DN'] == user's distinguished name (DN)
request.headers['HTTP_SSL_CLIENT_I_DN'] == issuer's DN
request.headers['HTTP_SSL_CLIENT_VERIFY'] == 'SUCCESS'

Simple Ruby Server With Rack

Create a file named config.ru:

run lambda { |env|
  [200, {"Content-Type" => "text/html"}, [view(env)]]
}

def view(env)
  res = ""
  res << "<html><body><pre>"
  env.sort.each do |key, value|
    res << "#{key}: #{value}"
    res << "\n"
  end
  res << "</pre></body></html>"
  res
end

Start the server with rackup:

$ rackup

Access http://localhost:9292 to see the request headers.

ActiveRecord's write_attribute is deprecated

ActiveRecord’s write_attribute is deprecated, so now you only have one hundred other ways of assigning attributes.

My favorite for dynamic assignment of attributes is:

class Dog
  def crap=(poo)
     self[:trash] = poo # don't try to use self.attributes[:trash] here you fool.
  end

  def crap
     self[:trash] # don't try to use self.attributes[:trash] here you fool.
  end
end

Sprockets for Sinatra

Features:

  • asset pipeline based on Sprockets
  • precompilation
  • versioning of assets

The “concern” that you need to include in your application:

#
# Asset pipeline for Sinatra based on Sprockets.
#
# NOTE: In production use rake assets:precompile to compile assets and generate an
# asset version file.
#
module AssetsConcern
  extend ActiveSupport::Concern

  included do
    set :assets_prefix, '/assets'
    set :assets_path, File.join(App.root, 'public', assets_prefix)
    set :assets_precompile, true
  end

  module ClassMethods
    #
    # Returns the current version.
    #
    def asset_version
      @@asset_version ||= read_asset_version
    end

    def read_asset_version
      file = 'public/assets/version'
      if File.exist?(file)
        File.read(file).to_i
      else
        nil
      end
    end

    #
    # Returns the path to the assets.
    #
    def asset_path(name, type)
      if App.asset_version
        file = "/assets/#{name}-#{App.asset_version}.#{type}"
      else
        file = "/assets/#{name}.#{type}"
      end
    end

    def sprockets
      require "yui/compressor"
      project_root = File.expand_path(File.dirname(__FILE__))
      assets = Sprockets::Environment.new(project_root)
      # Include js, css, and bower managed components
      %w(bower_components app/js app/css).each do |path|
        assets.append_path(File.join(App.root, path))
      end
      assets
    end
  end
end

The Rake task for compilication of assets:

namespace :assets do
  task :precompile do
    version = Time.now.to_i 
    App.sprockets['application.js'].write_to("public/assets/application-#{version}.js")
    App.sprockets['application.css'].write_to("public/assets/application-#{version}.css")
    File.open('public/assets/version', 'w') { |f| f << version }
    puts "Done... Version #{version}"
  end
end

The view template:

= css_tag(:application)
= js_tag(:application)

How to Implement Flash for Sinatra (or Any Other Framework)

Implementing Rails’ “Flash concept” for Sinatra is easy.

This example gives you:

  • Flash messages that are translated by the I18n library
  • Simple code. For example rack-flash is not needed.
  • Cachable pages. Storing your application’s flash messages in a cookie means you can cache the whole page. There are no user specific flash messages in the response HTML.

Implementation

Ruby code:

def flash(key, value)
  fail "Invalid key '#{key}'" unless [ :info, :error ].include?(key)
  response.set_cookie "flash_#{key}", value: I18n.t(value), path: '/'
end

Put the code in a helper or controller.

Coffeescript code:

class Flash
  constructor: (@flash_holder, @cookie_prefix) ->
    @flash_holder ?= '#flash'
    @cookie_prefix ?= 'flash_'

  show: ->
    for key in ['info', 'error']
      # Read cookie, e.g. flash_info
      name = @cookie_prefix + key
      value = $.cookie(name)
      if value
        # Set flash holder contents to cookie value
        $(@flash_holder).attr('class', name).html(value).show('fast')
        $.removeCookie(name)

$ ->
  # Display the flash message
  new Flash().show()

  # You can customize the behavior by passing parameters to the constructor, e.g.
  new Flash('#flash_holder', 'cookie_prefix').show()

HTML code:

<div id="#flash"></div>

SASS code:

#flash
  display: none

.flash_info
  color: darkgreen

.flash_error
  color: darkred

Usage

In your controller:

get '/' do
  flash :info, 'login.failed'
end

Dependencies

How to color your bash prompt red in production

bashcolorredchristianVersion 3

How to color your bash prompt red in production:

export RED="\[\e[1;31m\]"
export NORMAL="\[\e[0m\]"

# user@host: ~/directory $
BASE_PROMPT="$RED\u@\h$NORMAL: \w "

# Root prompt should have #
if [[ $EUID == 0 ]] ; then
  export PS1="$BASE_PROMPT#"
else
  export PS1="$BASE_PROMPT\$"
fi

Put it in /etc/profile.d/promptcolor.sh for all users to see the prompt in color.

Script also helps with lowering anxiety levels.

Checking For JavaScript Errors or e.g. XSS Attacks With Capybara

Use the following code to check for JavaScript errors, or XSS vulnerabilities:

class IntegrationSpec < MiniTest::Spec
  def assert_no_js_errors
    assert_equal 0, page.driver.error_messages.size, page.driver.error_messages.ai
  end

  def assert_no_alerts(types = [:alert, :confirm, :prompt])
    types.each do |type|
      alerts = page.driver.send(:"#{type}_messages")
      assert_equal 0, alerts.size, alerts.ai
    end
  end
end

Test plan

  • Insert XSS hacks into database
  • Write integration tests that visit all pages

Requires

  • capybara
  • capybara-webkit

Rails Services

railsserviceschristianVersion 10

Put your business logic in a service layer, not in your controllers, helpers, models, or anywhere else.

app/services/service.rb:

#
# Base service class.
#
module Service
  extend ActiveSupport::Concern

  included do
  end

  module ClassMethods
    def call(*args)
      new(*args).call
    end
  end
end

app/services/create_report.rb:

#
# Create order user story.
#
class CreateOrder
  include Service

  def initialize(user, product)
    @user = user
    @product = product
    validate
  end

  def call
    Order.transaction do
      ...
    end
  end

  def validate
    ...
  end
end

Usage:

class OrdersController # or OrderJob
  def create
    ...
    @order = CreateOrder.call(user, product)
    ...
  end
end

Naming conventions:

  • app/services/verb_subject.rb
  • class VerbSubject
  • app/services/namespace/verb_subject.rb
  • class Namespace::VerbSubject

Benefits:

  • maintainability
  • readability
  • testability
  • etc

Ruby on Rails Security Checklist

Checklist

  • Don’t store passwords (config/database.yml, db/seeds.rb), API keys (Amazon AWS), the Rails secret token (config/initializers/secret_token.rb), etc in version control.
  • Don’t store production passwords and API keys on developer laptops.
  • Inject secrets, API keys, etc with e.g. dotenv
  • Don’t import production data to other environments without obfuscating or cleaning data.
  • Use a different secret token in production than staging, development, and other environments.
  • AR serialize and store (YAML) is using eval. Don’t use YAML, use JSON.
  • Don’t write redirect_to params[:url]. If you have to then add only_path: true, or use URI.parse to get the path component.
  • ActiveRecord is not safe from SQL injection. See http://rails-sqli.org for details.
  • Enforce password strength.
  • Set cookie timeout expire_after: 30.minutes.
  • Hash passwords before storing them in a database (has_secure_password).
  • Don’t use GET for actions that change data (RequestForgeryProtection).
  • Uploaded files should be sanitized, name, path, etc.
  • Downloads of uploaded files should most likely be authorized (send_file , Downloads done right).

Recommended Software

  • secure_headers gem (Content Security Policy)
  • mod_security (Apache & nginx web application firewall)
  • rack::attack gem (Similar to mod_security)
  • bundler_audit gem (Checks Gemfile.lock for vulnerable gems)

Further resources

How to Visualize a Rails Application

Visualizing a Rails project is easy with:

# Generate ERD diagram
rake erd filetype=dot
dot -Tpng erd.dot > erd.png

# Generate models diagram
railroady -M -o models.dot
dot -Tpng models.dot > models.png

# Generate controllers diagram
railroady -C -o controllers.dot
neato -Tpng controllers.dot > controllers.png

# Generate AASM state diagram
railroady -A -o states.dot
dot -Tpng states.dot > controllers.png

Timing and Benchmarking Minitest Tests

Put this in test/support/timing.rb:

module MiniTestTiming
  class << self; attr_accessor :failure_threshold end

  def before_setup
    @start_time = Time.now
    super
  end

  def after_teardown
    super
    end_time = Time.now
    duration = (end_time - @start_time).seconds.round(1)
    time = duration.to_s + "s"
    name = "#{self.class.name}.#{__name__}"
    limit = MiniTestTiming.failure_threshold
    File.open 'log/test_duration.log', 'a' do |f|
      f << "#{time.ljust(5)}: #{name}\n"
    end
    assert duration < limit, "#{name} took too long (#{time} > #{limit}s)"
  end
end

# Set threshold for failing tests to 1 second
MiniTestTiming.failure_threshold = 1

class MiniTest::Test
  include MiniTestTiming
end

The time it takes your tests will show up in log/test_duration.log.

You can also output the time it took to run a test by using the verbose switch:

rake test TESTOPTS="--verbose"

MiniTest with Deferred Garbage Collection

class DeferredGarbageCollection
  DEFERRED_GC_THRESHOLD = (ENV['DEFER_GC'] || 10.0).to_f

  @last_gc_run = Time.now

  def self.start
    GC.disable if DEFERRED_GC_THRESHOLD > 0
  end

  def self.reconsider
    if DEFERRED_GC_THRESHOLD > 0 && Time.now - @last_gc_run >= DEFERRED_GC_THRESHOLD
      GC.enable
      GC.start
      GC.disable
      @last_gc_run = Time.now
    end
  end
end

module MiniTestDeferredGC
  def before_setup
    DeferredGarbageCollection.start
    super
  end

  def after_teardown
    super
    DeferredGarbageCollection.reconsider
  end
end

class MiniTest::Test
  include MiniTestDeferredGC
end