Wednesday, November 30, 2016

Building iOS Map Applications in Swift 3

Building iOS Map Applications in Swift 3
When using Mapkit in Xcode, all we need is to do configurations in the following steps:

1. Enable Maps capability

project > capabilities > Maps > on

2. Adding import libraries

Add these two lines in ViewController.swift
import CoreLocation
import MapKit

3. Adding keys in info.plist

NSLocationWhenInUseUsageDescription : 需要時取得使⽤用者位置
NSLocationAlwaysUsageDescription : 不斷的取得位置

4. Create a Location.gpx if using simulator and customizing default location


Setting the default location:
product > scheme > edit scheme
scheme

Thursday, October 13, 2016

Using Gem 'wicked_pdf' to Generate PDF FIle in Rails

Using Gem 'wicked_pdf' to Generate PDF FIle in Rails
Ok, it ‘s turn for PDF file in Rails.
The wicked_pdf is the best gem I have seen for generating pdf file. It is powerful, versatile and flexible. It also provides the capability of generate PDF from HTML file. This is just what I want.
Thewicked_pdf utilizing the shell utility wkhtmltopdf to serve a PDF file to a user from HTML.
There are also many options which provides huge flexibility to users to customize the output PDF file.

Installation

put it in Gemfile
gem 'wicked_pdf'
Likewise, bundle install. Then, execute the initializer.
$rails generate wicked_pdf
It will create app/config/initializers/wicked_pdf.rb. Normally, nothing has to do with it if higher version of Rails was used. (My Rails version is 5)
Because wicked_pdf is a wrapper for wkhtmltopdf, you’ll need to install that in Gemfile, too.
gem 'wkhtmltopdf-binary'
Of course, then bundle install.

Usage

1.Put the code in the response_tosection of the controller. Like:
def show
  @orderdetails = @order.orderdetails.includes(:model, :size, :color)
  respond_to do |format|
    fn = "order_#{@order.po_number}_#{Time.now.strftime("%Y-%m-%d %H:%M:%S")}"
    format.pdf { render pdf: fn, disposition: 'attachment'}
  end
end
2.Copy the show.html.erb to a new file and rename it as show.pdf.erb.
3. Remove all the link_to tags in the file to prevent them from showing in the pdf file.
It’s done!
Further reading: How To Create PDFs in Rails

Using Gem 'to_spreadsheet' to Generate XLSX FIle in Rails

Using Gem 'to_spreadsheet' to Generate XLSX FIle in Rails

Now, let’s find out how to generate Excel file in Rails.

After searching many gems, I found that axlsx is a very well-designed for my purpose. However, it generates xlsx file by building up the components piece by piece. I need a quick solution which can generates a xlsx file from html file. Then I found a gem called to_spreadsheet that is totally full-filled my needs!

Actually, to_spreadsheet is a wrapper of axlsx. It simplifies the usage and allows us using the existing view template to generate xlsx file.

Installation

Put it in Gemfile.

gem 'to_spreadsheet'

Likewise, bundle install.

Usage

1.Put the code in the response_tosection of the controller. Like:

def show
  @orderdetails = @order.orderdetails.includes(:model, :size, :color)
    respond_to do |format|
    fn = "order_#{@order.po_number}_#{Time.now.strftime("%Y-%m-%d %H:%M:%S")}"

    format.xlsx { render xlsx: :show, filename: fn }
  end
end

2.Copy the show.html.erb to a new file and rename it as show.xlsx.erb.
3. Remove all the link_to tags in the file to prevent them from showing in the xlsx file.
4. Try to put the data you need to show in <table></table> block.

Done!

Wednesday, October 12, 2016

Using Service Objects in Rails

Using Service Objects in Rails

If you have many lines of code in your Models or Controllers, you might need to extract the logics out of them and wrap them up to Service Objects

They may be:

  • A bunch of code called by many controllers.
  • Dealing with output or input.
  • A specific part of code containing domain knowledge.

Let us take the CSV file generation of the order details for example.

Create a New Directory ‘app/services’

First, let’s create a new directory ‘services’ under ‘app/’.

$cd app
$mkdir services
$cd services

Create a New File ‘app/services/document_service.rb’

Second, add a new file called ‘document_service.rb’.

Create a New Service Object called ‘DocumentService’

Third, move the code in private action query_to_csv of the orders_controller to the new service object. Now, we call this service object as DocumentService.

class DocumentService
  def initialize(order_id)
    @order_id = order_id
  end

  # Use ActiveRecord::Base.connection.exec_query to query db directly only once.
  def to_csv
    query_string = 'select a.po_number as "PO Number", c.name as "Model Name", d.name as Color,
        e.name as Size, b.price as Price, b.quantity as Quantity,
        (b.price*b.quantity) as "Total Amount"
      from orders a, orderdetails b, models c, colors d, sizes e
      where a.id = b.order_id and b.model_id=c.id and b.color_id=d.id and b.size_id=e.id and b.order_id=' + @order_id.to_s
    results = ActiveRecord::Base.connection.exec_query(query_string)
    CSV.generate(headers: true) do |csv|
      csv << results.columns
      results.rows.each do |row|
        csv << row
      end
    end
  end
