Skip to content

Instantly share code, notes, and snippets.

@winston
Last active March 4, 2016 11:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save winston/df1c53de02bf3103768a to your computer and use it in GitHub Desktop.
Save winston/df1c53de02bf3103768a to your computer and use it in GitHub Desktop.
Rails Recipes

Country Select

Currently, we see a text field for country_code when creating a gram.

Let's replace that with a select dropdown with actual country names.

1. Install Gem

Add to your Gemfile, and run bundle install in your terminal after that.

gem 'country_select'

2. Update new.html.erb

First, remove this line:

<%= f.text_field :country_code %>

In place of it, add this line:

<%= f.country_select :country_code, include_blank: true, priority_countries: ['SG', 'MY'] %>

Now, you have succcessfully replaced the text field for country_code with a select drop down.

When the form is submitted, it'll still be saving the 2-letters country code in the database.

3. Add a Helper

On our application, instead of showing country_code, we want to show the country name.

Let's add a helper in ApplicatioHelper to do this:

 module ApplicationHelper
  def country_name(country_code)
    country = ISO3166::Country[country_code]
    if country
      country.translations[I18n.locale.to_s]
    else
      country_code
    end
  end
end

This method uses the ISO3166::Country class from the country_select gem to find a country instance based on the country_code, and since I18n.locale.to_s returns en (our language) by default, this helper method will return the English name for the country code.

4. Add Helper to index.html.erb and show.html.erb.

In index.html.erb, let's use this method:

<div><%= country_name(gram.country_code) %></div>

In show.html.erb, let's use this method:

<div><%= country_name(@gram.country_code) %></div>

Restart the server and we are all done!

Pagination

Currently, on our home page, we are displaying ALL the grams in our database because our index action in GramsController uses @grams = Gram.all.

If we have hundreds of grams in the database, this page would take a long time to load, hence let's add some pagination.

1. Install Gem

Add to your Gemfile, and run bundle install in your terminal after that.

gem 'kaminari'

2. Update Controller Action

In our GramsController, modify the index action:

  def index
    @grams = Gram.page(params[:page]).per(5)
  end

The page and per are methods added by kaminari and to get a different page, our URL would look like grams?page=2

By default, the per value is 25.

3. Add Pagination Links

In the index.html.erb, add this to the bottom of the file:

<%= paginate @grams %>

Restart the server and you are done!

Image Upload Part 1

There are many different ways to do image uploads in RoR, and this is just one of the ways to do it.

Whatever method you choose, the steps involved are mostly similar:

