In this tutorial we’ll learn how to take advantage of the many plugins and libraries available to quickly add features to your application. First we’ll work with
paperclip, a library that manages file attachments and uploading.
In the past Rails plugins were distributed in zip or tar files that got stored into your application’s file structure. One advantage of this method is that the plugin could be easily checked into your source control system along with everything you wrote in the app. The disadvantage is that it made upgrading to newer versions of the plugin, and dealing with the versions at all, complicated.
These days, all Rails plugins are now ‘gems.’ RubyGems is a package management system for Ruby, similar to how Linux distributions use Apt or RPM. There are central servers that host libraries, and we can install those libraries on our machine with a single command. RubyGems takes care of any dependencies, allows us to pick any options if necessary, and installs the library.
Let’s see it in action. Go to your terminal where you have the rails server running, and type
Ctrl-C. If you have a console session open, type
exit to exit. Then open up
Gemfile and look for the lines like this:
Some lines are commented out because they start with the
# character. By specifying a RubyGem with the
gem command, we’ll tell the Rails application “Make sure this gem is loaded when you start up. If it isn’t available, freak out!” Here’s how we’ll require the paperclip gem, add this near those commented lines:
When you’re writing a production application, you might specify additional parameters that require a specific version or a custom source for the library. With that config line declared, go back to your terminal and run
rails server to start the application again. You should get an error like this:
The last line is key – since our config file is specifying which gems it needs, the
bundle command can help us install those gems. Go to your terminal and:
It should then install the paperclip RubyGem with a version like 5.1.0. In some projects I work on, the config file specifies upwards of 18 gems. With that one
bundle command the app will check that all required gems are installed with the right version, and if not, install them.
Note: You may need to reload your rails server for the paperclip methods to work.
Now we can start using the library in our application!
We want to add images to our articles. To keep it simple, we’ll say that a single article could have zero or one images. In later versions of the app maybe we’d add the ability to upload multiple images and appear at different places in the article, but for now the one will show us how to work with paperclip.
First we need to add some fields to the Article model that will hold the information about the uploaded image. Any time we want to make a change to the database we’ll need a migration. Go to your terminal and execute this:
That will create a file in your
db/migrate/ folder that ends in
_add_paperclip_fields_to_article.rb. Open that file now.
Remember that the code inside the
change method is to migrate the database forward, and Rails should automatically figure out how to undo those changes. We’ll use the
remove_column methods to setup the fields paperclip is expecting:
Then go to your terminal and run
has_attached_file method is part of the paperclip library. With that declaration, paperclip will understand that this model should accept a file attachment and that there are fields to store information about that file which start with
image_ in this model’s database table.
From version 4.0, all attachments are required to include a content_type validation, a file_name validation, or to explicitly state that they’re not going to have either. Paperclip raises MissingRequiredValidatorError error if you do not do this. So, we add the validates_attachment_content_type line so that our model will validate that it is receiving a proper filetype.
We also have to deal with mass assignment! Modify your
app/helpers/articles_helper.rb and update the
article_params method to permit an
First we’ll add the ability to upload the file when editing the article, then we’ll add the image display to the article show template. Open your
app/views/articles/_form.html.erb view template. We need to make two changes…
In the very first line, we need to specify that this form needs to accept “multipart” data. This is an instruction to the browser about how to submit the form. Change your top line so it looks like this:
Then further down the form, right before the paragraph with the save button, let’s add a label and field for the file uploading:
If your server isn’t running, start it up (
rails server in your terminal). Then go to http://localhost:3000/articles/ and click EDIT for your first article. The file field should show up towards the bottom. Click the
Choose a File and select a small image file (a suitable sample image can be found at http://www.hungryacademy.com/images/beast.png). Click SAVE and you’ll return to the article index. Click the title of the article you just modified. What do you see? Did the image attach to the article?
When I first did this, I wasn’t sure it worked. Here’s how I checked:
- Open a console session (
rails consolefrom terminal)
- Find the ID number of the article by looking at the URL. In my case, the url was
http://localhost:3000/articles/1so the ID number is just
- In console, enter
article = Article.find(1)
- Right away I see that the article has data in the
image_file_nameand other fields, so I think it worked.
article.imageto see even more data about the file
Ok, it’s in there, but we need it to actually show up in the article. Open the
app/views/articles/show.html.erb view template. Before the line that displays the body, let’s add this line:
Then refresh the article in your browser. Tada!
When first working with the edit form I wasn’t sure the upload was working because I expected the
file_field to display the name of the file that I had already uploaded. Go back to the edit screen in your browser for the article you’ve been working with. See how it just says “Choose File, no file selected” – nothing tells the user that a file already exists for this article. Let’s add that information in now.
So open that
app/views/articles/_form.html.erb and look at the paragraph where we added the image upload field. We’ll add in some new logic that works like this:
- If the article has an image filename
*Display the image
- Then display the
file_fieldbutton with the label “Attach a New Image”
So, turning that into code…
Test how that looks both for articles that already have an image and ones that don’t.
When you “show” an article that doesn’t have an image attached it’ll have an ugly broken link. Go into your
app/views/articles/show.html.erb and add a condition like we did in the form so the image is only displayed if it actually exists.
Now our articles can have an image and all the hard work was handled by paperclip!
Yes, a model (in our case an article) could have many attachments instead of just one. To accomplish this you’d create a new model, let’s call it “Attachment”, where each instance of the model can have one file using the same fields we put into Article above as well as an
article_id field. The Attachment would then
belong_to an article, and an article would
Paperclip supports automatic image resizing and it’s easy. In your model, you’d add an option like this:
This would automatically create a “medium” size where the largest dimension is 300 pixels and a “thumb” size where the largest dimension is 100 pixels. Then in your view, to display a specific version, you just pass in an extra parameter like this:
If it’s so easy, why don’t we do it right now? The catch is that paperclip doesn’t do the image manipulation itself, it relies on a package called imagemagick. Image processing libraries like this are notoriously difficult to install. If you’re on Linux, it might be as simple as
sudo apt-get install imagemagick. On OS X, if you have Homebrew installed, it’d be
brew install imagemagick. On windows you need to download and copy some EXEs and DLLs. It can be a hassle, which is why we won’t do it during this class.
If you do manage to get imagemagick installed, be advised that the custom sizes will only take affect on those images uploaded after the imagemagick installation. In otherwords, when the image is uploaded - Paperclip will use Imagemagick to create the customized sizes specified on the
has_attached_file line. However, there is a fix for this! Whenever you change your
has_attached_file styles - whether adding, editing, or deleting - you can easily reprocess all existing images with
rake paperclip:refresh CLASS=Article. Just replace ‘Article’ with the model name containing your Paperclip images in your other projects. For more information, see this helpful post in the Paperclip documentation.
All the details about Sass can be found here: http://sass-lang.com/
We’re not focusing on CSS development, so here are a few styles that you can copy & paste and modify to your heart’s content.
Place the following styles in a new file and save it as styles.css.scss in
If you refresh the page, it should look slightly different! But we didn’t add a reference to this stylesheet in our HTML; how did Rails know how to use it? The answer lies in Rails’ default layout.
We’ve created about a dozen view templates between our different models. Imagine that Rails didn’t just figure it out. How would we add this new stylesheet to all of our pages? We could go into each of those templates and add a line like this at the top:
Which would find the Sass file we just wrote. That’s a lame job, imagine if we had 100 view templates. What if we want to change the name of the stylesheet later? Ugh.
Rails and Ruby both emphasize the idea of “D.R.Y.” – Don’t Repeat Yourself. In the area of view templates, we can achieve this by creating a layout. A layout is a special view template that wraps other views. Rails has given us one already:
Check out your
Whatever code is in the individual view template gets inserted into the layout where you see the
yield. Using layouts makes it easy to add site-wide elements like navigation, sidebars, and so forth.
stylesheet_link_tag line? It mentions ‘application.’ That means it should load up
app/assets/stylesheets/application.css… Check out what’s in that file:
There’s that huge comment there that explains it: the
require_tree . line automatically loads all of the stylesheets in the current directory, and includes them in
application.css. Fun! This feature is called the
asset pipeline, and it’s pretty new to Rails. It’s quite powerful.
Now that you’ve tried out a plugin library (Paperclip), the “A Few Gems” tutorial is complete!