end

Modify The Show Action

Four, modify the show action and delete private action query_to_csv.

def show
   :
   :
  format.csv { send_data DocumentService.new(@order.id).to_csv, filename: fn }
  end
end

That’s all! Now the order controller looks nicer and cleaner.

Further readings:

Monday, October 10, 2016

The "Includes" Magic in Rails

The "Includes" Magic in Rails
If there are many associated models needed to be retrieved during a query, it’s time to use ‘includes’ instead of nothing. It will increase your apps performance by reducing the number of queries to database.
Here is the scenario:
Order has_many Orderdetails

Without ‘includes’

The code:
@orderdetails = @order.orderdetails
It generates the log below.
Started GET “/orders/1” for ::1 at 2016-10-10 04:09:07 +0800
Processing by OrdersController#show as HTML
Parameters: {“id”=>”1”}
Order Load (0.3ms) SELECT “orders”.* FROM “orders” WHERE “orders”.”id” = ? LIMIT ? [[“id”, 1], [“LIMIT”, 1]]
Rendering orders/show.html.erb within layouts/application
Orderdetail Load (0.6ms) SELECT “orderdetails”.* FROM “orderdetails” WHERE “orderdetails”.”order_id” = ? ORDER BY model_id, size_id ASC [[“order_id”, 1]]
Model Load (0.4ms) SELECT “models”.* FROM “models” WHERE “models”.”id” = ? LIMIT ? [[“id”, 1], [“LIMIT”, 1]]
Size Load (0.4ms) SELECT “sizes”.* FROM “sizes” WHERE “sizes”.”id” = ? ORDER BY name ASC LIMIT ? [[“id”, 1], [“LIMIT”, 1]]
Color Load (0.6ms) SELECT “colors”.* FROM “colors” WHERE “colors”.”id” = ? ORDER BY name ASC LIMIT ? [[“id”, 4], [“LIMIT”, 1]]
CACHE (0.0ms) SELECT “models”.* FROM “models” WHERE “models”.”id” = ? LIMIT ? [[“id”, 1], [“LIMIT”, 1]]
CACHE (0.0ms) SELECT “sizes”.* FROM “sizes” WHERE “sizes”.”id” = ? ORDER BY name ASC LIMIT ? [[“id”, 1], [“LIMIT”, 1]]
Color Load (0.2ms) SELECT “colors”.* FROM “colors” WHERE “colors”.”id” = ? ORDER BY name ASC LIMIT ? [[“id”, 2], [“LIMIT”, 1]]
CACHE (0.0ms) SELECT “models”.* FROM “models” WHERE “models”.”id” = ? LIMIT ? [[“id”, 1], [“LIMIT”, 1]]
Size Load (0.1ms) SELECT “sizes”.* FROM “sizes” WHERE “sizes”.”id” = ? ORDER BY name ASC LIMIT ? [[“id”, 2], [“LIMIT”, 1]]
CACHE (0.0ms) SELECT “colors”.* FROM “colors” WHERE “colors”.”id” = ? ORDER BY name ASC LIMIT ? [[“id”, 4], [“LIMIT”, 1]]
CACHE (0.0ms) SELECT “models”.* FROM “models” WHERE “models”.”id” = ? LIMIT ? [[“id”, 1], [“LIMIT”, 1]]
CACHE (0.0ms) SELECT “sizes”.* FROM “sizes” WHERE “sizes”.”id” = ? ORDER BY name ASC LIMIT ? [[“id”, 2], [“LIMIT”, 1]]
CACHE (0.0ms) SELECT “colors”.* FROM “colors” WHERE “colors”.”id” = ? ORDER BY name ASC LIMIT ? [[“id”, 2], [“LIMIT”, 1]]
Rendered orders/show.html.erb within layouts/application (105.4ms)
Rendered common/_navbar.html.erb (1.1ms)
Completed 200 OK in 275ms (Views: 236.7ms | ActiveRecord: 5.1ms)
It takes 14 queries and 5.1ms.

With ‘includes’

The code:
@orderdetails = @order.orderdetails.includes(:model, :size, :color)
It generates the log below.
Started GET “/orders/1” for ::1 at 2016-10-10 14:26:43 +0800
ActiveRecord::SchemaMigration Load (0.2ms) SELECT “schema_migrations”.* FROM “schema_migrations”
Processing by OrdersController#show as HTML
Parameters: {“id”=>”1”}
Order Load (0.2ms) SELECT “orders”.* FROM “orders” WHERE “orders”.”id” = ? LIMIT ? [[“id”, 1], [“LIMIT”, 1]]
Rendering orders/show.html.erb within layouts/application
Orderdetail Load (0.2ms) SELECT “orderdetails”.* FROM “orderdetails” WHERE “orderdetails”.”order_id” = ? ORDER BY model_id, size_id ASC [[“order_id”, 1]]
Model Load (0.1ms) SELECT “models”.* FROM “models” WHERE “models”.”id” = 1
Size Load (0.2ms) SELECT “sizes”.* FROM “sizes” WHERE “sizes”.”id” IN (1, 2) ORDER BY name ASC
Color Load (0.2ms) SELECT “colors”.* FROM “colors” WHERE “colors”.”id” IN (4, 2) ORDER BY name ASC
Orderdetail Load (0.2ms) SELECT “orderdetails”.* FROM “orderdetails” WHERE “orderdetails”.”order_id” = ? ORDER BY model_id, size_id ASC [[“order_id”, 1]]
Rendered orders/show.html.erb within layouts/application (84.2ms)
Rendered common/_navbar.html.erb (1.9ms)
Completed 200 OK in 617ms (Views: 560.0ms | ActiveRecord: 3.4ms)
It takes 6 queries and 3.4ms.
The includes method can minimize the queries to the database. Rails generates a single query each time it need the associated model by default. The ‘includes’ method, however, aggregates the queries to the same associated model .
For example, If there are four order details belong to order id ‘1’, Rails generates 12 ( 4 records x 3 associated models ) queries by default. On the other hand, it only takes 3 queries by using the ‘includes’ method.

