Home Application-development Rails 4 Application Development HOTSHOT

Rails 4 Application Development HOTSHOT

By Saurabh Bhatia
books-svg-icon Book
Subscription
$10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime!
What do you get with a Packt Subscription?
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
BUY NOW $10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime!
Subscription
What do you get with a Packt Subscription?
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
  1. Free Chapter
    A Social Recipe-sharing Website
About this book

Rails is a rapidly moving, open source, web development framework, and keeping up to speed with it is a big task. You might have already built applications using it, but there have been significant changes in the syntax and semantic of the Rails framework in the latest upgrade.

Rails 4 Application Development Hotshot shows you how to build the most popular types of applications using Rails 4, and highlights new ways to do things. The book also closely follows lots of the best practices, gems, and popular solutions already known to the community, and tracks the changes in these. This book brings new ideas to refactor and restructure code to make it perform better in production, and enables you to write production-ready code.

Publication date:
April 2014
Publisher
Packt
Pages
378
ISBN
9781783286294

 

Chapter 1. A Social Recipe-sharing Website

Food-recipe websites have been in existence since the advent of the Internet. Food.com, thefoodnetwork.com, and bbcgoodfood.com are some of most visited sites. Food is also one of the most popular searched categories on the Internet. Most of these websites have experts writing content for them. In this project, we will develop a website where amateur users can upload their recipes and those recipes can be viewed and shared by others and generated by several users. The recipes can be shared over various social networking sites by the readers.

 

Mission briefing


Our goal is to create a very basic social website where users can sign up and create recipes they know the best. Other users can filter these recipes based on their interests, tastes, and food preferences and share it on Facebook, Twitter, or other social networking sites of their choice. At the end of this project, we should be able to perform the following tasks:

  • Create an application

  • Know what's the best way for creating an application

  • Make use of some of the new features available for creating the application

User stories are a very important part of the entire project. They can make or break project schedules and have a drastic effect on the product in the long run. Once defined, our use cases will have steps on how a user interacts with the application and the validations required for it to pass. It will be much easier for us to keep this as a reference while coding. A good specification, both visual and technical, goes a long way in helping developers save time.

The home page will contain feed of the entire system—users who have newly joined the system, created new recipes, and edited new recipes. The screenshot of the home page of the final system is as follows:

Why is it awesome?

Everyone loves food, and some of us like to cook food too. The simplest and the most interesting way to build momentum for development is with a simple project. We will use this project to lay the foundation of Rails 4 comprehensively and build a base for the upcoming projects. Developers who have been using earlier versions of Rails will get a chance to work with new features in Version 4.0.0. Also, this will set the tone for the rest of the book, in terms of the process we will follow or we should follow while building our applications. We are following a test-driven development approach in the context of Rails 4. So, we will get a fair amount of exposure to the minitest framework, which has been newly introduced, and we will follow it up with some basics of ActiveRecord. While running through this, we will also work with Bootstrap 3.0 to style our views.

Your Hotshot objectives

While building this application, we will complete the following tasks:

  • Creating mockups

  • Adding test data and creating tests

  • Adding categories

  • Creating and adding recipes

  • Creating associations – recipes and categories

  • Adding authentication

  • Beautifying your views

Mission checklist

We need the following software installed on the system before we start with our mission:

  • Ruby 1.9.3 / Ruby 2.0.0

  • Rails 4.0.0

  • MySQL 6

  • Bootstrap 3.0

  • Sass

  • Devise

  • Git

  • A tool for mockups; I personally use MockFlow

 

Creating mockups


Before we actually start developing the application, we will build two types of specifications: visual specifications called mockups and technical specifications called user stories. Visual imagination needs a fair bit of creativity and is best left to the designers; however, for our reference here, we will see how to create mockups in case you are working on an end-to-end process.

Prepare for lift off

