13 July 2015

Linux development environment

See: Linux

Logging strategy

Source: http://www.railsonmaui.com/blog/2013/05/08/strategies-for-rails-logging-and-error-handling/

https://github.com/gshutler/browser_details# Browser Details is a Rack Middleware that logs information about the browser used to make a request.

gem 'browser_details'

https://github.com/roidrage/lograge# Lograge is an attempt to bring sanity to Rails' noisy and unusable, unparsable and, in the context of running multiple processes and servers, unreadable default logging output

gem "lograge"

More verbose logging: http://log4r.rubyforge.org/index.html

Source: https://pooreffort.com/blog/better-rails-logging/

https://github.com/evrone/quiet_assets
# Mutes assets pipeline log messages.group :development do
  gem 'quiet_assets'
end

Syslog::Logger

process logging

tail -f log/development.log

Creating Your Own Rails API Documentation

You can create your own local version of the consolidated Rails API documen-tation. Just type the following commands at a command prompt:

rails_apps> rails new dummy_app
rails_apps> cd dummy_app
dummy_app> rake doc:rails

The last step takes a while. When it finishes, you’ll have the Rails API documen-tation in a directory tree starting at doc/api.
We suggest moving this folder to your desktop and then deleting the dummy_app tree.
To view the Rails API documentation, open the location doc/api/index.html with your browser.

Column names issues

end is a reserved word in Oracle. Use end_at instead Workaround: quote it when used in queris.

Ex: @activities = Activity.where(“start >= '” + params[:start] + ”' AND 'end' < = '” + params[:end] + ”'”)

Javascript runtime

https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager#debian-and-ubuntu-based-linux-distributions Setup with Debian (as root):

apt-get install curl
curl -sL https://deb.nodesource.com/setup | bash -

Then install with Debian (as root):

apt-get install -y nodejs

node -v

Bower: frontend package manager

# npm install bower -g as su
$ bower install <package>
registered package
$ bower install jquery
GitHub shorthand
$ bower install desandro/masonry
Git endpoint
$ bower install git:github.com/user/package.git
URL
$ bower install http://example.com/script.js

AngularJS tutorial

AngularJS with Ruby on Rails

http://angular-rails.com/bootstrap.html

After installing bower run bower init to generate the file bower.json

RSpec

https://github.com/rspec/rspec-rails

Add rspec-rails to both the :development and :test groups in the Gemfile:
group :development, :test do
gem 'rspec-rails', '~> 3.0'
end

Download and install by running:

bundle install

Initialize the spec/ directory (where specs will reside) with:

rails generate rspec:install

Generators

Model

rails g model project name:string description:string

Migration to create belongs_to association

Order belongs_to :User
User has_many :orders

rails g migration AddUserToOrder user:references

Generate the following code

class AddUserToOrder < ActiveRecord::Migration
  def change
    add_reference :orders, :user, index: true
  end
end

Adds the following to the orders database table

t.integer  "user_id"
add_index "orders", ["user_id"], name: "index_orders_on_user_id", using: :btree

Migration to create index for uniquenes validation

rails generate model user email:string:uniq

Or

class AddEmailIndexToUser
  def change
    # If you already have non-unique index on email, you will need
    # to remove it before you're able to add the unique index.
    add_index :users, :email, unique: true
  end
end

Image uploader

Rails gem
https://github.com/carrierwaveuploader/carrierwave

Application to manipulate the image, ex, resize it to thumbnails:
http://www.imagemagick.org/
ImageMagick® is a software suite to create, edit, compose, or convert bitmap images. It can read and write images in a variety of formats (over 100) including DPX, EXR, GIF, JPEG, JPEG-2000, PDF, PNG, Postscript, SVG, and TIFF. Use ImageMagick to resize, flip, mirror, rotate, distort, shear and transform images, adjust image colors, apply various special effects, or draw text, lines, polygons, ellipses and Bézier curves.