Conclusion

Add ‘includes’ method for the query with many associated models.

SQL Statement with Single or Double Quotes for PostgerSQL ?

SQL Statement with Single or Double Quotes for PostgerSQL ?

Single Quotes or Double Quotes? That is a Question!

Consider a sql statement:

SELECT model AS 'Model Name' FROM orders

We usually write it in the format below in Ruby on Rails:

query_string = "SELECT model AS 'Model Name' FROM orders"

It works fine for SQLite and MySQL, but fails for PostgreSQL!

The root cause is that PostgreSQL only accepts double-quoted key words!

Solution: use double-quotes instead of single-quotes:

SELECT model AS "Model Name" FROM orders

In Rails, it should be:

query_string = 'SELECT model AS "Model Name" FROM orders'

Note that it turns to be single quotes for wrapping the whole query string.

Sunday, October 9, 2016

Using Ruby CSV Library to Export CSV File in Rails Apps - send_data method and 'exec_query' Approach

Using Ruby CSV Library to Export CSV File in Rails Apps - send_data method and 'exec_query' Approach

N+1 Queries Problem

Since there are 4 relations (order, model, color, size) for model Orderdetail, it causes ‘N+1 Query’ problems for this scenario. Let’s take a look at the console log:
Started GET “/orders/1.csv” for ::1 at 2016-10-08 12:54:14 +0800
ActiveRecord::SchemaMigration Load (0.2ms) SELECT “schema_migrations”.* FROM “schema_migrations”
Processing by OrdersController#show as CSV
Parameters: {“id”=>”1”}
Order Load (0.2ms) SELECT “orders”.* FROM “orders” WHERE “orders”.”id” = ? LIMIT ? [[“id”, 1], [“LIMIT”, 1]]
Orderdetail Load (0.2ms) SELECT “orderdetails”.* FROM “orderdetails” WHERE “orderdetails”.”order_id” = ? ORDER BY model_id, size_id ASC [[“order_id”, 1]]
Order Load (0.1ms) SELECT “orders”.* FROM “orders” WHERE “orders”.”id” = ? LIMIT ? [[“id”, 1], [“LIMIT”, 1]]
Model Load (0.1ms) SELECT “models”.* FROM “models” WHERE “models”.”id” = ? LIMIT ? [[“id”, 1], [“LIMIT”, 1]]
Size Load (0.1ms) SELECT “sizes”.* FROM “sizes” WHERE “sizes”.”id” = ? ORDER BY name ASC LIMIT ? [[“id”, 1], [“LIMIT”, 1]]
Color Load (0.3ms) SELECT “colors”.* FROM “colors” WHERE “colors”.”id” = ? ORDER BY name ASC LIMIT ? [[“id”, 4], [“LIMIT”, 1]]
Order Load (0.1ms) SELECT “orders”.* FROM “orders” WHERE “orders”.”id” = ? LIMIT ? [[“id”, 1], [“LIMIT”, 1]]
Model Load (0.1ms) SELECT “models”.* FROM “models” WHERE “models”.”id” = ? LIMIT ? [[“id”, 1], [“LIMIT”, 1]]
Size Load (0.1ms) SELECT “sizes”.* FROM “sizes” WHERE “sizes”.”id” = ? ORDER BY name ASC LIMIT ? [[“id”, 1], [“LIMIT”, 1]]
Color Load (0.1ms) SELECT “colors”.* FROM “colors” WHERE “colors”.”id” = ? ORDER BY name ASC LIMIT ? [[“id”, 2], [“LIMIT”, 1]]
Order Load (0.1ms) SELECT “orders”.* FROM “orders” WHERE “orders”.”id” = ? LIMIT ? [[“id”, 1], [“LIMIT”, 1]]
Model Load (0.1ms) SELECT “models”.* FROM “models” WHERE “models”.”id” = ? LIMIT ? [[“id”, 1], [“LIMIT”, 1]]
Size Load (0.1ms) SELECT “sizes”.* FROM “sizes” WHERE “sizes”.”id” = ? ORDER BY name ASC LIMIT ? [[“id”, 2], [“LIMIT”, 1]]
Color Load (0.2ms) SELECT “colors”.* FROM “colors” WHERE “colors”.”id” = ? ORDER BY name ASC LIMIT ? [[“id”, 4], [“LIMIT”, 1]]
Order Load (0.1ms) SELECT “orders”.* FROM “orders” WHERE “orders”.”id” = ? LIMIT ? [[“id”, 1], [“LIMIT”, 1]]
Model Load (0.1ms) SELECT “models”.* FROM “models” WHERE “models”.”id” = ? LIMIT ? [[“id”, 1], [“LIMIT”, 1]]
Size Load (0.1ms) SELECT “sizes”.* FROM “sizes” WHERE “sizes”.”id” = ? ORDER BY name ASC LIMIT ? [[“id”, 2], [“LIMIT”, 1]]
Color Load (0.1ms) SELECT “colors”.* FROM “colors” WHERE “colors”.”id” = ? ORDER BY name ASC LIMIT ? [[“id”, 2], [“LIMIT”, 1]]
Rendering text template
Rendered text template (0.0ms)
Sent data order_1234_2016-10-08 12:54:14.csv (14.0ms)
Completed 200 OK in 149ms (Views: 13.8ms | ActiveRecord: 4.7ms)
There are four order details for order 1, it takes 19 queries for 4.7ms. We can conclude that:
query number = ( number of order details X 4 ) + 3
Thus, the more order details contained in one order, the more query time used.

