Rails: Coding a Photo Gallery App
This is the first part of a write up detailing how to code out a gallery application for all your favourite pictures. Following this you will have an application deployed in the cloud serving content with ability to add pics and delete. The next part will involve adding authentication and not using scaffolding.
Step 1 Base Build setup
Ensure you have a Ruby ruby 2.6.5
and Rails Rails 5.2.4.2
installed. they’re very simple to install so I’d say ensure you have this setup first.
Install Postgres DB
brew install postgresql
brew services start postgresql
Make sure Postgres DB is running
psql -d postgres
Build a new rails app
cd ~/code/$YOUR_GITHUB_USERNAME
rails new fotogallery --webpack --database=postgresql
cd blog
Create the database on PostgreSQL
git add .
git commit -m "Initial commit"
hub create
git push origin master
Modify your Gemfile
:
# Gemfile
gem 'pg' # No more `gem 'sqlite'` thanks to `--database=postgresql`
Get yarn installed, then bootstrap, jquery and poppers.js
Yarn is a JavaScript package manager, and a faster alternative to NPM. It works almost exactly like NPM, but has one advantage — it creates a yarn.lock
file which you can check into source control, and make sure you’re running the same version of the packages everywhere.
Rails 5.1 will support Yarn out of the box, but in the meanwhile, I wanted to try it out on a Rails 5.0 project. I found the process surprisingly easy and painless. Let’s see how you can add Bootstrap to an existing Rails project through Yarn.
Since rails uses the asset pipeline to minify and compress stylesheets, javascripts, and images, using a sass version of bootstrap is the preferred way of including bootstrap over simply including bootstrap in your application layout view. Rails will compile your bootstrap and assets on the fly and will allow you a lot more flexibility in the design of your apps.
Query is a lightweight, “write less, do more”, JavaScript library. The purpose of jQuery is to make it much easier to use JavaScript on your website. jQuery takes a lot of common tasks that require many lines of JavaScript code to accomplish, and wraps them into methods that you can call with a single line of code.
A library used to manage poppers in web applications. Popper. js is a positioning engine, its purpose is to calculate the position of an element to make it possible to position it near a given reference element. Popper. js has zero dependencies
yarn add bootstrap
yarn add jquery popper.js
Install Simple form: Simple Form was designed to be customized as you need to. Basically it’s a stack of components that are invoked to create a complete html input for you, which by default contains label, hints, errors and the input itself.
# Gemfile
gem 'simple_form'
The build:
bundle install
rails generate simple_form:install --bootstrap
rm app/assets/stylesheets/application.css
touch app/assets/stylesheets/application.scss/* app/assets/stylesheets/application.scss */
@import "bootstrap/scss/bootstrap";
Scaffold a Post model
Open Gemfile
, and comment out the jbuilder
gem.
bundle install
rails generate scaffold article title body:text
rails db:migrate# config/routes.rb
Rails.application.routes.draw do
root to: 'articles#index'
resources :articles, except: :index
endgit add .
git commit -m "Add Article scaffold"rails s
Step 2 Decide upon a Hosting Environment for Production
Get Heroku installed, this is my simple go to hosting environment.
Install on OS X
# OS X
brew install heroku/brew/heroku
Login
heroku login
Create an Heroku app
heroku create $YOUR_APP_NAME --region eu
Let’s deploy!
Push your code to Heroku
git push heroku master
Run a command on Heroku
heroku run rails db:migrate
So we’re setting up an external service to use as a DB link for our images, super important when using a Production service such as Heroku. I’ve chosen Cloudordinary
Get a Cloudordinay Account Setup
Where do we put our secret keys?
We don’t want to share those secret keys on Github, we can use the dotenv gem for security.
# Gemfile
gem 'dotenv-rails', groups: [:development, :test]bundle installtouch .env
echo '.env*' >> .gitignoregit status # .env should not be there, we don't want to push it to Github.
git add .
git commit -m "Add dotenv - Protect my secret data in .env file"
Step 3 How to Store and Serve Images
Cloudinary & Environment
# Gemfile
gem 'cloudinary', '~> 1.12.0'bundle install# .env
CLOUDINARY_URL=cloudinary://298522699261255:Qa1ZfO4syfbOC-***********************8
Upload two pics
curl https://c1.staticflickr.com/3/2889/33773377295_3614b9db80_b.jpg > san_francisco.jpg
curl https://pbs.twimg.com/media/DC1Xyz3XoAAv7zB.jpg > boris_retreat_2017.jpg# rails c
Cloudinary::Uploader.upload("san_francisco.jpg")
Cloudinary::Uploader.upload("boris_retreat_2017.jpg")rm san_francisco.jpg boris_retreat_2017.jpg
And then go to cloudinary.com/console/media_library
<!-- app/views/articles/index.html.erb -->
<%= cl_image_tag("THE_IMAGE_ID_FROM_LIBRARY",
width: 400, height: 300, crop: :fill) %>
<!-- for face detection -->
<%= cl_image_tag("IMAGE_WITH_FACE_ID",
width: 150, height: 150, crop: :thumb, gravity: :face) %>
Crop modes:scale
, fit
, fill
, limit
, pad
, crop
.
You even have thumb
with Face detection
Transformation reference
This exhaustive documentation lists all you can do.
You can change the quality, put some effects (like Instagram), add
an overlay (watermark)
You can upload also other file formats (PDFs, etc.)
Setup
rails active_storage:install
rails db:migrate
This creates two tables in the database to handle the associations between our pictures uploaded on Cloudinary and any Model in our app.
Config
# config/storage.yml
cloudinary:
service: Cloudinary
Replace :local
by :cloudinary
in the config:
# config/environments/development.rb
config.active_storage.service = :cloudinary
Step 4 Code the Model, View, Controller
As its a photo gallery we’re using Multiple images
If you want to have many attached images, you can define a different relationship in your model:
class Article < ApplicationRecord
has_many_attached :photos
end
View & Controller
<!-- app/views/articles/_form.html.erb -->
<%= simple_form_for(article) do |f| %>
<!-- [...] -->
<%= f.input :photos, as: :file, input_html: { multiple: true } %>
<!-- [...] -->
<% end %># app/controllers/articles_controller.rb
def article_params
params.require(:article).permit(:title, :body, photos: [])
end<!-- app/views/articles/show.html.erb -->
<% @article.photos.each do |photo| %>
<%= cl_image_tag photo.key, height: 300, width: 400, crop: :fill %>
<% end %>
Helpful Active Storage methods
@article.photo.attached? #=> true/false
@article.photo.purge #=> Destroy the photo
Behind the scenes
- active_storage_blobs -> Stores attachment metadata (filename, content-type, etc.)
- active_storage_attachments -> Join table between attachments and the relevant models
Step 5 Adding a Delete button to the pictures
ROUTES
Rails.application.routes.draw do resources :articles do resources :comments end get 'articles/index' resources :articles root 'articles#index' resources :articles do resources :photos do match '/remove', to: 'articles#remove', via: 'delete' end endend
Articles CONTROLLER
def remove
@article = Article.find(params[:article_id])
@photo = @article.photos.find(params[:photo_id])
@photo.purge redirect_to article_path(@article), notice: "Upload was successfully removed."
end
VIEW
<% @article.photos.each do |photo| %>
<%= link_to "X", article_photo_remove_path(@article, photo),
:method => :delete,
:data => { :confirm => t('.confirm', :default => 'Are you sure you want to delete this upload?') },
:id =>'delete-faq', :class => 'delete' %></td>
<% end %>
Step 6 Managing the Repo
Ensure you setup a new repo on github and taking care not to “Initialize this repository with a README”. Then on the CLI:
git remote add origin git@github.com:haseebc/[NameOfApp].git
git push -u origin master
So this gives a simple but awesome photo gallery application.
Part 2 will include setting this up without scaffold and authentication.