Rails 4 : Multiple file upload with carrierwave, nested form and jquery file upload

In this example, I will show you how to allow a single form submission from a parent object with multiple children associate to it using the “nested form”.

1) Let say we have a schema for albums that has many photos associate to it.

  
create_table "albums", force: true do |t|
    t.string   "title",       null: false
    t.text     "description"
    t.integer  "user_id"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  
create_table "photos", force: true do |t|
    t.integer  "album_id"
    t.string   "image"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

2) Create new carrierwave uploader. see this post in order to setup one https://u.osu.edu/hasnan.1/2014/03/13/rails-4-upload-image-to-s3-using-fog-and-carrierwave/

3) Our Albums and Photos models.

class Album < ActiveRecord::Base
	# belongs to user model
	belongs_to	:user
	# Album has many photos
	has_many    :photos, :inverse_of => :album, :dependent => :destroy
	# enable nested attributes for photos through album class
	accepts_nested_attributes_for :photos, allow_destroy: true
end

class Photo < ActiveRecord::Base
        #photo belongs to album
	belongs_to	:album
	#validations
	validates 	:album, presence: true
	# Photo uploader using carrierwave
        mount_uploader :image, PhotoUploader
end

4) Our Controller where all the magic happens

In our case, we want to allow our user to upload multiple photos, and it will only get saved when user hits the submit button. User will be able to add more images in the future when they click edit album and add images from there.

So here is the album controller.


class AlbumsController < ApplicationController
# truncated for brevity.
  def create
    @album = current_user.albums.build(album_params)
    authorize @album
    if @album.save
      # to handle multiple images upload on create
      if params[:images]
        params[:images].each { |image|
          @album.photos.create(image: image)
        }
      end
      flash[:notice] = "Your album has been created."
      redirect_to @album
    else 
      flash[:alert] = "Something went wrong."
      render :new
    end
  end
  def update
    authorize @album
    if @album.update(params[:album].permit(:title,:description))
      # to handle multiple images upload on update when user add more picture
      if params[:images]
        params[:images].each { |image|
          @album.photos.create(image: image)
        }
      end
      flash[:notice] = "Album has been updated."
      redirect_to @album
    else
      render :edit
    end
  end
end


Photos controller:

class PhotosController < ApplicationController
# truncated for brevity.
  def create
    @photo = Photo.new(photo_params)
    @photo.save
  end
end

5) Our Views

In this example, we are not using nested resources. That means we have our routes to setup like this
config/routes.rb

resources :albums
resources :photos

app/views/albums/_form.html.erb

 <%= form_for @album, :html => {:multipart => true} do |f| %>
  
<%= f.label :title, :class => 'control-label' %>
<%= f.text_field :title, :class => 'text_field' %>
<%= f.label :description, :class => 'control-label' %>
<%= f.text_field :description, :class => 'text_field' %>
#images[] returned to the album as an array. We use file_field_tag since images is not @album's attribute <%= file_field_tag "images[]", type: :file, multiple: true %>
<%= f.submit "submit", :class => "btn btn-default" %>
<% end %>

pay attention to the comment that I made on _form.html.erb template.

6) Handle image preview with jquery file upload
I'm not going to cover this topic at this time.

So, that's it. now when your user hit the submit button, rails will save all the images to album.cerita.tv iqbalhasnan.wordpress.com

Rails 4: upload image to s3 using fog and carrierwave

1. Add these lines to your gemfile and run ‘bundle install’

# for aws cloud storage
gem 'fog'
# photo resizing
gem "mini_magick"
# file upload solution
gem 'carrierwave'

2. generate new carrierwave uploader using this command’rails generate uploader Avatar’
That will creates a file in ‘app/uploaders/avatar_uploader.rb’

3. Update the file with the following

# encoding: utf-8

class AvatarUploader < CarrierWave::Uploader::Base

  # Include RMagick or MiniMagick support:
  # include CarrierWave::RMagick
  include CarrierWave::MiniMagick

  # Choose what kind of storage to use for this uploader:
  #storage :file
  storage :fog
  # Override the directory where uploaded files will be stored.
  # This is a sensible default for uploaders that are meant to be mounted:
  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

  # Provide a default URL as a default if there hasn't been a file uploaded:
  def default_url
    # For Rails 3.1+ asset pipeline compatibility:
    # ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
  
    #{}"/images/fallback/" + [version_name, "default.png"].compact.join('_')
    'default_avatar.png' #rails will look at 'app/assets/images/default_avatar.png'
  end

  # Process files as they are uploaded:
  # process :scale => [200, 300]
  #
  # def scale(width, height)
  #   # do something
  # end

  # Create different versions of your uploaded files:
  version :large_avatar do
    # returns a 150x150 image
    process :resize_to_fill => [150, 150]
  end
  version :medium_avatar do
    # returns a 50x50 image
    process :resize_to_fill => [50, 50]
  end
  version :small_avatar do
    # returns a 35x35 image
    process :resize_to_fill => [35, 35]
  end

  # Add a white list of extensions which are allowed to be uploaded.
  # For images you might use something like this:
  def extension_white_list
    %w(jpg jpeg gif png)
  end