Direct SQL Query

Is there a way to solve this problem? What if we just use one single query to get all the data we need? Here is my plan. I wrote a single SQL statement which can pull out all the data from the database:
select a.po_number as ‘PO Number’, c.name as ‘Model Name’,
d.name as ‘Color’, e.name as ‘Size’, b.price as ‘Price’, b.quantity as Quantity (b.price*b.quantity) as ‘Total Amount’
from orders a, orderdetails b, models c, colors d, sizes e
where a.id = b.order_id and b.model_id=c.id and b.color_id=d.id and b.size_id=e.id and b.order_id=a.id
I tested it through a standalone database client and it returned the correct result. Then, I called ‘“exec_query” method to do the one-query action in the orders controller. The class used is:
ActiveRecord::Base.connection.exec_query([query string])
The result set is an ActiveRecord::Result object and very easy to handle with. The header and values of the result set can be obtained by using the following methods:
# Get the column names of the result:
result.columns
# => ["id", "model", "color"]

# Get the record values of the result:
result.rows
# => [[1, "model_1", "color_1"],
      [2, "model_2", "color_2"],
      ...
     ]
All we need to do is to save the header and the values to the csv string. All these code was wrapped into a private action query_to_csv(). In action show, the csv string is passed into send_data which outputs the result to the file in csv format.
format.csv { send_data query_to_csv(@order.id), filename: fn }
It’s quite simple. Here is the code in the controller.
app/controllers/orders_controller.rb
class OrdersController < ApplicationController
    :
    :
  def show
    @orderdetails = @order.orderdetails
    add_breadcrumb @order.id, order_path

    respond_to do |format|
      format.html
      format.json
      fn = "order_#{@order.po_number}_#{Time.now.strftime("%Y-%m-%d %H:%M:%S")}.csv"
      # Send_data method and exec_query approach
      format.csv { send_data query_to_csv(@order.id), filename: fn }
    end
  end   

  private

  # Use ActiveRecord::Base.connection.exec_query to 
  # query db directly only once.
  def query_to_csv(order_id)
    query_string = 'select a.po_number as "PO Number", 
        c.name as "Model Name", d.name as Color,
        e.name as Size, b.price as Price, 
        b.quantity as Quantity,
        (b.price*b.quantity) as "Total Amount"
      from orders a, orderdetails b, models c, 
        colors d, sizes e
      where a.id = b.order_id and b.model_id=c.id and
        b.color_id=d.id and b.size_id=e.id and
        b.order_id=' + order_id.to_s
    results = ActiveRecord::Base.connection.exec_query(query_string)       
    CSV.generate(headers: true) do |csv|
      csv << results.columns
      results.rows.each do |row|
        csv << row
      end
    end
  end
end
Note that PostgreSQL Server only allow double quote for the sql statement. So I use single quote for quoting the whole query string and double quote for the item name with space in it. For example:
‘select a.po_number as “PO Number”…………….. ‘
That’s it!

Performance Improvement

Let’s take a look at the console log.
Started GET “/orders/1.csv” for ::1 at 2016-10-08 12:58:50 +0800
ActiveRecord::SchemaMigration Load (0.2ms) SELECT “schema_migrations”.* FROM “schema_migrations”
Processing by OrdersController#show as CSV
Parameters: {“id”=>”1”}
Order Load (0.2ms) SELECT “orders”.* FROM “orders” WHERE “orders”.”id” = ? LIMIT ? [[“id”, 1], [“LIMIT”, 1]]
(0.2ms) select a.po_number as ‘PO Number’, c.name as ‘Model Name’, d.name as ‘Color’, e.name as ‘Size’, b.price as ‘Price’, b.quantity as Quantity, (b.price*b.quantity) as ‘Total Amount’ from orders a, orderdetails b, models c, colors d, sizes ewhere a.id = b.order_id and b.model_id=c.id and b.color_id=d.id and b.size_id=e.id and b.order_id=1
Rendering text template
Rendered text template (0.0ms)
Sent data order_1234_2016-10-08 12:58:50.csv (4.5ms)
Completed 200 OK in 56ms (Views: 4.3ms | ActiveRecord: 1.3ms)
It takes 3 queries for 1.3ms! More than three times faster!
Note: the private action can be extracted as a Service Object. Please refer to this article.

