9 - Resque #271

(12/1/16)

Resque uses Redis

Need to add Resque tasks

in '/lib/tasks/resque.rake'

  require "resque/tasks"
  task "resque:setup" => :environment

This task can then be used by Resque workers by:
  $ rake resque:work QUEUE='*'

The highlighting can be run in the background by adding to a resque worker queue

Whatever we pass in to 'enqueue' is converted to JSON so that it can be stored in the Redis database

/app/controllers/snippets_controller.rb

def create
  ...
  Resque.enqueue(SnippetHighlighter, @snippet.id)
  ...
end

Then create a worker in the 'app' so it's automatically in Rails loadpath

/app/workers/snippet_highlighter.rb

class SnippetHighlighter
  @queue = :snippets_queue
  def self.perform(snippet_id)
    snippet = Snippet.find(snippet_id)
    uri = URI.parse('http://pygments.simplabs.com/')
    request = Net::HTTP.post_form(uri, {'lang' => snippet.language, 'code' => snippet.plain_code})
    snippet.update_attribute(:highlighted_code, request.body)
  end
end

Embed Resque web interface

GitHub uses Resque

10 - Turbolinks #390

(8/2/16)

Get AJAX functionality with just normal HTML

Gem included by default in Rails 4

Browser needs to support it

github.com/turbolinks/turbolinks

"Turbolinks 5 is a complete rewrite that adds support for iOS and Android hybrid applications."

Add to your JavaScript manifest file (app/assets/javascripts/application.js)

//= require turbolinks

Turbolinks then listens for 'click' events for each link on the page

Issues with existing JS functionality

/app/assets/javascripts/projects.js.coffee

jQuery ->
  $('.edit_task input[type=checkbox]').click ->
    $(this).parent('form').submit()

This uses 'jQuery' function to listen for the 'ready' event on the document

With Turbolinks this is only triggered for the initial page so navigating to the task's page will NOT trigger the 'ready' event

Performance increase metrics

github.com/steveklabnik/turbolinks_test

11 - RubyGems #384

(4/3/16)

Deciding which gem to use or build from scratch

www.ruby-toolbox.com/

see most Popular gems
see how Activity of gems

GitHub can also be used to see last commits dates and resolution of issues

Gemfile

'Gemfile.lock' used to stick to particular versions until "$ bundle update"
Use '~>' operator to prevent upgrading to incompatible gem versions

Find lines of code with 'Cloc'

$ brew install cloc
$ cd gem-name ; cloc app lib
Compare against $ cloc spec

Check source code

'lib/[gemname]' often first file loaded and shows Dependencies + Overview of structure
Railtie file (eg 'lib/devise/rails.rb') gives an idea what happens when gem loaded
Rails Engine has 'app' directory

12 - Tagging #382

(11/3/16)

'acts-as-taggable-on' gem

github.com/mbleigh/acts-as-taggable-on

$ rails g acts_as_taggable_on:migration
$ rake db:migrate

/app/models/article.rb:

class Article < ActiveRecord::Base
  attr_accessible :content, :name, :tag_list
  acts_as_taggable
end

'tag_list' attribute returns an array of strings

Now need to set up routes for tags

/config/routes.rb:

Blog::Application.routes.draw do
  get 'tags/:tag', to: 'articles#index', as: :tag

  resources :articles
  root to: 'articles#index'
end

Filter Article listing based on clicking tag link (created by 'raw article.tag_list.map …')

/app/controllers/articles_controller.rb:

def index
  if params[:tag]
    @articles = Article.tagged_with(params[:tag])
  else
    @articles = Article.all
  end
end