The functionality of ImageMagick is typically utilized from the command line or you can use the features from programs written in your favorite language. Choose from these interfaces: G2F (Ada), MagickCore (C), MagickWand (C), ChMagick (Ch), ImageMagickObject (COM+), Magick++ (C++), JMagick (Java), L-Magick (Lisp), Lua, NMagick (Neko/haXe), Magick.NET (.NET), PascalMagick (Pascal), PerlMagick (Perl), MagickWand for PHP (PHP), IMagick (PHP), PythonMagick (Python), RMagick (Ruby), or TclMagick (Tcl/TK). With a language interface, use ImageMagick to modify or create images dynamically and automagically.

Install from source:
http://www.imagemagick.org/script/install-source.php

$ tar xvzf ImageMagick.tar.gz
$ cd ImageMagick-6.9.0
$ ./configure
$ make
$ sudo make install
$ sudo ldconfig /usr/local/lib
$ make check

Problem with decoder: try http://blog.ericlamb.net/2008/11/fix-for-convert-no-decode-delegate-for-this-image-format/

Recomended:
apt-get install imagemagick

Decode error

  1. Go to: http://www.imagemagick.org/download/delegates/ and download the required/missing delegate library.
  2. Execute “gunzip libjpeg-6b.tar.gz”
  3. Execute “tar -xvf libjpeg-6b.tar”
  4. Change directories to the newly created “libjpeg-x”
  5. Execute ”./configure”
  6. Execute “make”
  7. Execute “make test”
  8. Execute “make -n install” first to see if the makefile will put the files where you want them.
  9. If there are no errors and you're ok with the installation path go ahead and install with “make install”

See also: http://stackoverflow.com/questions/10810356/carrierwave-error-msg-failed-to-manipulate-with-minimagick-maybe-it-is-not-an

Rename the application

Since rails 4.1.x, if you want to rename your application, the only two files you need to modify are config/application.rb:

require File.expand_path('../boot', __FILE__)

require 'rails/all'

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module YourApplicationName # <-- rename it here
   class Application < Rails::Application
     ...
   end
end

and config/initializers/session_store.rb (optional):

# Be sure to restart your server when you modify this file.

Rails.application.config.session_store :cookie_store, key: '_your_application_name__session'

POST via JSON

You'll need to set the following headers in your post.

Content-Type: application/json
Accept: application/json

A 422 means Unprocessable Entity — generally that there was a validation failure.

ApplicationControler.rb

Source: http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection.html

class ApplicationController < ActionController::Base
  protect_from_forgery
  skip_before_action :verify_authenticity_token, if: :json_request?

  protected

  def json_request?
    request.format.json?
  end
end

Expose only some fields when rendering JSON

In the user model:

  def to_json(options={})
    options[:except] || [:crypted_password]
  end

In the controller:

respond_to do |format|
  format.json { render json: @user, except: [:crypted_password] }
end

Redirect back

In the partial view:

<%= hidden_field_tag :referer, request.referer %>

In the controller:

redirect_to params[:referer]

Form helpers

Collection_select

Api:

collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {}) public 

To render a select combo with gates names and return the selection for the current loop (id=3):

<%= form_tag loop_add_gate_path do  %>
  <%= collection_select :gate, :id, Gate.all, :id, :name, include_blank: true %>
  <%= submit_tag %>
<% end %>

Params received in controller:

{..., "gate"=>{"id"=>"2"}, "commit"=>"Save changes", "controller"=>"loops", "action"=>"add_gate", "id"=>"3"}

Inside a form_for

<%= f.collection_select :spacetype_id, Spacetype.all, :id, :name, include_bank: true %>

Form_for with hidden fields

<% form_for(@post) do |f| %>
  <%= f.hidden_field :user_id, { :value => user.id } %>
<% end %>

Page 404

TimeZone <<<<<<<<<<<<<<< not recomended >>>>>>>>>>>>>>>>>

This aproach gave problems of discoordination and it's much more difficult to control

  1. PostgreSQL saves in timestamp UTC.
  2. No Time Zone configured in app.
  3. Helper to localize date
  4. The views translates to user's time zone.
  5. The entry of time info is compensated by the time zone offset.
  6. Queries with time span: compensate to match utc time.
