Austin Story

Ruby, Rails and Javascript Blog

Powered by Genesis

Adding Nested Image Attributes to Rails 4 App using Paperclip

February 27, 2014 By Austin Story 1 Comment

***Update – this no longer works due to changes in Paperclip but leaving here for historical context.  I have moved to using the gem Refile for image stuff***

So have a new web application where users will be able to upload images along with a post. I haven’t used the ruby gem Paperclip from ThoughtBot since rails 3.2 (which seems like it was only a couple months ago) so I thought I would write up a post on how I got it working.

The background of the app

  • Post have many images
  • Image is polymophic and can belong to either Post or Comment

So first step is to add the gem ‘paperclip’ to your gem file and then run bundle install.

Next, configure paperclip in development/production.rb   For this app we will be doing file storage locally until it is live

config/enviornments/development.rb

[code]
Paperclip.options[:command_path] = "/usr/local/bin"
[/code]

After that we need to setup our models for Image and Post. Image is polymorphically related to Post as imageable and also contains content through the paperclip gem. Post has images and can accept nested attributes for images.

app/models/image.rb

[code]
class Image < ActiveRecord::Base
belongs_to :imageable, :polymorphic => true
has_attached_file :content, :styles=>{:medium => "300×300>", :thumb => "100×100>"}
validates_attachment_content_type :content, :content_type => %w(image/jpeg image/jpg image/png)
end
[/code]

app/models/post.rb

[code]
class Post < ActiveRecord::Base
belongs_to :user
has_many :images, :as => :imageable, dependent: :destroy
accepts_nested_attributes_for :images
end
[/code]

Now that the models are good, we have to code around the strong parameters that were added in rails 4. Notice that in the private section we are adding ‘images_attributes: [:content]’ to prevent the image upload from being filtered and adding @post.images.build to the new action so that our view can render out the file tag properly.

app/controllers/posts_controller.rb

[code]
class PostsController < ApplicationController

def new
@post = Post.new
@post.images.build
end

def create
@post = Post.new(post_params)
@post.user_id = current_user.id

respond_to do |format|
if @post.save
format.html { redirect_to @post, notice: ‘Post was successfully created.’ }
format.json { render action: ‘show’, status: :created, location: @post }
else
format.html { render action: ‘new’ }
format.json { render json: @post.errors, status: :unprocessable_entity }
end
end
end

private
#strong parameters
def post_params
params.require(:post).permit(:title, :description, images_attributes: [:content])
end
end
[/code]

Last part is the view, now that it is wired up it will just be adding a couple lines to our form. I removed the code that will spit out the errors that come through for succinctness here. Note that the upload image part has :images in fields_for which corresponds to our has_many :images in Post model and that the file_file :content corresponds to the has_attached_file :content in our Image mode.

Also, in order for the html field to accept images, make sure you add the multipart to the html.

app/views/posts/_form.html.erb

[code]
<%= form_for @post, :html => { :multipart => true } do |f| %>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_field :description %>
</div>
<div>
Upload Image
<%= f.fields_for :images do |ph| %>
<%= ph.file_field :content %>
<% end%>
</div>

<div class="actions">
<%= f.submit %>
</div>
<% end %>

[/code]

That about does it. If you put this together and it doesn’t work, restart your server and it should start singing.

Filed Under: Programming, Ruby on Rails Tagged With: paperclip, Polymorphic, Rails 4, ruby on Rails, Strong Parameters

Integrating Wicked-pdf, Paperclip and Mercury Rails for an editable web document that can be exported to PDF

October 11, 2013 By Austin Story 4 Comments

So i’m creating an editable web document that can be displayed as a PDF using mercury-rails, paperclip and wicked-pdf

gemfile

[code]
#PDF Editing Region

#WYSIWIG Editor
gem ‘mercury-rails’

#PDF Printing
gem ‘wkhtmltopdf-binary’
gem ‘wicked_pdf’

#Image uploading
gem ‘paperclip’
gem ‘aws-sdk
[/code]

Mercury

After you save this we have to run a migration for Mercury-rails
[code]bundle
rails g mercury:install
rake mercury_engine:install:migrations
rake db:migrate[/code]

Now that we have mercury rails installed, you should be able to go to any location in your app and view it with a WYSIWIG editor by prepending /editor to the request.

I am assuming that you will be using this to setup some sort of template relationship with users and that users is already created. So next step is to create the template.

[code]
rails generate scaffold templates

class CreateTemplates < ActiveRecord::Migration
def change
create_table :templates do |t|
t.string :name
t.integer :user_id
t.text :content
t.timestamps
end
end
end
[/code]