end

4. Update your user.rb with the following, assuming you already have avatar column in database with datatype string

class User < ActiveRecord::Base
  .
  .
  # Avatar uploader using carrierwave
  mount_uploader :avatar, AvatarUploader
end

5. Create a new file in 'config/initializer/s3.rb', and paste the following code

CarrierWave.configure do |config|
  config.fog_credentials = {
      :provider               => 'AWS',
      :aws_access_key_id      => ENV['S3_KEY'],
      :aws_secret_access_key  => ENV['S3_SECRET']
      # :region                 => ENV['S3_REGION'] # Change this for different AWS region. Default is 'us-east-1'
  }
  config.fog_directory  = ENV['S3_BUCKET']
end

6. If you are on unix machine, add the following code in your ~/.bash_profile, and run source to reload it, otherwise hardcoded the value of your access id, key and bucket in step 5.

#aws s3
export S3_KEY="SecretKeyFromAWS"
export S3_SECRET="SecRetKEy"
export S3_BUCKET="bucketname"

7. Code for upload view, pay attention to ':multipart => true'

<%= form_for @user, :html => {:multipart => true} do |f| %>
    
    <%= f.file_field :avatar %>
<% end %>

8. Code to display in view

original size
<%= image_tag(@user.avatar)%>

large size
<%= image_tag(@user.avatar.large_avatar)%>

medium size
<%= image_tag(@user.avatar.medium_avatar)%>

small size
<%= image_tag(@user.avatar.small_avatar)%>

Ansible: replace one line of code in multiple files in a directory

We are going to use lineinfile module and register statement.

Task that ‘ls’ the directory, and store in register statement.

- name: list of the .conf files and store it in register
  raw: find /etc/httpd/conf.d -type f -name "*.conf"
  register: certs_dir
  tags: update-cert

Task that replace one line on every *.conf file.

- name: update certs with the new name in *conf
  lineinfile: dest={{item}} backup=yes state=present regexp="^  SSLCertificateFile" insertafter="^  SSLCertificateFile" line="  SSLCertificateFile      /etc/pki/tls/certs/new_cert.cer"
  with_items: certs_dir.stdout_lines
  tags: update-cert

Test

ansible-playbook -i hosts sites.yaml -u root --ask-pass --tags update-cert

Grails: How to use postgreSQL with Grails

1. open BuildConfig.groovy and comment out mavenRepo “http://download.java.net/maven/2/ under the repositories section and add this line under  dependencies section

    

repositories {
        // uncomment these (or add new ones) to enable remote dependency resolution from public Maven repositories
        mavenRepo "http://download.java.net/maven/2/"

        //repo for spring-security
        mavenRepo "http://repo.spring.io/milestone/"
    }

dependencies {
        runtime "postgresql:postgresql:9.1-901.jdbc4"
    }


2. open DataSource.groovy file and edit the following :

dataSource {
    pooled = true
    driverClassName = "org.postgresql.Driver"
    dialect = "org.hibernate.dialect.PostgreSQLDialect"
    username = "iqbal"
    password = ""
}
hibernate {
    cache.use_second_level_cache = true
    cache.use_query_cache = false
    cache.region.factory_class = 'net.sf.ehcache.hibernate.EhCacheRegionFactory' // Hibernate 3
//    cache.region.factory_class = 'org.hibernate.cache.ehcache.EhCacheRegionFactory' // Hibernate 4
}

// environment specific settings
environments {
    development {
        dataSource {
            dbCreate = "create-drop" // one of 'create', 'create-drop', 'update', 'validate', ''
            url = "jdbc:postgresql://localhost:5432/seram"
        }
    }
    test {
        dataSource {
            dbCreate = "update"
            url = "jdbc:postgresql://localhost:5432/seram"
        }
    }
    production {
        dataSource {
            dbCreate = "update"
            url = "jdbc:h2:prodDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE"
            properties {
                maxActive = -1
                minEvictableIdleTimeMillis=1800000
                timeBetweenEvictionRunsMillis=1800000
                numTestsPerEvictionRun=3
                testOnBorrow=true
                testWhileIdle=true
                testOnReturn=false
                validationQuery="SELECT 1"
                jdbcInterceptors="ConnectionState"
            }
        }
    }
}