Saturday, October 8, 2016

First CHEF Solo Installation and 'Hello World'!

First CHEF Solo Installation and 'Hello World'!

Hello World! My First Cookbook.

CHEF is a platform of deployment automation. It is one of the important tools for DevOps engineers.
This is the first time I install Chef in my Mac, trying to understand how it works. Here are the installation processes I documented according to this tutorial.
Chef installation overview

Perequisites

First, make sure all listed below have been installed in my Mac.

Workstation installation

curl -L https://www.opscode.com/chef/install.sh | sudo bash

sudo gem install knife-solo

gem list

knife configure

Node Setup

Use Ubuntu virtual Box for virtual machine.
mkdir chef
cd chef
vagrant init ubuntu/trusty64
Edit Vagrantfile. Remove ‘#’ before the two lines below and save.
config.vm.network :private_network, ip: "192.168.33.10"
config.vm.network :forwarded_port, guest: 80, host: 8080
Start the Vagrant virtual machine
vagrant up

SSH Setup

Official documentation
Extra reading
This step is to setup the host name of the virtual machine. It writes the host data into the ssh configuration file in the local machine (not the virtual machine).
vagrant ssh-config —host hogege >> ~/.ssh/config
Now we can use ssh hogege to connect to the vagrant virtual machine.
ssh hogege

Chef site-cookbooks creation

knife solo init chef-repo
cd chef-repo
knife solo prepare hogege
$ bootstrapping Chef...

Create cookbook

knife cookbook create hello -o site-cookbooks/

