Eric Norcross
Eric Norcross

Reputation: 4306

Rails 4, Cloudinary, Carrierwave not uploading

I've been wracking my brain for hours pouring over documentation and I can not for the life of me figure out what's wrong. The form is accepting an image, but does not trigger a direct upload. My files below.

Gemfile

source 'https://rubygems.org'

# Platform 
  gem 'rails', '4.2.6'

# Authentication / Authorization 
  gem 'devise'

# View Tools
  gem 'jquery-rails'
  gem 'jquery-ui-rails'
  gem 'haml-rails'
  gem 'font-awesome-rails'
  gem 'friendly_id', '~> 5.1.0'

# Form
  gem 'cocoon'
  gem 'ckeditor'
  gem 'mini_magick'

# DB Tools
  gem 'pg'
  gem 'seed_dump'
  gem 'annotate'

# Image Serving & Uploading
  gem 'carrierwave'
  gem 'cloudinary'

# Server Tools
  gem 'puma'
  gem 'foreman'
  gem 'turbolinks'
  gem 'rails_12factor' # To silence Heroku deprecation warning

# Gems used only for assets and not required in production environments by default.
  gem 'sassc-rails'
  gem 'uglifier'

group :development do 
  gem 'web-console', '~> 2.0'
  gem 'awesome_print', :require => 'ap'
  gem 'better_errors'
  gem 'binding_of_caller'
  gem 'meta_request'
end

application.js

//= require jquery
//= require jquery_ujs
//= require jquery-ui
//= require bootstrap.min
//= require cloudinary
//= require cocoon
//= require turbolinks
//= require_tree .

scripts.js

$(function() {
  if($.fn.cloudinary_fileupload !== undefined) {
    $("input.cloudinary-fileupload[type=file]").cloudinary_fileupload();
  }
});

application.haml

    !!!
    %html

      %head
        %meta{:charset => "utf-8"}
        %meta{:content => "IE=edge", "http-equiv" => "X-UA-Compatible"}
        %meta{:content => "width=device-width, initial-scale=1", :name => "viewport"}/
        %title Martin Furniture
        = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true
        = javascript_include_tag 'application', 'data-turbolinks-track' => true
        = csrf_meta_tags
        = cloudinary_js_config

        %link{:href => "https://fonts.googleapis.com/css?family=Open+Sans|Oswald", :rel => "stylesheet"}

      %body
        = render :partial => 'layouts/partials/header'
        = yield
        = render :partial => 'layouts/partials/footer'

image_uploader.rb

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

figure.rb

# == Schema Information
#
# Table name: figures
#
#  id              :integer          not null, primary key
#  caption         :string(255)
#  link            :text
#  figureable_id   :integer
#  figureable_type :string
#  created_at      :datetime         not null
#  updated_at      :datetime         not null
#  title           :string(255)
#  alt             :string(255)
#  image           :string
#

class Figure < ActiveRecord::Base
  default_scope                     { order(id: :ASC) }

  mount_uploader                    :image, ImageUploader

  # Figureable (Polymorphic)
  # ==========================================================================================================  
    belongs_to                      :figureable, 
                                      polymorphic:    true

end

page.rb

# == Schema Information
#
# Table name: pages
#
#  id          :integer          not null, primary key
#  name        :string(255)      not null
#  slug        :string
#  created_at  :datetime         not null
#  updated_at  :datetime         not null
#  template_id :integer          default(1), not null
#  title       :string(255)
#  content     :text
#  link        :text
#  parent_id   :integer
#

class Page < ActiveRecord::Base
  default_scope                     { order(id: :ASC) }

  extend FriendlyId
  friendly_id :name, use: :slugged

  # Figures (Polymorphic)
  # ==========================================================================================================  
    has_many                        :figures, 
                                      as:             :figureable

    accepts_nested_attributes_for   :figures, 
                                      reject_if:      :all_blank, 
                                      allow_destroy:  true

  # Validations
  # ==========================================================================================================  
    validates                       :name, 
                                      presence:       true,
                                      format:         { with: /[0-9a-zA-Z\s\/_:\-.|]*/ }

end

figures_controller.rb

class FiguresController < ApplicationController
  before_action :set_figure, only: [:show, :edit, :update, :destroy]

  def index
    @figures = Figure.all
  end

  def show
  end

  def new
    @figure = Figure.new
  end

  def edit
  end

  def create
    @figureable   = find_figureable

    if @figurable
      @figure     = @figureable.figures.build(figure_params)
    else 
      @figure     = Figure.new(figure_params)
    end

    if @figure.save
      redirect_to @figure, notice: 'Figure was successfully created.'
    else
      render :new
    end
  end

  def update
    if @figure.update(figure_params)
      redirect_to @figure, notice: 'Figure was successfully updated.'
    else
      render :edit
    end
  end

  def destroy
    @figure.destroy
    redirect_to figures_url, notice: 'Figure was successfully destroyed.'
  end

  private
    def set_figure
      @figure = Figure.find(params[:id])
    end

    def figure_params
      params.require(:figure).permit(:caption, :link, :figurable_id, :figurable_type)
    end

    def find_figureable
      params.each do |name, value|
        if name =~ /(.+)_id$/
          return = $1.classify.constantize.find(value)
        end
      end

      nil

    end
end

pages_controller.rb