User has many templates
tempalates belong to user

Since I only want users to be able to edit templates using mercury, i’m going to limit change the routes in mecury. The routing file that mounts the mercury engine (mount Mercury::Engine => ‘/’) produces the following

[code]
get ‘/editor(/*requested_uri)(.:format)’, :to => ‘mercury#edit’
get ‘/mercury/:type/:resource(.:format)’, :to => ‘mercury#resource’
get ‘/mercury/snippets/:name/options(.:format)’, :to => ‘mercury#snippet_options’
get ‘/mercury/snippets/:name/preview(.:format)’, :to => ‘mercury#snippet_preview’
[/code]

So, to limit this to just the templates, replace the requested_uri with templates

[code]

get ‘/editor/templates/:id(.:format)’, :to => ‘mercury#edit’
get ‘/mercury/:type/:resource(.:format)’, :to => ‘mercury#resource’
get ‘/mercury/snippets/:name/options(.:format)’, :to => ‘mercury#snippet_options’
get ‘/mercury/snippets/:name/preview(.:format)’, :to => ‘mercury#snippet_preview’
[/code]

This will also prevent people from poking around your site trying to exploit any holes that may or may not be in mercury rails

Now we need to create a content editable image, In app/views/templates, open up show.html.erb and add this, which gives you a hard coded semi-representation of an 8.5 by 11 area.

[code]
<div id="template_content" class="mercury-region" data-type="editable" contenteditable=true and data-mercury=full style="margin: 25px 30px; height: 900px; width:670px; border-color: 5px solid black;">

<%= raw @template.content%>

</div>
[/code]

Now, we need to add the ability to save when the save button is clicked. Go into your templates_controller and change update to be this
[code]
template = Template.find(params[:id])
template.content = params[:content][:template_content][:value]
template.save!
render text: ""
[/code]

Add Paperclip

Adding paperclip is really a cinch.

First give mercury the ability to use images

[code]rails generate mercury:install:images
bundle install[/code]

First install image magic on your machine, I use ubuntu so
[code]
sudo apt-get update
sudo apt-get install imagemagick[/code]

Now this is installed, create an Amazon Web Services (AWS) S3 account. Create a bucket and an amazon ID. Give the ID full priveldges to that AWS bucket.

Now, create a yaml file in config/ called s3.yml. You can have multiple ones for productiona and development (which is advisable if you plan to run test suites for this)

[code]
development:
access_key_id: gibberish
secret_access_key: longer-more-secret-gibberish
[/code]

Now go to your app/models/mercury/images.rb and change the has_attatched_file to this
[code]
has_attached_file :image, :styles => { :medium => "300×300>", :thumb => "100×100>" },
:storage => :s3,
:s3_protocol => ‘https’,
:s3_credentials => "#{Rails.root}/config/s3.yml",
:path => ":attachment/:id/:style/:filename",
:url => ":attachment/:id/:style/:filename",
:bucket => ‘some-amazon-bucket’
[/code]

Magically, you will now be able to upload images to your content when you save it.

Adding Wicked-PDF

Now is the last part, being able to append .pdf to your images to get this to print out pdfs.

Add support for PDF in config/initializers/mime_types.rb
[code]Mime::Type.register "application/pdf", :pdf[/code]

Then change your show action of the templates_controller to this

[code]def show
@template = Template.find(params[:id])

respond_to do |format|
format.html # show.html.erb
format.json { render json: @template }
format.pdf do
render :pdf => "file.pdf"
end
end
end
[/code]

Last, add a file to app/views/templates called show.pdf.erb with this line of code in it
[code]
<%= raw @template.content%>
[/code]

Assuming that you have followed all the steps and I didn’t forget anything you should now have a webpage with content that you can edit, save, upload images to and save as a pdf. Enjoy!

Resources
Mercury-Rails Railscast
S3 and Paperclip

Possible additional steps (or posts in the future)
Adding liquid templating to this for variables
Scrubbing the raw html for any ruby references

Filed Under: Programming, Ruby on Rails Tagged With: mercury, mercury-rails, paperclip, ruby, ruby on Rails, web document editor, web editor, wicked-pdf

Categories

  • AngularJS
  • Books
  • Devise
  • Elasticsearch
  • ES6
  • Information Security
  • Integrations
  • Javascript
  • Linux
  • Minitest
  • PhoneGap
  • Programming
  • React
  • Redux
  • Ruby
  • Ruby on Rails
  • Stripe
  • Testing
  • Theory
  • TypeScript
  • Uncategorized
  • Vue
  • Webpack