This is a continuation of the SiteHub project brought up in my Rails Forum Skeleton tutorials (Part 1, Part 2, Part 3, Thoughts), moving on to developing the news-site/blog component that the project will hopefully involve. If you’ve been through those tutorials you’ll likely see some familiar commands and actions, however I’ll do my best to explain everything for newcomers as well.
Firstly, I’ll be using Ruby 2.1.1 or greater and Rails 4.1.1 so make sure whatever you’re using is compatible with this setup. For the uninitiated, Rails is an Model-View-Controller (MVC) web application framework that allows us to develop powerful websites/web applications using a known and trusted paradigm. Model-View-Controller refers to the three prime components of Rails, the data model, including the tables, attributes and logic that enforces it, the views which display output to a user and receive input from them and the controller which takes the input and advises the model of what to update or takes a model change and sends it to the views to be shown to the user.
To start the project, open up a command prompt and navigate to your projects directory (everyone should have one of these IMHO!), then run rails new blog && cd blog
which should create the skeleton of a Rails application and then drop you into it’s directory. Open the Gemfile in this directory and add the following at the end:
gem 'devise'
gem 'redcarpet'
gem 'gravtastic'
Save and exit this file and run bundle install
to install Devise, which will handle user authentication, registration, sessions, etc; Redcarpet which handles Markdown (which we’ll use to handle post formatting) and Gravtastic which allows Gravatar user icons. To set up Devise, run rails generate devise:install
which creates the initialisers and then in config/environments/development.rb you’ll need to add the line config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
just above the end
keyword.
Next we’ll run rails g scaffold post title:string content:text user_id:integer
which generates “scaffolding”, basic entries for a model, controller and views for post. We’ll notice that a post has a title and content, but also a user_id field which we’ll use to connect to the User model when we create it. To create the user model, run rails generate devise User
and then rake db:migrate
to roll all these changes into the schema of the database.
Open up app/models/post.rb and add belongs_to :user
before the end
keyword. In app/models/user.rb, add has_many :posts
before the end
keyword. This creates a relationship, a one-to-many relationship specifically between the User and Post models. This means that every post is connected to a user and each user can have many posts connected to them.
Next, open up app/views/posts/_form.html.erb and above the <div></div>
for the submit button add <%= f.hidden_field :user_id, value: current_user.id %>
and delete the <div></div>
for the user_id section above it. This creates an extra, hidden field when creating a new or editing an old post which contains the currently logged in user ID which can be used to assign a post to a user. Because we created the “user_id” field when we generated the scaffold, the “post_params” method in app/controllers/posts_controller.rb should already permit :user_id
as a parameter. Still in the posts controller, put the code in each of the edit, update and destroy methods inside the following code blog (make sure not to put the function header/end inside though!):
if author_exists = User.where(:id => @post.user_id).first
if current_user == author_exists
[EXISTING CODE HERE]
else
render :show
end
else
render :show
end
This enforces some integrity and access control, making it so users can only delete/edit their own posts. It also checks to see if the author exists to prevent an error when checking against current_user
. The last thing we need to do in the posts controller is add the following line up the top, right under the class definition:
before_filter :authenticate_user!, :only => [:new, :edit, :create, :update, :destroy]
This requires a reader of our blog to be an authenticated user with an active session if they want to create a new post, edit a post or destroy a post.
To neaten things up a bit, open app/views/posts/index.html.erb and edit the links to Edit/Destroy like so:
<td><%= link_to 'Show', post %></td>
<% if current_user == post.user && post.user != nil %>
<td><%= link_to 'Edit', edit_post_path(post) %></td>
<td><%= link_to 'Destroy', post, method: :delete, data: { confirm: 'Are you sure?' } %></td>
<% end %>
Now they’ll only be displayed to the logged in owner of the posts. Next, open up app/models/user.rb again add the following lines to the top, just under the class header:
include Gravtastic
gravtastic
This sets up Gravtastic to fetch authenticated user Gravatars, which we’ll put into play shortly. Now open up app/views/posts/index.html.erb and app/views/posts/show.html.erb and change all references of user_id
to user.email
to make it more human-readable. Also wrap each of those lines in the following code segment to protect in case the user has deleted their account:
<% if post.user != nil %> [OR @post.user FOR show.html.erb]
[EXISTING CODE HERE]
<% else %>
[User Deleted]
<% end %>
In the show.html.erb entry, under the <p></p>
section for the user email drop in the following line:
<% if @post.user != nil %>
<%= image_tag @post.user.gravatar_url(:default => "wavatar") %>
<% end %>
This adds in a Gravatar image for the post user (if they exist). You don’t have to include the (:default => "wavatar")
section but for users where a Gravatar doesn’t exist it puts in a cute image.
Now of course, we’ll need to know if we’re logged in or not and have a link to edit our account settings, so add the following to app/views/layouts/application.html.erb (above the <%= yield %>
tag):
<% if user_signed_in? %>
Logged in as <strong><%= current_user.email %></strong>.
<%= link_to 'Edit profile', edit_user_registration_path, :class => 'navbar-link' %> |
<%= link_to "Logout", destroy_user_session_path, method: :delete, :class => 'navbar-link' %>
<% else %>
<%= link_to "Sign up", new_user_registration_path, :class => 'navbar-link' %> |
<%= link_to "Login", new_user_session_path, :class => 'navbar-link' %>
<% end %>
It’s also important to make sure any notices from the backend get displayed, so quickly nip through each of your existing view pages in app/views/posts and remove any sections near the top that say <p id="notice"><%= notice %></p>
, then add that notice section above the <%= yield %>
section of app/views/layouts/application.html.erb. This makes sure that each page will have a notice display if it’s appropriate.
Lastly, open up config/routes.rb and add root 'posts#index'
after the resources entries, this will mean people accessing the root path of our server (http://localhost:3000/) will be directed to the posts index. If you open up a new terminal session, navigate to the directory the app is in and run rails server
it should start the server and you can check out your handiwork. Congratulations, you’ve now created the basic structure for a simple blog.
You can check out the project this tutorial is based on as it evolves on GitHub. Second part to the tutorial is up too.