Tag cloud (each link's font proportional to popularity)

Gem supplies 'tag_cloud' method

/app/views/articles/index.html.erb:

<div id="tag_cloud">
  <% tag_cloud Article.tag_counts, %w{s m l} do |tag, css_class| %>
    <%= link_to tag.name, tag_path(tag.name), class: css_class %>
  <% end %>
</div>

github.com/mbleigh/acts-as-taggable-on/blob/master/lib/acts_as_taggable_on/tags_helper.rb

Implementing tagging from scratch

$ rails g model tag name
$ rails g model tagging tag:belongs_to article:belongs_to

/app/models/tag.rb:

class Tag < ActiveRecord::Base
  attr_accessible :name
  has_many :taggings
  has_many :articles, through: :taggings
end

/app/models/article.rb:

class Article < ActiveRecord::Base
  attr_accessible :content, :name, :tag_list
  has_many :taggings
  has_many :tags, through: :taggings

  def self.tagged_with(name)
    Tag.find_by_name!(name).articles
  end

  def self.tag_counts
    Tag.select("tags.*, count(taggings.tag_id) as count").
      joins(:taggings).group("taggings.tag_id")
  end

  def tag_list
    tags.map(&:name).join(", ")
  end

  def tag_list=(names)
    self.tags = names.split(",").map do |n|
      Tag.where(name: n.strip).first_or_create!
    end
  end
end

/app/views/articles/index.html.erb:

<%= raw article.tags.map(&:name).map { |t| link_to t, tag_path(t) }.join(', ') %>

/app/helpers/application_helper.rb:

module ApplicationHelper
  def tag_cloud(tags, classes)
    max = tags.sort_by(&:count).last
    tags.each do |tag|
      index = tag.count.to_f / max.count * (classes.size - 1)
      yield(tag, classes[index.round])
    end
  end
end

13 - Ransack #370

(16/4/16)

Plugin extensions

'search' method added to Product model by github.com/activerecord-hackery/ransack/blob/rails-4.2/lib/ransack/adapters/active_record/base.rb and github.com/activerecord-hackery/ransack/blob/rails-4.2/lib/ransack/adapters/active_record.rb

'q' method added by github.com/activerecord-hackery/ransack/blob/rails-4.2/lib/ransack/configuration.rb

Basic features using GET

Controller changes

/app/controllers/products_controller.rb

class ProductsController < ApplicationController
  def index
    # Originally "@products = Product.all"

    @search = Product.search(params[:q])
    @products = @search.result
  end
end

View changes

/app/views/products/index.html.erb

<%= search_form_for @search do |f| %>
  ...
<% end %>

Making results table headers into links that sort the results

/app/views/products/index.html.erb

<tr>
  <th><%= sort_link(@search, :name, "Product Name") %></th>
  <th><%= sort_link(@search, :released_on, "Release Date") %></th>
  <th><%= sort_link(@search, :price, "Price") %></th>
</tr>

Dynamic search form

/app/views/products/index.html.erb

<%= search_form_for @search do |f| %>
  <%= f.condition_fields do |c| %>
    ...
  <% end %>
<% end %>

/app/controllers/products_controller.rb

class ProductsController < ApplicationController
  def index
    ...
    @search.build_condition
  end  
end

Using this we can also select on associations (as part of the 'condition_fields'

<%= a.attribute_select associations: [:category] %>

Adding and removing conditions dynamically via JavaScript

Move conditions fields into partial

Create 'link_to_add_fields' in /app/helpers/application_helper.rb

module ApplicationHelper
  def link_to_add_fields(name, f, type)
    new_object = f.object.send "build_#{type}"
    id = "new_#{type}"
    fields = f.send("#{type}_fields", new_object, child_index: id) do |builder|
      render(type.to_s + "_fields", f: builder)
    end
    link_to(name, '#', class: "add_fields", data: {id: id, fields: fields.gsub("\n", "")})
  end
end

Alter /app/views/products/index.html.erb

<%= search_form_for @search do |f| %>
  <%= f.condition_fields do |c| %>
    <%= render "condition_fields", f: c%>
  <% end %>
  <p><%= link_to_add_fields "Add Conditions", f, :condition %>
  <div class="actions"><%= f.submit "Search" %></div>
<% end %>

/app/assets/javascripts/products.js.coffee

jQuery ->
  $('form').on 'click', '.remove_fields', (event) ->
    $(this).closest('.field').remove()
    event.preventDefault()

  $('form').on 'click', '.add_fields', (event) ->
    time = new Date().getTime()
    regexp = new RegExp($(this).data('id'), 'g')
    $(this).before($(this).data('fields').replace(regexp, time))
    event.preventDefault()

GET to POST

If too many conditions then hit limit of data sent over GET request so use POST instead

14 - Image Manipulation #374

(24/5/16)

www.imagemagick.org/script/command-line-tools.php

$ man ImageMagick

$ convert octocat.png -resize '70x70^' -gravity center -crop '70x70+0+0' -quantize GRAY -colors 256 -contrast source.png
$ composite stamp_overlay.png source.png source.png

'convert' not only processes existing images but can also create from scratch
$ convert -size 70x70 canvas:red colour.png

$ convert -size 70x70 canvas:red source.png -compose copy-opacity -composite stamp.png
$ convert -size 70x70 canvas:red \( source.png -negate \) -compose copy-opacity -composite stamp.png
$ convert -size 70x70 canvas:red \( octocat.png -resize '70x70^' -gravity center -crop '70x70+0+0' -quantize GRAY -colors 256 -contrast stamp_overlay.png -composite -negate \) -compose copy-opacity -composite stamp.png

Simple ruby script first, 'stamp.rb':

require "rmagick"

source = Magick::Image.read("octocat.png").first
source = source.resize_to_fill(70, 70).quantize(256, Magick::GRAYColorspace).contrast(true)
overlay = Magick::Image.read("stamp_overlay.png").first
source.composite!(overlay, 0, 0, Magick::OverCompositeOp)
colored = Magick::Image.new(70, 70) { self.background_color = "red" }
colored.composite!(source.negate, 0, 0, Magick::CopyOpacityCompositeOp)
colored.write("stamp.png")

or by using command line tools from ruby:

# Or through the command line:
system <<-COMMAND
convert -size 70x70 canvas:red \\( octocat.png \
  -resize '70x70^' -gravity center -crop '70x70+0+0' \
  -quantize GRAY -colors 256 -contrast stamp_overlay.png \
  -composite -negate \
\\) -compose copy-opacity -composite stamp.png
COMMAND

/app/models/stamp.rb

class Stamp < ActiveRecord::Base
  attr_accessible :image
  mount_uploader :image, StampUploader
end

/app/uploaders/stamp_uploader.rb

class StampUploader < CarrierWave::Uploader::Base
  # ...
  %w[red green blue purple black].each do |color|
    version(color) { process stamp: color }
  end

  def stamp(color)
    manipulate! format: "png" do |source|
      overlay_path = Rails.root.join("app/assets/images/stamp_overlay.png")
      overlay = Magick::Image.read(overlay_path).first
      source = source.resize_to_fill(70, 70).quantize(256, Magick::GRAYColorspace).contrast(true)
      source.composite!(overlay, 0, 0, Magick::OverCompositeOp)
      colored = Magick::Image.new(70, 70) { self.background_color = color }
      colored.composite(source.negate, 0, 0, Magick::CopyOpacityCompositeOp)
    end
  end
end

15 - FnordMetric #378

(25/5/16)

github.com/whitesmith/capistrano-recipes/blob/master/fnordmetric.rb

Add to 'cloudreach' app:

Uses Redis

16 - MiniProfiler #368

(3/6/16)

/Gemfile

gem 'rack-mini-profiler'

MiniProfiler enabled by default in 'development'

/app/views/projects/index.html.erb

<%= pluralize project.tasks.length, "task" %>
to
<%= pluralize project.tasks.size, "task" %>

To make it more efficient we need to write some SQL:

/app/controllers/projects_controller.rb

def index
  @projects = Project.order(:created_at).select("projects.*, count(tasks.id) as tasks_count").joins(
  "left outer join tasks on project_id = projects.id"
  ).group("projects.id")
end

Can measure specific piece of code with 'Rack::MiniProfiler.step':

/app/controllers/projects_controller.rb

def index
  @projects = Project.order(:created_at).select("projects.*, count(tasks.id) as tasks_count").joins("left outer join tasks on project_id = projects.id").group("projects.id")
  Rack::MiniProfiler.step("fetch projects") do
    @projects.all
  end
end

Profiling app in 'production' mode

/config/environments/production.rb

# Enable Rails's static asset server
config.serve_static_assets = true

Then:

$ rake assets:precompile
$ rake db:setup RAILS_ENV=production
$ rails s -e production

Show MiniProfiler in Production mode

/app/controllers/application_controller.rb

class ApplicationController < ActionController::Base
  protect_from_forgery

  before_filter :miniprofiler

  private
  def miniprofiler
    Rack::MiniProfiler.authorize_request
  end
end

samsaffron.com/archive/2012/07/12/miniprofiler-ruby-edition

github.com/SamSaffron/MiniProfiler/blob/master/Ruby/README.md

17 - Twitter Bootstrap Basics #328

(16/8/16)

One option is to download from getbootstrap.com/ + move files into appropriate places in filesystem

…but better if using Rails to use a gem (because Bootstrap uses Less)

twitter-bootstrap-rails (github.com/seyhunak/twitter-bootstrap-rails)

Works directly with Less
Has generators built in

$ rails g bootstrap:install

Layouts

Bootstrap 3

Railscast uses 'twitter-bootstrap-rails (2.0.3)'

(as seen from 'Gemfile.lock' in 'ryanb-railscasts-episodes-f72b83a.tar.gz')

blog.jetstrap.com/2013/08/bootstrap-3-grids-explained/

twitter-bootstrap-rails generators

Themed generators to improve the look of scaffold generated code view

www.sitepoint.com/twitter-bootstrap-less-and-sass-understanding-your-options-for-rails-3-1/