Table of Contents
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
http://ruby-doc.org/stdlib-2.0.0/libdoc/syslog/rdoc/Syslog/Logger.html
$ gnome-system-log
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
- Go to: http://www.imagemagick.org/download/delegates/ and download the required/missing delegate library.
- Execute “gunzip libjpeg-6b.tar.gz”
- Execute “tar -xvf libjpeg-6b.tar”
- Change directories to the newly created “libjpeg-x”
- Execute ”./configure”
- Execute “make”
- Execute “make test”
- Execute “make -n install” first to see if the makefile will put the files where you want them.
- If there are no errors and you're ok with the installation path go ahead and install with “make install”
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
- PostgreSQL saves in timestamp UTC.
- No Time Zone configured in app.
- Helper to localize date
- The views translates to user's time zone.
- The entry of time info is compensated by the time zone offset.
- 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
Fuente: http://guides.rubyonrails.org/action_mailer_basics.html
Preview mails: http://localhost:3000/rails/mailers/user_notifier/new_registration_mail_preview
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']}