class PagesController < ApplicationController
  before_action :set_page, only: [:edit, :update, :destroy]

  # GET /pages
  def index
    @pages = Page.all.reorder(slug: :ASC)
  end

  # GET /pages/1
  def show
    @page = Page.friendly.find(params[:id])

    page_template = "layouts/templates/#{@page.name.parameterize.underscore}"

    if lookup_context.find_all(page_template).any?
      render page_template
    else
      render :show
    end
  end

  # GET /pages/new
  def new
    @page = Page.new    
  end

  # GET /pages/1/edit
  def edit
  end

  # POST /pages
  def create
    @page = Page.new(page_params)

    if @page.save
      redirect_to pages_url, notice: 'Page was successfully created.'
    else
      render :new
    end
  end

  # PATCH/PUT /pages/1
  def update
    if @page.update(page_params)
      redirect_to pages_url, notice: 'Page was successfully updated.'
    else
      render :edit
    end
  end

  # DELETE /pages/1
  def destroy
    @page.destroy
    redirect_to pages_url, notice: 'Page was successfully destroyed.'
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_page
      @page = Page.friendly.find(params[:id])
    end

    # Only allow a trusted parameter "white list" through.
    def page_params
      params.require(:page)
        .permit(
          :name, 
          :slug,
          :template_id,
          :title,
          :content,
          :link,

          # belongs_to
            :parent_id,

          # has_many (Nested Attributes)
            { :figures_attributes => [ 
                :_destroy,
                :id,
                :caption,
                :link,
                :title,
                :alt,
                :image
            ]},

        )
    end
end

pages/_form.haml

= form_for @page do |f|
  - if @page.errors.any?
    #error_explanation
      %h2= "#{pluralize(@page.errors.count, "error")} prohibited this page from being saved:"
      %ul
        - @page.errors.full_messages.each do |msg|
          %li= msg

  .field
    = f.label :name
    = f.text_field :name

  %h3 Figures
  #figures
    = f.fields_for :figures do |figure|
      = render 'figures/figure_fields', f: figure
    .links
      = link_to_add_association 'Add Figure', f, :figures, partial: 'figures/figure_fields'

  .field
    = f.cktext_area :content

  .actions
    = f.submit 'Save'

figures/_figure_fields.html

.nested-fields
  .field
    = f.label :caption
    = f.text_field :caption
  .field
    = f.label :link
    = f.text_field :link

  .field
    = f.label :image, "Image"
    = f.cl_image_upload :image

  = link_to_remove_association "Remove Figure", f

params upon form submission

Processing by PagesController#create as HTML
20:43:40 web.1  |   Parameters: {"utf8"=>"✓", "authenticity_token"=>"AUTHENTICITY_TOKEN", "page"=>{"name"=>"Blah2", "figures_attributes"=>{"1471318988746"=>{"caption"=>"B", "link"=>"C", "_destroy"=>"false"}}, "content"=>""}, "file"=>"main_image.jpg", "commit"=>"Save"}

rendered input element

<input type="file" name="file" data-url="https://api.cloudinary.com/v1_1/CLOUD_NAME/auto/upload" data-form-data="{"callback":"http://localhost:8080/cloudinary_cors.html","timestamp":1471319573,"signature":"SIGNATURE","api_key":"API_KEY"}" data-cloudinary-field="page[figures_attributes][1471319575584][image]" class="cloudinary-fileupload">

cloudinary.yml

---
#Production MF
development:
  cloud_name: CLOUD_NAME
  api_key: 'API_KEY'
  api_secret: SECRET_KEY
  enhance_image_tag: true
  static_image_support: false
production:
  cloud_name: CLOUD_NAME
  api_key: 'API_KEY'
  api_secret: SECRET_KEY
  enhance_image_tag: true
  static_image_support: true
test:
  cloud_name: CLOUD_NAME
  api_key: 'API_KEY'
  api_secret: SECRET_KEY
  enhance_image_tag: true
  static_image_support: false

*Note the above CLOUD_NAME, API_KEY, SIGNATURE, AUTHENTICITY_TOKEN AND SECRET_KEY are filled out with the correct information, but have been removed for this post. Additionally, the cloudinary_cores.html file is in my public directory.

I've tried following the instructions both on the Cloudinary Gems repo and on their help docs and can not figure out what the problem is.

If there's anything else anyone needs, please let me know.

Upvotes: 0

Views: 499

Answers (1)

Eric Norcross
Eric Norcross

Reputation: 4306

So, it turns out that the turbolinks gem was the culprit here. By changing scripts.js to the following, I'm now uploading properly.

Note: This is the usage for Turbolinks 5 - Previous versions require a slightly different setup.

scripts.js

// jQuery "$(document).ready()" equivalent for Trubolinks 5 usage

$(document).on('turbolinks:load', function() {
  console.log("trubolinks:load - jQuery Ready!");

  $(function() {
    cloudinaryUploaderInit();
  });
});

var cloudinaryUploaderInit = function() {
  // console.log("cloudinaryUploaderInit");
  if ($.fn.cloudinary_fileupload !== undefined) {
    $("input.cloudinary-fileupload[type=file]").each(function() {
      $(this).cloudinary_fileupload();
    });
  } else {
    // console.log("`cloudinary_fileupload` is undefined");
  }
}

EDIT: Alternatively (and prehaps more approproately), you can use the jquery-turbolinks gem as outlined here: https://github.com/cloudinary/cloudinary_gem/issues/126

Upvotes: 1

Related Questions