# 2 application.rb
# config.time_zone = 'Madrid' >> the app works in UTC zone

# 3 application_helper.rb
def localize_date(date)
  I18n.l date.in_time_zone('Madrid'), format: "%d/%m/%Y %a, %H:%M" if date
end

# 4 activities/index.html.erb
<%= localize_date activity.start %>


# 5 applicationController.rb
def offset_utc
  tz = ActiveSupport::TimeZone.new('Madrid')
  tz.utc_offset / 3600
end
  
# AtivityController.rb
start_compensated_to_utc = activity_params[:start].to_datetime - offset_utc.hours
@activity = Activity.new(activity_params.merge(user_id: current_user.id).merge(start: start_compensated_to_utc))

# activities/_form.html.erb
<div class="field">
  <%= f.label t :start %>
  <%= f.text_field :start, value: localize_date(@activity.start) %>
</div>

# 6 reportsController.rb
@activities = Activity.joins(:project).select('projects.name').where("start >= to_timestamp('#{params[:start]}', 'DD/MM/YYYY') - interval '#{offset_utc} hours'").where("start <= to_timestamp('#{params[:ended]} 23:59:59', 'DD/MM/YYYY HH24:MI:SS') - interval '#{offset_utc} hours'").group(:name).sum(:duration)

# in postgreSQL this is valid
select * from activities where start >= to_timestamp('02/02/2015','DD/MM/YYYY') - interval '1 hour';

TimeZone

«««««««««< in progress »»»»»»»»>

# application.rb
config.time_zone = 'Madrid'
<code>
<code>
#config/initializers/time_formats.rb
Date::DATE_FORMATS[:default] = "%m/%d/%Y %H:%M"
Time::DATE_FORMATS[:default] = "%m/%d/%Y %H:%M"
# _form.html.erb
<div class="field">
    <%= f.label t :start %>
    <%= f.text_field :start, value: localize_date(@activity.start) %>
  </div>

  <% if @activity.start %>
    <div class="field">
      <%= f.label t :ended %>
      <%= f.text_field :ended, value: localize_date(@activity.ended) %>
    </div>
# index.html.erb
<td><%= localize_date activity.start %></td>
<td><%= localize_date activity.ended %></td>

Scopes

Convert string to date to order registers

check_in is the date field in string format

scope :by_date, -> { order("cast(check_in as date)") }

Passing parameters

scope :for, -> ( date ) { where("start <= to_timestamp('#{date}', 'DD/MM/YYYY')").where("finish >= to_timestamp('#{date}', 'DD/MM/YYYY')") }
scope :batch_images, ->(batch) { where("IMG_BATCH = ?", batch.batch_id) }

Using Pry as rails console

# application.rb

    config.console do
      require 'pry'
      config.console = Pry
    end

Conditional JS

# application.html
  <%= yield :js if content_for?(:js) %>

# some view
<% content_for :js do %>
  <%= javascript_include_tag 'worker', 'data-turbolinks-track' => true %>
  <script>
    worker();
  </script>
<% end %>

# worker.js
some ajax function

Before_filter customized

login required except for index and json request.

before_filter :login_required, except: :index
before_filter(only: :index) do |controller|
  controller.send(:login_required) unless controller.request.format.json?
end

Export data in seeds format

Create rake task (replace Faq with the model name) It generates db/seeds.rb (by default) Example: shk/faqs Model: Faq

# USAGE: rake export:seeds_format > db/seeds.rb
namespace :export do
  desc "Prints Faq.all in a seeds.rb way."
  task :seeds_format => :environment do
    Faq.order(:id).all.each do |faq|
      puts "Faq.create(#{faq.serializable_hash.delete_if {|key, value| ['created_at','updated_at','id'].include?(key)}.to_s.gsub(/[{}]/,'')})"
    end
  end
end
rake export:seeds_format > db/seeds.rb

I18n automatic humanize translations

To avoid the humanization

<%= f.label(:vat, t(:vat)) %>
es: 
  vat: IVA

Generators options

Models