There are several mockup tools available online and are free to download and install. Balsamiq (https://www.mybalsamiq.com), MockFlow (http://mockflow.com), and mockingbird (https://gomockingbird.com/) are some of the tools that I have explored and are fairly useful. We will use MockFlow for our projects. Sign up and create a free account with MockFlow.

Engage thrusters

For creating mockups, we will perform the following steps:

  1. Setting up a project in MockFlow is pretty straightforward. As soon as we log in to the account, we will be able to see an Add Project button. Once we click on it, the following screen shows up with various options for setting up different kinds of projects. We are going to select From Scratch and build our Wireframes using the given set of tools.

  2. We will select the From Scratch option present under the Wireframe Project screen, name it, and proceed with the setup of the pages we want in our application.

  3. The tool to the right contains two tabs:

    • pages: With this option, you can Create, Sort, Duplicate, and Delete pages in your application

    • components: With this option, the textboxes, text areas, scrollbars, logos, images, and different elements of the page can be simply dragged-and-dropped from the component panel to the canvas on the center of the page to create a Wireframe

  4. Let's start building our first mockup. Drag-and-drop the Layout Builder icon located in the components panel, and using your mouse, create and resize it so it fits on the page.

  5. This layout suits our application needs because our aim is to build an application with a filter bar to the left that would allow users to filter categories with ease. The central portion will display the content and will contain the list of various recipes. The portion to the left will contain the list of various categories.

  6. The header will contain the logo, login details, and dashboard links, whereas the footer will contain copyright information and company information links.

  7. After resizing the layout builder, we will add the logo and images to the header. In order to do so, we will first drag-and-drop the Image component from the components panel and double-click on it. We will be presented with a modal box to manage and upload images. Browse and upload images using this tool. Once an image is selected, just drag and move it to the position where you want to see the logo placed.

  8. The next step would logically be to build the inner page. This page will have some text on it. We will drag the title and text from the components bar and drop it to the central part of the layout.

  9. Add checkboxes and the remaining elements to the mockup.

  10. We will finally add some checkboxes to the left bar for filters. This includes food type, food preferences, and cuisines in order to properly categorize our recipes.

  11. We can now figure out other elements of the page, for example, in order to create links such as Login/Signup, and About Us, we can use the Label component from the components panel.

Objective complete – mini debriefing

As seen in the previous steps, we added various page elements, including text areas, a title, and checkboxes to our page. We can use these page elements to create mockups for all the pages. Mockups for the home page and recipe page are shown in the following two screenshots:

The home page now looks complete with different links and information in the footer shown as follows:

Classified intel

The options offered in MockFlow include building mockups for the following:

  • Web applications

  • Mobile applications

  • Themes specific to a particular CMS, or using a particular CSS framework such as Bootstrap

  • Simple Wireframing from scratch or from templates

 

Adding test data and creating tests


Rails does a lot of work for us by providing us with generators, right from a blank application to different parts of the application. The trick lies in using it only when required. Our first application will consider a very simple use case of generators, but we will scarcely use them in subsequent projects. In this task, we will generate our application and write tests before we write the code.

Prepare for lift off

As MySQL and PostgreSQL are the most common RDBMS around, we're going to use either of them for building most of our applications. The default database in the development mode with Rails is SQLite. Make sure you have one of these databases working on your system and also make sure that the connection with Rails is working. We will use MySQL for most of our projects including this one.

Engage thrusters

The steps for creating a new application and setting up the database (db) are as follows:

  1. Let us first create a blank application with a MySQL database as the default database using the following command:

    :~/$ rails new curry-nation -d mysql
    
  2. Now we can go ahead and set up the application's database.yml file under config to connect to the system's database. You would need to make this file suit the database that you are using. We are using MySQL; likewise, you can edit the file for the database of your choice.

    config/database.yml
    development
      adapter: mysql2
      encoding: utf8
      database: curry-nation_development
      pool: 5
      username: root
      password: 
      socket: /var/run/mysqld/mysqld.sock
    test:
      adapter: mysql2
      encoding: utf8
      database: curry-nation_test
      pool: 5
      username: root
      password: 
      socket: /var/run/mysqld/mysqld.sock
    
    production:
      adapter: mysql2
      encoding: utf8
      database: curry-nation_production
      pool: 5
      username: root
      password: 
      socket: /var/run/mysqld/mysqld.sock
  3. Once the database is set up, we need to create the database using the following commands:

    :~/curry-nation$ rake db:create
    :~/curry-nation$ rake db:migrate
    
  4. We will first prepare our fixtures. Fixtures contain test data that loads into the test database. These are placed in the fixtures folder under test with the filename recipes.yml:

    test/fixtures/recipes.yml
    curry:
      title: Curry
      food_preference_id: 1
      food_type: 1
      cuisine_id: 1
      servings: 1
      cooking_time: 1
      level_of_difficulty: Easy
      ingredients: Onions Tomatoes Salt Oil
      procedure: Heat Oil Chop Onions, tomatoes and Salt to it. 
  5. Once the fixtures are ready, we can populate the db with fixtures. However, we have not yet created the models and tables. Hence, we will load the fixtures' data once we create our models.

  6. We can now go ahead and write integration tests. We will now add an integration test and create it line by line:

    ~/test/integration$ recipe_test.rb
    
  7. We will load the test helper that will load the test database and other dependencies for the test:

    require 'test_helper'
  8. Load the test record and navigate to the new recipe page:

    test/integration/recipe_test.rb
        curry = recipes(:curry)
        get "/recipes/new"
  9. Post the data to the new method and assert for a success response. At this point, it even checks for validations if they are defined. Depending on this, it would be redirected to the index page:

    test/integration/recipe_test.rb
        assert_response :success
        post_via_redirect "/recipes/new", title: recipes(:curry).title
  10. We can now prepare the database and run the test:

    :~/curry-nation/test/integration$ rake db:create RAILS_ENV="test"
    (in /curry-nation)
    r:~/curry-nation/test/integration$ rake test recipe_test.rb 10 tests, 10 assertions, 10 failures, 0 success, 0 skips
    
  11. The final integration test looks like this:

    test/integration/recipe_test.rb
    class RecipeFlowsTest < ActionDispatch::IntegrationTest
      fixtures :recipes
      test "create recipes" do
        https!
        curry = recipes(:curry)
        get "/recipes/new"
        assert_response :success
        post_via_redirect "/recipes/new", title: recipes(:curry).title
        assert_equal '/recipes', path
        assert_equal 'Create Recipe', flash[:notice]
        https!(false)
        get "/recipes"
        assert_response :success
        assert assigns(:recipes)
      end
    end
  12. Our integration tests look at the way the pages and routes work with each other. Controller tests look at how data is passed between these calls, and the methods call themselves.

  13. Set up a recipe variable and get the index method:

    test/integration/recipe_test.rb
    class RecipesControllerTest < ActionController::TestCase
      setup do
        @recipe = recipes(:one)
      end
      test "should get index" do
        get :index
        assert_response :success
        assert_not_nil assigns(:recipes)
      end
  14. We will use assert to get a new page in our controller test:

    test/controllers/recipes_controller_test.rb
      test "should get new" do
        get :new
        assert_response :success
      end
  15. We will also perform a test for creating a recipe:

    test/controllers/recipes_controller_test.rb
      test "should create recipe" do
        assert_difference('Recipe.count') do
          post :create, recipe: { cooking_time: @recipe.cooking_time, cuisine_id: @recipe.cuisine_id, food_preference_id: @recipe.food_preference_id, food_type: @recipe.food_type, ingredients: @recipe.ingredients, level_of_difficulty: @recipe.level_of_difficulty, procedure: @recipe.procedure, servings: @recipe.servings, title: @recipe.title }
        end
    
        assert_redirected_to recipe_path(assigns(:recipe))
      end
  16. Add a test for showing a recipe using the following code:

    test/controllers/recipes_controller_test.rb
      test "should show recipe" do
        get :show, id: @recipe
        assert_response :success
      end
  17. We will test the edit and update methods:

    test/controllers/recipes_controller_test.rb
      test "should get edit" do
        get :edit, id: @recipe
        assert_response :success
      end
    
      test "should update recipe" do
        patch :update, id: @recipe, recipe: { cooking_time: @recipe.cooking_time, cuisine_id: @recipe.cuisine_id, food_preference_id: @recipe.food_preference_id, food_type: @recipe.food_type, ingredients: @recipe.ingredients, level_of_difficulty: @recipe.level_of_difficulty, procedure: @recipe.procedure, servings: @recipe.servings, title: @recipe.title }
        assert_redirected_to recipe_path(assigns(:recipe))
      end
  18. Lastly, we will check for deletions:

    test/controllers/recipes_controller_test.rb
      test "should destroy recipe" do
        assert_difference('Recipe.count', -1) do
          delete :destroy, id: @recipe
        end
    
        assert_redirected_to recipes_path
      end
    end

Tip

Downloading the example code

You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.

Objective complete – mini debriefing

As we saw in the previous task, the structure of the default testing framework in Rails 4 includes the respective style folder structure, which is much cleaner and nicely abstracted compared to the earlier versions. This is how it looks:

:~/curry-nation/test$ ls
controllers  fixtures  helpers  integration  mailers  models  test_helper.rb

The test folder is self-descriptive in terms of the folder structure and clearly denotes which test belongs to which part of the system.

Here, we have prepared the test data and written tests that match the specifications. This will help us emulate our functionality. We are now ready to write some code in order to run our tests. The tests in this case failed because there is no code for the tests to run.

Classified intel

Testing is the backbone of your application. If you don't write tests, you are opening a Pandora's box for yourself.

 

Adding categories


To make the content of the website easily browsable, it makes sense to categorize it in different ways according to the diversity of choice a user has regarding food recipes. In this task, we will build navigation bars that would be visible on the left-hand side. Actually, it goes much deeper than just being the navigation bar. This is because it has to be built in a way that allows us to effectively search for data in future. So, for us, categories are a way to arrange data and make it more accessible, and in this task, we will see how to create categories.

Categories in our application are divided into three parts:

  • Food preferences: Food preferences include the value system of users. They might like dairy free, vegan, vegetarian, meat, and so on. Recipes are categorized on the basis of this.

  • Food types: Food types denote whether the food is a main course, a curry, a side dish, or a dessert.

  • Cuisines: The final categorization is on the basis of cuisine.

Engage thrusters

The steps for adding categories are as follows:

  1. We first need to create models that can be associated with the recipes:

    :~/curry-nation$ rails g model food_type name:string
          invoke  active_record
          create db/migrate/20130803103254_create_food_types.rb
          create    app/models/food_type.rb
          invoke    test_unit
          create      test/models/food_type_test.rb
          create      test/fixtures/food_types.yml
  2. We can't leave the categories blank, and they need some default data. We do not have an interface to load categories so we will use the seeds' data by adding default data using seed scripts.

  3. This generates a food type model, fixtures, blank tests, and table migrations. These values have to be available in the database in order to be used with the recipes. We will load them using seeds.rb.

    db/seeds.rb
    food_types = ["Curry", "Dessert", "Sides","Breakfast"]
    food_types.each{|d| FoodType.where(:name => d).create}

    Once done, we'll run the following code:

    rake db:migrate
    rake db:seed

The following steps will help us to modify seeds:

  1. The default seeds, if simply defined, can create duplicate records in the database and might fail validations. This is because every time we run rake db:seeds, it runs all the queries again. In order to avoid this, we can add first_or_create after the data, which checks for the record in the database before adding it to the database:

    db/seeds.rb
    food_types.each{|d| FoodType.where(:name => d).first_or_create}
  2. Likewise, we can create other models related to categories in the same way:

    :~/curry-nation$ rails g model food_preference name:string
        invoke  active_record
        create    db/migrate/20130803110704_create_food_preferences.rb
        create    app/models/food_preference.rb
        invoke    test_unit
        create      test/models/food_preference_test.rb
        create      test/fixtures/food_preferences.yml
    :~/curry-nation$ rake db:migrate
    ==  CreateFoodPreferences: migrating ==========================================
    -- create_table(:food_preferences)
       -> 0.1313s
    ==  CreateFoodPreferences: migrated (0.1315s) =================================
    
    :~/curry-nation$ rails g model cuisine name:string
        invoke  active_record
        create    db/migrate/20130803111845_create_cuisines.rb
        create    app/models/cuisine.rb
        invoke    test_unit
        create      test/models/cuisine_test.rb
        create      test/fixtures/cuisines.yml
    :~/curry-nation$ rake db:migrate
    ==  CreateCuisines: migrating ==========================================
    -- create_table(:cuisines)
       -> 0.1107s
    ==  CreateCuisines: migrated (0.1109s) ===================================
  3. Load them into the database as follows:

    db/seeds.rb
    food_preferences = ["Vegetarian", "Vegan", "Meat","Dairy"]
    food_preferences.each{|d| FoodPreference.where(:name => d).first_or_create}
    
    cuisines = ["Italian", "Mexican", "Indian","Chinese"]
    cuisines.each{|d| Cuisine.where(:name => d).first_or_create}
    :~/curry-nation$ rake db:seed
  4. For accessing the console and checking the entered data, we can load the Rails console and check whether all the values are present in the database or not:

    :~/curry-nation$ rails c
    Loading development environment (Rails 4.0.0)
    1.9.3-p327 :002 > FoodType.all
      FoodType Load (0.9ms)  SELECT `food_types`.* FROM `food_types`
     => #<ActiveRecord::Relation [#<FoodType id: 1, name: "Curry", created_at: "2013-08-03 10:57:37", updated_at: "2013-08-03 10:57:37">, #<FoodType id: 2, name: "Dessert", created_at: "2013-08-03 10:57:37", updated_at: "2013-08-03 10:57:37">, #<FoodType id: 3, name: "Sides", created_at: "2013-08-03 10:57:37", updated_at: "2013-08-03 10:57:37">, #<FoodType id: 4, name: "Breakfast", created_at: "2013-08-03 10:57:37", updated_at: "2013-08-03 10:57:37">]> 
    1.9.3-p327 :003 > FoodPreference.all
      FoodPreference Load (0.7ms)  SELECT `food_preferences`.* FROM `food_preferences`
     => #<ActiveRecord::Relation [#<FoodPreference id: 1, name: "Vegetarian", created_at: "2013-08-03 11:15:56", updated_at: "2013-08-03 11:15:56">, #<FoodPreference id: 2, name: "Vegan", created_at: "2013-08-03 11:15:56", updated_at: "2013-08-03 11:15:56">, #<FoodPreference id: 3, name: "Meat", created_at: "2013-08-03 11:15:56", updated_at: "2013-08-03 11:15:56">, #<FoodPreference id: 4, name: "Dairy", created_at: "2013-08-03 11:15:56", updated_at: "2013-08-03 11:15:56">]> 
    1.9.3-p327 :004 > Cuisine.all
      Cuisine Load (0.6ms)  SELECT `cuisines`.* FROM `cuisines`
     => #<ActiveRecord::Relation [#<Cuisine id: 1, name: "Italian", created_at: "2013-08-03 11:28:54", updated_at: "2013-08-03 11:28:54">, #<Cuisine id: 2, name: "Mexican", created_at: "2013-08-03 11:28:54", updated_at: "2013-08-03 11:28:54">, #<Cuisine id: 3, name: "Indian", created_at: "2013-08-03 11:28:54", updated_at: "2013-08-03 11:28:54">, #<Cuisine id: 4, name: "Chinese", created_at: "2013-08-03 11:28:54", updated_at: "2013-08-03 11:28:54">]> 

Objective complete – mini debriefing

We have successfully created category-related models and loaded values to them using seeds. We also saw the best practice for creating seeds so that we can avoid loading duplicate data in the database.

Seeds should be defined for all kinds of default data in the system. Also, the process of adding seeds should be incremental and ongoing. Some might argue that it is very close to fixtures; however, fixtures belong to the test bed, whereas seeds are generic data that should be loaded by default in the system.

 

Creating and adding recipes


Scaffolding is the easiest way to start, but as the word itself suggests, it's just scaffolding. Rails goes much beyond that. Also, whether to use it or not in practical projects is a huge debate. However, I feel that we can use it to start but it's important that we build our functionalities in it. This will provide us with a template that adheres to best practices to start with, and then builds our code upon it.

Engage thrusters

After successfully writing our tests, we will write our code to make sure our tests run.

  1. We will first understand our use case:

    • User story; that is, to create a recipe

    • User enters the title

    • Users selects food preferences, food type, cuisine, and the level of difficulty

    • User enters servings, cooking time, ingredients, and procedure

    • User saves the recipe

    Note

    Make sure that the validations required are as follows:

    • Title is mandatory

    • Food preferences, food type, and cuisine are mandatory

  2. We will start with generating a scaffold. The general format is to write the command followed by the name of model, fields, and datatype of each field shown as follows:

    :~/curry-nation$ rails g scaffold recipe title:string  cooking_time:string difficulty_level:string food_type_id:integer food_preference_id:integer cuisine_id:integer ingredients:text procedure:text
    

    This will create files that include model, controller, basic views, and skeleton tests.

  3. We can now see what we have already created. In order to see what we have created so far, let's fire up our server and see what we just created by navigating to localhost:3000.

  4. Now, as we can see, the category values that we added previously are blank textboxes in our form. We would need to create dropdowns for each one of them so that they are selected and sent to the db.

        <div class="form-group">
          <%= f.label :food_preference %><br>
          <%= f.select(:food_preference_id, options_from_collection_for_select(FoodPreference.all, :id, :name), {:prompt => 'Please Choose'}, :class => "form-control")  %>
        </div>
      
    <div class="form-group">
          <%= f.label :food_type %><br>
          <%= f.select(:food_type_id, options_from_collection_for_select(FoodType.all, :id, :name), {:prompt => 'Please Choose'}, :class => "form-control")  %>
        </div>
    
        <div class="form-group">
          <%= f.label :cuisine %><br>
          <%= f.select(:cuisine_id, options_from_collection_for_select(Cuisine.all, :id, :name), {:prompt => 'Please Choose'}, :class => "form-control")  %>
        </div>
  5. As you can see in the preceding code, we are able to populate the values in the select box from our database tables and pass the IDs to the recipe table of the database.

  6. We will define an array in the recipe model and access it in the view. There is also another dropdown required for "level of difficulty" to be defined inside the recipe model. We can create a simple array with the names of difficulty levels as follows:

    app/models/recipe.rb
    DIFFICULTY=%w(Easy Medium Hard)
  7. We can now call the level of difficulties directly inside our views and access the array values by calling it on the model name using Recipe::DIFFICULTY, shown as follows:

    app/views/recipes/_form.html.erb
       <div class="form-group">
          <%= f.label :difficulty_level %><br>
          <%=f.select :difficulty_level, Recipe::DIFFICULTY,{} , :class => "form-control"%>
        </div>

Objective complete – mini debriefing

At the end of this task, we will be able to create a recipe and add them to categories. However, we have not yet created a real association between them as we discussed earlier. We also saw that we can define arrays and call them directly from our model class like we did in the case of food type, food preferences, and cuisines.

 

Creating associations – recipes and categories


Associations are important in order to pass and access data between the models. ActiveRecord adds one of the major productivity boosts by avoiding writing SQL by hand. In this task, we will define relationships between different models and tell them how they should behave with each other.

Engage thrusters

We will discuss creating an association between the recipe and category models in this section.

  1. According to our use case, each food type can have multiple recipes associated to it. This is because logically speaking, a category will have many recipes associated to it.

    app/models/food_type.rb
    class FoodType < ActiveRecord::Base
      has_many :recipes
    end
  2. Also, each recipe belongs to a particular food type, which we can define by adding a belongs_to rule to the recipe model.

    app/models/recipe.rb
    class Recipe < ActiveRecord::Base
      belongs_to :food_type
    end
  3. In the same way, we can associate other categories to the recipe model too, shown as follows:

    app/models/food_preference.rb
    class FoodPreference < ActiveRecord::Base
      has_many :recipes
    end
    app/models/cuisine.rb
    class Cuisine < ActiveRecord::Base
     has_many :recipes
    end
  4. We can now display these values in our views in the following ways:

    app/views/recipes/index.html.erb
     <td><%= recipe.food_preference.name %></td>
     <td><%= recipe.food_type.name %></td>
      <td><%= recipe.cuisine.name %></td>

Objective complete – mini debriefing

We have successfully set up associations between the models, and they can now be accessed seamlessly between controllers and views.

Right associations are not only important for properly passing data between controllers and models, but also for critical tasks such as searching.

 

Adding authentication


We want legitimate people to post on our website and avoid spam. In order to do so, authentication is a must. In this task, we will see how to use devise to add authentication to the application. The choice of devise is quite obvious because it is a very complete authentication engine in every sense. It is also very easily extensible and hence the best choice for this.

Prepare for lift off

Devise is the most popular and up-to-date solution of authentication with Rails. We will use it to add user authentication to our website.

Engage thrusters

Let's have a look at how can we use devise to add user authentication to our website.

  1. The use case for devise is as follows:

    • User story; that is, user sign-up

    • User clicks on sign-up

    • User fills in the e-mail

    • User enters and confirms the password

    • If validations are passed, the user gets a valid account

    Note

    The points that are checked for validations are:

    • Is the e-mail format valid?

    • Does the password comprise a minimum of eight characters in length?

    • Does the information entered in the password and confirm password fields match?

  2. We can add devise and generate the basic authentication by adding the following code to the Gemfile and running the bundle:

    gem 'devise', github: 'plataformatec/devise'
    
  3. We can install devise using the following command line. We can then go ahead and perform the installation of basic configuration files of devise:

    :~/curry-nation$rails g devise:install
    
  4. This will create two files for us: initializers/devise.rb and locales/devise.en.yml. We can now generate our user model:

    :~/curry-nation$rails g devise user
    
  5. The following command line will mount the Devise application routes on the routes.rb file:

    config/routes.rb
      devise_for :users
    
  6. We will now protect selected methods. Devise comes with a set of methods that can be readily used with user-related resources in our application. We will first proceed with the protection of our specific methods inside our recipe model:

    app/controllers/recipes_controller.rb
     before_filter :authenticate_user!, only: [:new, :edit, :create, :update, :destroy]
    
  7. This will allow us to protect the new, edit, create, update, and destroy methods using user authentication. The current_user method allows access to the logged-in user in the session. We can display the e-mail of the user using this method.

  8. Let's write a "create user login" user story as follows:

    • User story; that is, user login

    • User clicks on the login link

    • User fills in the username and password

    • Validations are applied to check whether both the username and password are present in the database

  9. We can also protect specific methods in views. The if user_signed_in? method is a conditional method provided by Devise. We can use it to check whether the user session is in progress or not. If it is, then we can display the e-mail of the user and the logout link; if not, then display the login and sign-up links:

    app/views/layouts/application.html.erb
    <ul class="nav navbar-nav pull-right">
        <% if user_signed_in? %>
        <li><%=link_to "#{current_user.email}" %></li>
        <li class="active"><%= link_to "Logout", destroy_user_session_path%></li>
        <%else%>
        <li><%= link_to "Login", new_user_session_path %></li>
        <li class="active"><%= link_to "SignUp", new_user_registration_path%></li>
        <%end%>
    </ul>
  10. We can make the methods visible only to the logged-in users. Also, though we have already protected our new and edit methods using authentication, we can hide them altogether from the views, again by using the if user_signed_in? method:

    app/views/recipes/index.html.erb
        <% if user_signed_in? %>
        <td><%= link_to 'Edit', edit_recipe_path(recipe), :class=>"btn btn-success btn-small"%></td>
        <td><%= link_to 'Delete', recipe, method: :delete, data: { confirm: 'Are you sure?' }, :class=>"btn btn-danger btn-small" %></td>
            <%end%>

Objective complete – mini debriefing

At the end of this task, our application has devise-based authentication for login and sign-up functionalities. We also protected certain methods and made them accessible only after we completed the login process. Lastly, we looked at various methods to pass user data to session objects such as current_user.

Devise also supports OpenLDAP and API methods for extending authentication for our apps on the mobile platform.

 

Beautifying your views


Proper styling is equally important as it can make or break your website despite writing all of the code correctly. With a myriad of websites at a user's disposal and so many new user experiences, the user interface takes on a huge role.

We will use Twitter's Bootstrap framework not only for our convenience, but also to ensure good quality code for the markup. The main advantages Bootstrap has to offer are as follows:

  • Clean and high performing markup

  • Responsiveness

  • HTML4 and HTML5 doctype standards compliant

  • Easily customizable

  • Uses the latest design practices

Prepare for lift off

Read Bootstrap's Getting started task at http://getbootstrap.com/getting-started/ and get started with Version 3.

Engage thrusters

In this task, we will see some of the styling classes of Bootstrap and use it to style our application:

  1. Add Bootstrap to the asset pipeline.

    We will use the bootstrap-rails gem in order to add Bootstrap to our asset pipeline. Add the following line to the Gemfile and bundle install:

    gem 'anjlab-bootstrap-rails', :require => 'bootstrap-rails'
  2. Make CSS and JavaScript available to the asset pipeline. Then add the following line to application.css. This is for informing the asset pipeline to access Bootstrap files from this folder:

    app/assets/application.css
     *= require twitter/bootstrap
  3. Add the required directive to the application.js file to make all the Bootstrap JavaScripts available to the Rails application:

    app/assets/application.js
    //= require twitter/bootstrap

    Then add the necessary style to the layouts.

  4. Once we've added these, all the CSS and .js files in Bootstrap are ready to be used in our application. This is how our code looks at the moment. There is practically no styling and only the default methods of scaffold.css are being used:

    app/views/layouts/application.html.erb
    <body>
        <ul>
            <li><%= link_to "Recipes", recipes_path %></li>
        </ul>
        <ul>
            <% if user_signed_in? %>
            <li><%=link_to "#{current_user.email}" %></li>
            <li><%= link_to "Logout", destroy_user_session_path%></li>
            <%else%>
            <li><%= link_to "Login", new_user_session_path %></li>
            <li><%= link_to "SignUp", new_user_registration_path%></li>
            <%end%>
        </ul>
        <%= yield %>
    </body>
  5. Create a layout that consists of two columns.

  6. According to our mockup, we intend to make a two-column layout for our application. The left bar contains various categories, and the central portion is present for rendering the content.

  7. Bootstrap does this by creating rows and then dividing them into columns of different sizes. All these classes are inherited from a class called container, which has all body-related classes:

    app/views/layouts/application.html.erb
        <div class="container">
            <div class="row">
                <div class="col-lg-2">
                </div>
                <div class="col-lg-9">
                </div>
            </div>
        </div>
  8. The col-lg-2 class will create a div tag with a width of 16.6667 percent, and col-lg-9 will create a div tag with a width of 75 percent.

  9. Then you can style the navigation. The top-level class for creating a navigation bar is navbar, and the specific class to create a menu that sticks to the top is navbar-static-top. The navbar-brand class is the logo class:

    app/views/layouts/application.html.erb
        <!-- Static navbar -->
        <div class="navbar navbar-static-top">
            <div class="container">
            <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".nav-collapse">
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="#"><%= image_tag "currynation.png"%></a>
            <div class="nav-collapse collapse">
                <ul class="nav navbar-nav">
                <li class="active"><%= link_to "Recipes", recipes_path %></li>
                </ul>
                <ul class="nav navbar-nav pull-right">
                <% if user_signed_in? %>
                <li><%=link_to "#{current_user.email}" %></li>
                <li class="active"><%= link_to "Logout", destroy_user_session_path%></li>
               <%else%>
               <li><%= link_to "Login", new_user_session_path %></li>
               <li class="active"><%= link_to "SignUp", new_user_registration_path%></li>
               <%end%>
               </ul>
            </div><!--/.nav-collapse -->
        </div>
  10. Add styles to individual pages and the customizing buttons.

  11. We will also style our index page by inheriting the table class:

    <table class="table">
  12. We can also customize the links in our app so they look like buttons by adding a class called btn, following the btn-primary class, which defines the color and size of the button. So, for example, we will apply the color blue to the button and assign a small size to it using the btn-small class:

    app/views/recipes/index.html.erb
        <td><%= link_to 'Show', recipe, :class=>"btn btn-primary btn-small"%></td>
        <% if user_signed_in? %>
        <td><%= link_to 'Edit', edit_recipe_path(recipe), :class=>"btn btn-success btn-small"%></td>
        <td><%= link_to 'Delete', recipe, method: :delete, data: { confirm: 'Are you sure?' }, :class=>"btn btn-danger btn-small" %></td>
        <%end%>
  13. Now we'll look at how to style sublinks and wrap them into Rails' loops:

    Our left bar for displaying categories can be displayed as a panel with several sublinks. These are generated in loop using the Rails' each loop. We will first define the values for the sidebar in our application_controller.rb file:

    app/controllers/application.rb
      helper_method :sidebar_values
    
      def sidebar_values
        @food_preferences = FoodPreference.all
        @food_types = FoodType.all
        @cuisines = Cuisine.all
      end

    In app/views/layouts/application.html.erb, we must define the sidebar as rows :

    app/views/layouts/application.html.erb
    <div class="row">
            <div class="col-lg-2">
              <div class="panel panel-primary" id="panels">
                <div class="panel-heading">Food Preferences
                </div>
                 <% @food_preferences.each do |fp| %>
              <p><%= fp.name%></p>
                 <%end%>
              </div>
               <div class="panel panel-primary" id="panels">
                <div class="panel-heading">Food Type
                </div>
                 <% @food_types.each do |ft| %>
              <p><%= ft.name%></p>
                 <%end%>
              </div>
                <div class="panel panel-primary" id="panels">
                <div class="panel-heading">Cuisine
                </div>
                 <% @cuisines.each do |c| %>
              <p><%= c.name%></p>
                 <%end%>
              </div>
            </div> 
        <div class="col-lg-9">
         <%= yield %>
            </div> 
         </div>

Objective complete – mini debriefing

We have our completely styled page at the end of this iteration with the help of Bootstrap 3. We will use Bootstrap throughout our book and see many facets of it in the coming applications. However, this is a good start, as the first step in styling always belongs to HTML elements.

Bootstrap 3 uses a flat UI design, which is the latest trend in web designing. Also, it is not backward compatible with earlier versions.

 

Mission accomplished


We have created a simple recipe-sharing application by using the default Rails' methods and looked at the basics of testing. Practically, these websites can work like multiuser blogs similar to a WordPress installation, meant only for creating recipes.

 

Hotshot challenges


Now that we have seen how to create a simple, social recipe-sharing application, it is time to try out some challenges on our own:

  • Filter recipes by clicking on food preferences, food type, cuisine, and display the results.

  • Write an ActiveRecord query for recipe finders.

  • Create another filter based on the level of difficulty, cuisine, food type, and food preferences. Also, create a radio-button field for each level of difficulty.

  • Use Bootstrap to style the radio-button fields.

About the Author
  • Saurabh Bhatia

    Saurabh Bhatia has been developing professional software since 2005. However, his programming interests date back to his school days. Starting with Java, he quickly moved to Ruby on Rails in 2006, and it has been his primary choice of development framework since then. He built a Ruby on Rails consulting company and ran it for five years. He has worked with several companies in the tech industry, from getting two-person startups off the ground to developing software for large corporates. He is currently the CTO of Ruling Digital Inc., a software company that develops software for universities.

    He has been an open source enthusiast and has helped Ubuntu penetrate the Indian market since 2007. He was a part of the open source promotion society called Twincling Society for Open Source in Hyderabad. He started and moderated Bangalore Ruby Users Group and also moderates the Mumbai Ruby Users Group. He is also a part of the RailsBridge initiative for mentoring new Rails developers.

    Over the years, he has written several articles online and in print for different publications, such as Linux User and Developer, Linux For You, Rails Magazine, Developer.com (http://www.developer.com/), and SitePoint Ruby (http://www.sitepoint.com/ruby/). He currently resides in Taiwan. He wishes to continue writing and share his knowledge as much as possible with budding developers.

    Browse publications by this author
Latest Reviews (3 reviews total)
It is a great read. Only thing about it is it is a version behind, at least when I got it but still great to read and trying to get it to run with the new version of rails and bootstrap is a great exercise.
Great product and easy to buy.
Rails 4 Application Development HOTSHOT
Unlock this book and the full library FREE for 7 days
Start now