1. You'll need a data storage to store the uploaded images. The usual choices are:

  • You App Server (if there's permanent storage and plenty of space)
  • Amazon Web Service's S3
  • Other 3rd Party storage services, i.e. Cloudinary

2. You'll need to save the URL to the image (in the data storage) in your database.

This typically involves creating a new column in the database table to store the URL.

3. You'll need a RubyGem to handle images (upload, download and saving to the database).

RubyGems that can help:

  • paperclip
  • carrierwave
  • dragonfly
  • refile

4. You'll need JavaScript libraries to do asynchronous uploads, cropping and filtering.

Pick the appropriate JavaScript libraries to perform the desired actions.


For a start, let's focus on getting a simple image upload working, without the use of JavaScript. Ref: https://github.com/jollygoodcode/minigram/commit/57c4a8826e1d130a17a83c8f5f02f89709917bb4

1. Install Gems

Add these to your Gemfile, and run bundle install in your terminal after that.

gem 'carrierwave'
gem 'cloudinary'

bundle install will read your Gemfile, and download these gems to your local file system

2. Add New Column to Grams

In your terminal, run this command:

$ rails generate migration add_image_to_grams image:string

This will generate a new migration that adds the column image to the grams table. Because you typed add_image_to_grams followed by the column and type image:string, then Rails can interpret that and generate the migration automatically for you.

If you open up the migration file, you would see add_column :grams, :image, string.

Now, run the migration with:

$ rake db:migrate

3. Create ImageUploader

Next, let's create the ImageUploader required by carrierwave to work.

Run this in your terminal:

$ rails generate uploader image

This will creat a new folder app/uploaders and a new file image_uploader.rb.

4. Update ImageUploader

Open up ImageUploader at app/uploaders/image_uploader.rb and change the contents to:

# encoding: utf-8

class ImageUploader < CarrierWave::Uploader::Base
  include Cloudinary::CarrierWave

  version :standard do
    cloudinary_transformation width: 600, height: 600, crop: :pad, background: 'black'
  end
 end

 This ImageUploader inherits from `CarrierWave::Uploader::Base` (carrierwave gem)
 and includes methods from `Cloudinary::CarrierWave` (cloudinary gem).

 This would take care of image uploads and processing of the image on Cloudinary.

5. Link the Gram model to ImageUploader

Remember we added a new column image to grams table?

That column should be managed by ImageUploader, so that after an image is uploaded, the URL to the image would be saved into the new column.

Hence we need to link them up. Let's modif Gram.rb to look like so:

class Gram < ActiveRecord::Base
  validates_presence_of :title, :content

  mount_uploader :image, ImageUploader
end

6. Update new.html.erb

Next, we need to update our form so that you can upload an image when creating a gram.

Let's add the following to app/views/grams/new.html.erb, before the title input.

<div>
  <%= f.file_field :image %>
  <%= f.hidden_field :image_cache %>
</div>

The :image field is for selecting and uploading an image, and the :image_cache is for supporting page reloading and validation errors while not losing the uploaded image.

7. Update show.html.erb

Add this line to your show.html.erb template to display the uploaded image:

<div><%= image_tag @gram.image_url(:standard) %></div>

8. Update index.html.erb

Add this line to your index.html.erb template to display the uploaded image:

<div><%= image_tag gram.image_url(:standard) %></div>

9. Sign Up with Cloudinary

To make all these work, we need to sign up for the Cloudinary service at http://cloudinary.com/.

Once you have signed in, download the YML file and put it at config/cloudinary.yml.

10. Start Your Rails Server

Finally, start your server with rails s -b 0.0.0.0 and give it a try!

Bonus: Protect Your Cloudinary Credentials

If you open up config/cloudinary.yml, you will see that it contains credentials to your Cloudinary account. In the real world, it's not considered a best practice.

Let's see how we can fix this.

First, let's add this to Gemfile, and then run bundle install.

group :development, :test do
  gem 'dotenv-rails'
end

Next, create a .env file in the root folder of your project, and copy the credentials in config/cloudinary.yml over.

CLOUDINARY_CLOUD_NAME=cloudname
CLOUDINARY_API_KEY=12345
CLOUDINARY_API_SECRET=abcde

Finally, modify config/cloudinary.yml to become:

---
development:
  cloud_name: <%= ENV['CLOUDINARY_CLOUD_NAME'] %>
  api_key: <%= ENV['CLOUDINARY_API_KEY'] %>
  api_secret: <%= ENV['CLOUDINARY_API_SECRET'] %>
  enhance_image_tag: true
  static_image_support: false
production:
  cloud_name: <%= ENV['CLOUDINARY_CLOUD_NAME'] %>
  api_key: <%= ENV['CLOUDINARY_API_KEY'] %>
  api_secret: <%= ENV['CLOUDINARY_API_SECRET'] %>
  enhance_image_tag: true
  static_image_support: true
test:
  cloud_name: <%= ENV['CLOUDINARY_CLOUD_NAME'] %>
  api_key: <%= ENV['CLOUDINARY_API_KEY'] %>
  api_secret: <%= ENV['CLOUDINARY_API_SECRET'] %>
  enhance_image_tag: true
  static_image_support: false

Do you notice what this is <%= ENV['CLOUDINARY_CLOUD_NAME'] %>?

Yes. These are erb tags common in views. You can use them in config Yaml files too.

Restart your server and everything should still work as per normal.

Yea! You have a basic image uploading working now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment