618 snippets – displaying 1–30

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

Updating Passwords and Emails When Using Active Admin and Devise

With Active Admin and Devise, we want to:

  • Skip email confirmations
  • Require password and email confirmations, but only when changing the email or password

This code supports our requirements:

ActiveAdmin.register User do
  controller do
    def update
      if params[:user][:password].blank?
        params[:user].delete("password")
        params[:user].delete("password_confirmation")
      end
      if params[:user][:email].blank?
        params[:user].delete("email")
        params[:user].delete("email_confirmation")
      end
      super
    end
  end

  before_save do |user|
    user.skip_confirmation!
    user.skip_reconfirmation!
  end
end

See the Active Admin DSL for details

Integration Tests With Capybara, Devise, Minitest::Spec, and Capybara-Webkit

Does your boss want you to run integration tests with Capybara, Devise, Minitest::Spec, Capybara-Webkit, and the Headless gem?

Then use this code, which features:

  • Automatic test failure on JavaScript errors
  • Check for XSS attack vectors with assert_no_alerts.
  • Sign in/out with Devise

require 'headless'
require 'awesome_print'

headless = Headless.new(reuse: true, destroy_at_exit: false)
puts "Headless mode. Display :#{headless.display}. PID #{Process.pid}"
headless.start

class IntegrationSpec < MiniTest::Spec
  include Rails.application.routes.url_helpers
  include Capybara::DSL
  include Warden::Test::Helpers
  Warden.test_mode!

  before do
    assert_no_js_errors
  end

  after do
    Warden.test_reset!
  end

  def sign_in(user, scope = :user)
    login_as(user, scope: scope)
  end

  def sign_out(scope = :user)
    logout(scope)
  end

  def assert_no_js_errors
    errors = page.driver.error_messages
    assert_equal 0, errors.size, errors.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

  def default_url_options
    Rails.configuration.action_mailer.default_url_options
  end
end

MiniTest::Spec.register_spec_type( /integration$/, IntegrationSpec )

Remember to name your tests like this:

require 'test_helper'

describe "Dashboard integration" do
...
end

Otherwise the tests won’t inherit from the IntegrationSpec class.

How to Test Authentication With Devise+Capybara+Minitest

Testing authentication functionality with Capybara and Devise? See the following checklist:

  • Use shared connections or disable transactional fixtures.
  • Set Capybara.default_host to match config.session_store.domain or you’ll get “401 Unauthorized”
  • Name of test should end with “integration”, e.g. describe “Dashboard Business integration” do
  • Add the following to your integration tests:

  include Warden::Test::Helpers
  Warden.test_mode!

  after do
    Warden.test_reset!
  end

Full example of integration test with Devise, Capybara, and minitest:

class IntegrationSpec < MiniTest::Spec
  include Rails.application.routes.url_helpers
  include Capybara::DSL
  include Warden::Test::Helpers
  Warden.test_mode!

  before do
    @routes = Rails.application.routes
  end

  after do
    Warden.test_reset!
  end

  def sign_in(user)
    login_as(user, scope: :user)
  end

  def sign_out
    logout(:user)
  end

  def default_url_options
    Rails.configuration.action_mailer.default_url_options
  end
end

MiniTest::Spec.register_spec_type( /integration$/, IntegrationSpec )

How to show Rails environment in Rails/Pry console

Show Rails environment in Rails/Pry console by adding this to ~/.pryrc:

def env
  env = Rails.env
  if env.production? 
    "\e[1;31m#{env}\e[0m - " 
  else
    "\e[1;30m#{env}\e[0m - " 
  end
end

prompt = env

Pry.config.prompt = [
  proc { |obj, nest_level, _| "#{prompt}(#{obj}):#{nest_level} > " },
  proc { |obj, nest_level, _| "#{prompt}(#{obj}):#{nest_level} * " }
]

Pry.config.hooks.add_hook(:before_session, :show_ruby_version) do
  puts "Ruby #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}"
end

Environment name is shown in red, if in production. Note: use pry-rails gem…

Rails Console Update Problems?

Console not updating when typing? Pry/Ruby/Rails/something messing up console output? Check your stty settings:

stty -a

Is echo disabled. Do you see “-echo” in the output?

Try resetting the console:

reset
# or
stty 

Check if you have incorrectly colorized output:
http://stackoverflow.com/questions/8806643/colorized-output-breaks-linewrapping-with-readline/8916332#8916332

Are you using a gem or something else that colorizes console output? Try disabling them.

Examples:

See:
http://askubuntu.com/questions/171449/shell-does-not-show-typed-in-commands-reset-works-but-what-happened

Strip trailing whitespace with vim

" Strip trailing whitespace
function! <SID>StripTrailingWhitespaces()
    " Skip these
    if &ft =~ '\|slim|haml'
        return
    endif
    " Preparation: save last search, and cursor position.
    let _s=@/
    let l = line(".")
    let c = col(".")
    " Do the business:
    %s/\s\+$//e
    " Clean up: restore previous search history, and cursor position
    let @/=_s
    call cursor(l, c)
endfunction
autocmd BufWritePre * :call <SID>StripTrailingWhitespaces()

Validating HTML with validator.w3.org and Ruby

require "net/http"

def validate(html)
  response = Net::HTTP.start("validator.w3.org") do |r|
    query = "output=json&fragment=" + CGI.escape(html)
    r.post2("/check", query)
  end
  if "Invalid" == response["x-w3c-validator-status"]
    File.open('/tmp/html-validation.html', 'w') { |f| f << html }
    JSON.parse(response.body)
  end
end

# List all validation errors
html = File.read('/tmp/html-validation.html')
if errors = validate(html)
  puts errors
end

Validating HTML Emails with Nokogiri, Tidy, and validator.w3.org

# We're validating the HTML generated by the Devise mailer
user = User.new email: 'xxx@xxx'
mail = Devise.mailer.confirmation_instructions(user, self).deliver
html = mail.html_part.body.to_s

Validate with tidy:

File.open('/tmp/html-validation.html', 'w') { |f| f << html }
unless system "tidy -errors -q -f /tmp/validation-errors.txt /tmp/html-validation.html"
  fail "Validation failed:\n #{File.read('/tmp/validation-errors.txt')}"
end

Remember to install tidy first:

$ sudo apt-get install tidy

Validate with Nokogiri is not so fun or accurate:

# Validate XML
doc = Nokogiri.XML(html)
doc.errors

# Validate against a schema, if you want
xsd = Nokogiri::XML::Schema(open('http://www.w3.org/2002/08/xhtml/xhtml1-strict.xsd'))
xsd.validate(doc)

For validation with w3.org, see this script

Recursive Postgres Query That Finds Descendants of a Node in a Tree

The recursive query:

WITH RECURSIVE tree AS (
  SELECT id, name, parent_id FROM nodes WHERE id = 25
  UNION ALL
  SELECT nodes.id, nodes.name, nodes.parent_id FROM nodes, tree WHERE nodes.parent_id = tree.id)

This is where we define a virtual table called tree:

WITH RECURSIVE tree

This SQL finds the root node and initializes the recursive function:

SELECT id, name, parent_id FROM nodes WHERE id = 25

The next part recursively fetches the descendants of the specified node:

SELECT nodes.id, nodes.name, nodes.parent_id FROM nodes, tree WHERE nodes.parent_id = tree.id)

This SQL query shows the descendants, and the parent which has an ID of 25:

SELECT * FROM tree;

In Ruby you could use this code to generate your SQL:

  def tree_sql(opts={})
    table = opts.fetch(:table) # e.g. 'categories'
    cols = opts.fetch(:cols) # e.g. %w(id name)
    <<-SQL
      WITH RECURSIVE #{table}_tree AS (
        -- initialize
        SELECT
          #{cols.join(', ')}, 0 as n_depth
        FROM
          #{table}
        WHERE
          -- Use bind variables and ? here if you want
          id = #{parent_id}
        UNION ALL
          -- iterate
          SELECT
            #{cols.map { |col| "#{table}.#{col}" }.join(', ')}, n_depth +1
          FROM
            #{table}, #{table}_tree
          WHERE
            #{table}.parent_id = #{table}_tree.id)
    SQL
  end

Details:

  • Use UNION instead of UNION ALL if you have duplicates, or if you’re not worried about performance when Postgres has to scan for duplicates.
  • WITH Queries in Postgres
  • Recursive queries can loop indefinitely. If this happens, use the ANY functions.

Frozen ActiveRecord Attributes Validator

Disallows update of frozen attributes:

#
# Validates that an attribute cannot be changed after object has been created.
#
# Usage:
#   validates :user_id, :token, frozen_attribute: true, allow_blank: true
#
# allow_blank: set to true to allow setting nil attributes
#
class FrozenAttributeValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    changed = record.send("#{attribute}_changed?")
    was_not_blank = if options[:allow_blank]
                      !(record.send("#{attribute}_was").blank? && value.present?)
                    else
                      true
                    end
    if record.persisted? && changed && was_not_blank
      record.errors.add(attribute, 'is frozen')
    end
    true
  end
end

Put the thing in app/validators/frozen_attribute_validator.rb. Use it like this:

class Horse < ActiveRecord::Base
  validates :user_id, :token, frozen_attribute: true, allow_blank: true