WARN: This command is being deprecated in favor of `chef generate cookbook` and will soon return an error.
Please use `chef generate cookbook` instead of this command.
 at /Users/chaoyee/.rvm/gems/ruby-2.3.1@global/gems/chef-12.14.89/lib/chef/knife.rb:430:in `block in run_with_pretty_exceptions'
** Creating cookbook hello in /Users/chaoyee/chef/chef-repo/sitecookbooks
** Creating README for cookbook: hello
** Creating CHANGELOG for cookbook: hello
** Creating metadata for cookbook: hello
Add the line below to /site-cookbooks/hello/recipts/default.rb file
log "Hello World"
Edit chef-repo/nodes/hogege.json
{
  “run_list”:[
    “recipe[hello]”
  ] 
}
Start cooking!
knife solo cook hogege
Result:
Recipe: hello::default
  * log[Hello World] action write
Running handlers:
Running handlers complete
Chef Client finished, 1/1 resources updated in 01 seconds
It shows the log has been written to the virtual machine ‘hogege’.
Finally, remember to shutdown the virtual machine by typing:
vagrant halt
:-)

Thursday, October 6, 2016

Using Ruby CSV Library to Export CSV File in Rails Apps - send_data method Approach

Using Ruby CSV Library to Export CSV File in Rails Apps - send_data method Approach

‘send_data’ method approach

As we mentioned in the previous article, there are two approaches to generate CSV file. Here is the other approach: ‘send_data’ method approach.
The ‘send_data’ method approach use ‘send_data’ method to export data directly to the CSV file in the controller. It does not use the template file, instead, the ‘to_csv’ method is created in the object model. The ‘to_csv’ method utilizes CSV commands to generate data. Then ‘send_data’ method use object.to_csvto export data to the CSV file.
Again, we need to require CSV library in application.rb
app/config/application.rb
require 'csv'  <== add this line
require 'rails/all'
  :
For example, I need a download button on the show page of the model Order. First, I have to add code to the show action of the order controller. ( Actually I manipulate @orderdetails is orders controller )
app/controllers/orders_controller.rb
def show
     :
     :
  respond_to do |format|
    format.html
    fn = "order_#{@order.po_number}_#{Time.now.strftime("%Y-%m-%d %H:%M:%S")}.csv"
    format.csv { send_data @orderdetails.to_csv, filename: fn }
  end
end
Second, a new method .to_csv should be added in Orderdetail model.
app/models/orderdetail.rb
class Orderdetail < ApplicationRecord
    :
    :
  def po_number
    self.order.po_number
  end

  def m_name
    self.model.name
  end

  def s_name
    self.size.name
  end

  def c_name
    self.color.name
  end

  def self.to_csv
    header = ['PO Number','Model Name', 'Size', 'Color', 'Price (USD)', 'Quantity', 'Total Amount']

    attributes = %w{ po_number model_name 
      size_name color_name price quantity 
      total_amount}

    CSV.generate(headers: true) do |csv|
      csv << header

      all.each do |orderdetail|
        csv << attributes.map{ |attr| orderdetail.send(attr) }
      end
    end  
  end
    :
    :  
end
Note that %w(aaa bbb) is a shortcut for [“aaa”, “bbb”]. Meaning it’s a notation to write an array of strings separated by spaces instead of commas and without quotes around them.
The CSV library generates data according to the attributesarray to the csvobject. Then pass it to the function who calls it ( here is the send_datamethod).
While typing the url below in the browser, it downloads the CSV file instantly.
http://localhost:3000/orders/1.csv
Here is the exported CSV file.
File name: order_1234_2016-10-05 23-38-27.csv
PO Number,Model Name,Size,Color,Price (USD),Quantity,Total
1234,PINEROLO AL SE 0.2 TIG welded Sora 18 Carbon/Alloy Fork,700C X 48CM,Gloss Red,365.42,106,38734.52
1234,PINEROLO AL SE 0.2 TIG welded Sora 18 Carbon/Alloy Fork,700C X 48CM,Gloss Black,365.42,2,730.84
1234,PINEROLO AL SE 0.2 TIG welded Sora 18 Carbon/Alloy Fork,700C X 50CM,Gloss Red,365.42,2,730.84
1234,PINEROLO AL SE 0.2 TIG welded Sora 18 Carbon/Alloy Fork,700C X 50CM,Gloss Black,365.42,2,730.84
The last step is to make a link somewhere in the order show page.
app/views/orders/show.html.erb
<%= link_to 'Export to CSV', order_path(@order, format: :csv) %>
Done!

Using Ruby CSV Library to Export CSV File in Rails Apps - Template Approach

Using Ruby CSV Library to Export CSV File in Rails Apps - Template Approach
When we need to export data to a file in CSV format, we don’t need an extra gem to do so. The standard ruby library has included this functionality. All we need is to require it in application.rb
app/config/application.rb
require 'csv'  <== add this line
require 'rails/all'
  :
Now, I need to have a download button on the show page of the model Order. First, I have to add code to the show action of the order controller.
At this point, there are two ways to do the job. One is template approach, the other is send_data method approach.
(send_data method approach is in another article: here )

Template Approach

Template approach using XXX.csv.erbas a template to generate data to CSV file. Utilization of CSV commands exists in the template file.
app/controllers/orders_controller.rb
def show
     :
     :
  respond_to do |format|
    format.html
    fn = "order_#{@order.po_number}_#{Time.now.strftime("%Y-%m-%d %H:%M:%S")}.csv"
    format.csv do
      headers['Content-Disposition'] = "attachment; filename =" + fn
      headers['Content-Type'] ||= 'text/csv'
    end
  end
end
Second, a new template file show.csv.erb should be added and written in CSV format.
app/views/orders/show.csv.erb
<%- headers = ['PO Number','Model Name', 'Size', 'Color', 'Price (USD)', 'Quantity', 'Total'] -%>
<%= CSV.generate_line headers -%>
<%- @orderdetails.each do |orderdetail| -%>
  <%= CSV.generate_line([
    orderdetail.order.po_number,
    orderdetail.model.name,
    orderdetail.size.name,
    orderdetail.color.name,
    orderdetail.price,
    orderdetail.quantity,
    orderdetail.total_amount
    ]) -%>
<%- end -%>
Note that <%- -%>prevents the app from generating a line break in the CSV file.
While typing the url below in the browser, it downloads the CSV file instantly.
http://localhost:3000/orders/1.csv
Here is the exported CSV file.
File name: order_1234_2016-10-05 23-38-27.csv
PO Number,Model Name,Size,Color,Price (USD),Quantity,Total
1234,PINEROLO AL SE 0.2 TIG welded Sora 18 Carbon/Alloy Fork,700C X 48CM,Gloss Red,365.42,106,38734.52
1234,PINEROLO AL SE 0.2 TIG welded Sora 18 Carbon/Alloy Fork,700C X 48CM,Gloss Black,365.42,2,730.84
1234,PINEROLO AL SE 0.2 TIG welded Sora 18 Carbon/Alloy Fork,700C X 50CM,Gloss Red,365.42,2,730.84
1234,PINEROLO AL SE 0.2 TIG welded Sora 18 Carbon/Alloy Fork,700C X 50CM,Gloss Black,365.42,2,730.84
The last step is to make a link somewhere in the order show page.
app/views/orders/show.html.erb
<%= link_to 'Export to CSV', order_path(@order, format: :csv) %>
Done!
By the way, there are excellent articles about Ruby CSV library, highly recommended!
A Guide to the Ruby CSV Library, Part I
A Guide to the Ruby CSV Library, Part II

Monday, October 3, 2016

Few Tips for Pagination in Rails Apps

Few Tips for Pagination in Rails Apps
When pagination is needed in Rails apps, I always use gem will_paginate. Because it is very simple and just requiring little effort to accomplish my tasks, gem will_paginate become one of my standard gems for every project.
It is simple, but it still need customization to fit my need. For example, the output is not very pretty. The look is not professional.

With Bootstrap Style

What if we use Bootstrap Style to make it a better look? Sure, there is a gem integrates the Twitter Bootstrap pagination component with will_paginate. It is will_paginate-bootstrap.
As usual, add will_paginate-bootstrapin Gemfile:
gem 'will_paginate-bootstrap'
Prepare collection in controller.
def index
  @orders = Order.all.page(params[:page]).per_page(3)
end
Note that the new, shorter page() method is used. It is easier and more intuitive. Then add the line in view template (change @orders to your collection ):
<%= will_paginate(@orders, renderer: BootstrapPagination::Rails %>

Customize the ‘Previous’ and ‘Next’ Tabs

What if we need to replace these two tabs to a simpler way such as ‘<’ and ‘>’?
According to the API documentation, we can assign these options :previous_label and :next_label to the characters we prefer. Therefore, the code becomes:
<%= will_paginate(@orders, 
  renderer: BootstrapPagination::Rails, 
  previous_label: '<', 
  next_label: '>' 
%>
The result:
Bootstrap Pagination

One More Thing: Helper Method

Note that there are many models shown with pagination. The code is duplicated in every view template. Once again, we need code refactoring. I make a helper method generate_pagination in application_helper.rb :
def generate_pagination(object_handled)
  object_paginated = will_paginate(
    object_handled, 
    renderer: BootstrapPagination::Rails,
    previous_label: '<', next_label: '>')
  object_paginated.html_safe if object_paginated
end
Note that the object_paginated is nil (no pagination shown) while there is only one page in the view. A check of object_paginated is necessary for preventing error.
And the view template:
<%= generate_pagination(@orders) %>
That’s it!

Saturday, October 1, 2016

Making breadcrumbs navigation in Rails Apps

Making breadcrumbs navigation in Rails Apps
Making breadcrumbs in rails apps is quite simple. By using gem breadcrumbs_on_rails, breadcrumbs show up right away.
Only Master-details navigation needed to be carefully designed in order to reveal the relationship between the two models. Please follow the Tips below.

Installation

Add this line to Gemfile:
gem "breadcrumbs_on_rails"
then:
bundle install

Controller Setting

Add code below to the controller
class OrdersController < ApplicationController
  add_breadcrumb 'home', :root_path
  add_breadcrumb 'orders', :orders_path

  def show
    @orderdetails = @order.orderdetails
    add_breadcrumb @order.id, order_path
  end
end  
In most cases, adding breadcrumbs navigation only to ‘index’, show’ and ‘edit’ page.

Add Breadcrumbs in View

And then, add render_breadcrumbs helper to the application view file.
app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    :
  </head>
  <body>
    <div class="container" >
         :
      <%= render_breadcrumbs %>
      <%= yield %>
    </div>
  </body>
</html>
And it’s done!

Tips

Put ‘home’ navigation code in ApplicationController

The ‘home’ navigation need to be shown in every page of the app. So the code of breadcrumb is necessary to move from all controllers to application controller.
class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  add_breadcrumb 'home', :root_path
end

Using customized separator

I use “/” instead of “>>” as the separator. Just assign the separator as follows: (in Bootstrap style)
<%= render_breadcrumbs tag: :li, separator: "" %>

Master-Details navigation

The breadcrumb navigation should show the master-detail relationship between two models. For example, a order may have many order details. The navigation should be something like this:
home / orders / 1 / orderdetails / 2 
The solution is to add three lines of code in action ‘edit’ of the controller for showing the last three breadcrumb above.
def edit
  @order = Order.find(@orderdetail.order_id)
  add_breadcrumb @order.id, order_path(@order)
  add_breadcrumb 'orderdetails', order_path(@order)
  add_breadcrumb @orderdetail.id, edit_order_orderdetail_path
end

Twitter Bootstrap Style

Add class='breadcrumb' and it’s done!
<div class='breadcrumb'>
  <%= render_breadcrumbs tag: :li, separator: "" %>
</div>

Saturday, September 24, 2016

Using bootstrap-datepicker in Rails Apps

Using a datepicker widget in Rails is simple. There are two ways of doing so:


  • Using ‘date_field’ (HTML5)
  • Gem ‘bootstrap-datepicker-rails’

Using 'date_field' to Generate Datepicker Widget

<%= form_for(order) do |f| %>
        :
    <%= f.date_field :shipment_require_date %>
        :
<% end %>       
The datepicker widget will show up while focusing on the field.
However, this HTML5 functionality only supported by Chrome.


It does not work while using Safari or Firefox.

Using Gem‘bootstrap-datepicker-rails’

Gem ‘bootstrap-datepicker-rails’ is a datepicker widget gem in the Bootstrap style for Rails. The document is here.
According to the document, just few steps to go and you are all done!
  • First, include ‘bootstrap-datepicker-rails’ in Gemfile.
gem 'bootstrap-datepicker-rails'
  • And add this line to app/assets/stylesheets/application.css
 *= require bootstrap-datepicker
  • Add this line to app/assets/javascripts/application.js
//= require bootstrap-datepicker
  • add code 
    There are two ways of coding: 
    • jQuery
    • Code In-line

jQuery: Assigning values to the options by using javascript.

<%= f.text_field :shipment_require_date, class: "datepicker" %>
        :
    <script type="text/javascript">
      $(document).ready(function(){
        $('.datepicker').datepicker({
          format: "yyyy/mm/dd",
          autoclose: true,
          todayBtn: "linked",
          todayHighlight': true
        });
      });
    </script>
The story ends.

Code In-line: Assign values to the options directly in-line.

According to the options document, option should be transformed to a certain format (data-date-) in order to be placed in-line to the code.
All options that take a “Date” can handle a Date object; a String formatted according to the given format; or a timedelta relative to today, eg “-1d”, “+6m +1y”, etc, where valid units are “d” (day), “w” (week), “m” (month), and “y” (year). Use “0” as today. There are also aliases for the relative timedelta’s: “yesterday” equals “-1d”, “today” is equal to “+0d” and “tomorrow” is equal to “+1d”.
Most options can be provided via data-attributes. An option can be converted to a data-attribute by taking its name, replacing each uppercase letter with its lowercase equivalent preceded by a dash, and prepending “data-date-” to the result. For example, startDate would be data-date-start-dateformat would bedata-date-format, and daysOfWeekDisabled would be data-date-days-of-week-disabled.
Therefore, option ‘todayBtn’ should be ‘data-date-today-btn‘.
The code of the datepicker part is:
app/views/orders/_form.html.erb
<%= f.text_field :shipment_require_date, 
    data: { provide: "datepicker", 
        'date-format': 'yyyy-mm-dd', 
        'date-autoclose': 'true', 
        'date-today-btn': 'linked',
        'date-today-highlight': 'true'} %>
It generates HTML code as follows:
<input data-provide="datepicker" data-date-format="yyyy-mm-dd" data-date-autoclose="true" data-date-today-btn="linked" data-date-today-highlight="true" type="text" name="order[shipment_require_date]" id="order_shipment_require_date" />
Is this way more concise than the jQuery one? It depends. Maybe the jQuery way is better since the code will be cleaner if there are more than two datepicker widgets.

Making a Helper for Code In-line.

As you can see, if using code in-line method and the datepicker input fields are more than one, it is necessary to refactor the code. A helper is created as follows:
app/helpers/application_helper.rb
def datepicker_field(form, field)
    form.text_field(field, data: { provide: "datepicker",
      'date-format': 'yyyy-mm-dd',
      'date-autoclose': 'true',
      'date-today-btn': 'linked',
      'date-today-highlight': 'true'}).html_safe
end
It will generate the HTML code exactly the same as above.
The code of the datepicker part changes to:
app/views/orders/_form.html.erb
<%= datepicker_field(f, :shipment_require_date) %>

It's nice and clean! Happy ending!

Saturday, September 10, 2016

An Improved Error Message Bar

When showing error message such as validation errors or any kind of error messages stored in the object instantiated from the class ActiveModel::Errors, the default way to show error messages is ugly and inefficient. Let’s take a look at the validation error handling code generated by rails g scaffold (take model Color for example):
/app/views/colors/_form.html.erb
<%= form_for(color) do |f| %>
  <% if color.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(color.errors.count, "error") %> prohibited this color from being saved:</h2>
      <ul>
        <% color.errors.full_messages.each do |message| %>
          <li><%= message %></li>
        <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :name %>
    <%= f.text_field :name %>
  </div>

  <div class="action">
    <%= f.submit %>
    <%= link_to 'Back' %>
  </div>
<% end %>
The error message handling code is as follows:
<% if color.errors.any? %>
  <div id="error_explanation">
  <h2><%= pluralize(color.errors.count, "error") %> prohibited this color from being saved:</h2>
    <ul>
      <% color.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
    </ul>
  </div><% end %>
The new.html.erb and edit.html.erb forms call partial form _form.html.erb. However, there are many models using the same code as above except the model name. It is obvious that code refactoring jobs must be done to the error message code.
Besides, the original error message code generates a not very nice-looking web page. It is just few lines of messages shown on top of the page. Why don’t we use the same idea of notice message bar to make a better looking error message bar?

Sure we can, so here is the code and how to implement it:

1. Create a helper method error_message_for(object_habdled)in application_helper.rb.

/app/helpers/applicaiton_helper.rb
def error_message_for(object_handled)
    if object_handled.errors.any?
      close_button_options = { class: "close", "data-dismiss" => "alert", "aria-hidden" => true }
      close_button = content_tag(:button, "×", close_button_options)

      error_content = close_button + error_content
      object_handled.errors.full_messages.each do |message|
        error_content = error_content + message + simple_format("\n")
      end
      content_tag(:div, error_content, id: "error_explanation", class: "alert alert-danger alert-dismissable")
    end
  end

2. Call the helper method in every _form.html.erband assign the model name accordingly.

Insert <%= error_message_for(color) %>on top of the partial form.
For example:
/app/views/color/_form.html.erb
<%= error_message_for(color) %>
<%= form_for(color) do |f| %>
  <div class="field">
    <%= f.label :name %>
    <%= f.text_field :name %>
  </div>
  <div class="action">
    <%= f.submit %>
    <%= link_to 'Back' %>
  </div>
<% end %>
Now, it shows a very nice looking error message bar on top of the page with Bootstrap style.