$ rails g model user email age:integer
$ rails g model admin --parent user
$ rails g model admin/user
$ rails g model user email:index location_id:integer:index
$ rails g model user pseudo:string:uniq
$ rails generate model user pseudo:string{30}
$ rails generate model product 'price:decimal{10,2}'
$ rails generate model user username:string{30}:uniq
$ rails generate model photo album:references
$ rails generate model product supplier:references{polymorphic}
$ rails generate model product supplier:references{polymorphic}:index

Types

integer
primary_key
decimal
float
boolean
binary
string
text
date
time
datetime
timestamp

Enums in models

I18n helpers

#fr.yml

helpers:
  submit:
    # This will be the default ones, will take effect if no other
    # are specifically defined for the models.
    create: "Créer"
    update: "Modifier"

    # Those will however take effect for all the other models below
    # for which we define a specific label.
    user:
      create: "Créer mon compte"
      update: "Mettre à jour mon compte"
    product:
      create: "Déposer l'objet"
      update: "Modifier l'objet"
    session:
      create: "Se connecter"

After that, you only need to define your submit button like this:

<%= f.submit class: 'any class you cant to apply' %>

Let nginx serve public files directly

Add to production.rb

# send public files directly, no passing through whole rails rack
# config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx

Limit logger file size

# production.rb / test.rb

# Limit your development log file
config.logger = Logger.new(config.paths["log"].first, 1, 5242880) # 5 megabytes
config.logger = Logger.new(config.paths["log"].first, 1, 10485760) # 10 megabytes

clickable label in checkboxes

        <div class="form-group wide-row">
          <% Spacetype.all.each do |t| %>
          <span class="find-spaces-checkbox-spacetypes">
            <%= check_box_tag "spacetypes[#{t.id}]", t.id, true %>
            <%= label_tag "spacetypes[#{t.id}]", t.name %>
          </span>
          <% end %>
        </div>

o también

          <% Activity.all.each do |act| %>
          <span class="find-spaces-checkbox-spacetypes">
            <label><%= check_box_tag "activities[#{act.id}]", act.id, checked: true %><%= act.name %></label>
          </span>
          <% end %>

.wide-row { line-height: 4 !important; } .find-spaces-checkbox-spacetypes, .find-spaces-quantity {

border: 1px solid $brand-primary;
background-color: white;
padding: 1em;
label { font-weight: normal; }

}

# controller @spaces = Space.all.where(spacetype_id: params[“spacetypes”].values)

query with HABTM association:

class Space
  has_one :activities_set, dependent: :destroy
  accepts_nested_attributes_for :activities_set, allow_destroy: true
  has_many :activities, through: :activities_set
class Activity < ActiveRecord::Base
  has_and_belongs_to_many :activities_set
class ActivitiesSet < ActiveRecord::Base
  belongs_to :space
  has_and_belongs_to_many :activities

with a query form like this:

          <% Activity.all.each do |act| %>
          <span class="find-spaces-checkbox">
            <label>
              <%= check_box_tag "activities[#{act.id}]", act.id, checked: true %>
              <%= act.name %>
            </label>
          </span>
          <% end %>

# controller

@spaces = Space.all.includes(:activities).where(activities: { id: params["activities"].values})

query comparing with nil

field IS nil
where("capacity >= ? OR capacity = ? OR capacity IS ?", people, 0, nil)

Redondear a 2 decimales

'%.2f' % number

ej:

('%.2f' % 123.436).to_f * 100 => 12344.0
sprintf "%.2f", 500.0 => 500.00

Sending mail from Rails

model --> set default values

after_initialize :defaults, unless: :persisted?
            # ":if => :new_record?" is equivalent in this context
def defaults
  self.extras||={} if self.has_attribute? :extras
  self.other_stuff||="This stuff" if self.has_attribute? :other_stuff
  self.assoc = [OtherModel.find_by_name('special')] if self.has_attribute? :assoc
  self.bool_field = true if (self.has_attribute? :bool_field) && self.bool_field.nil?end

Add helpers methods to your Rails console

RAILS APPS OPTIMIZATION

SQL query in Rails

ActiveRecord::Base.connection.execute("select * from coupons").each{|c| puts c['code']}