JavaBeat

  • Home
  • Java
    • Java 7
    • Java 8
    • Java EE
    • Servlets
  • Spring Framework
    • Spring Tutorials
    • Spring 4 Tutorials
    • Spring Boot
  • JSF Tutorials
  • Most Popular
    • Binary Search Tree Traversal
    • Spring Batch Tutorial
    • AngularJS + Spring MVC
    • Spring Data JPA Tutorial
    • Packaging and Deploying Node.js
  • About Us
    • Join Us (JBC)
  • Privacy

How to write comments controller using Rails?

April 15, 2011 by Krishna Srinivasan Leave a Comment

This article is based on Rails 3 in Action, to be published Fall 2011. It is being reproduced here by permission from Manning Publications. Manning publishes MEAP (Manning Early Access Program,) eBooks and pBooks. MEAPs are sold exclusively through Manning.com. All pBook purchases include free PDF, mobi and epub. When mobile formats become available all customers will be contacted and upgraded. Visit Manning.com for more information. [ Use promotional code ‘java40beat’ and get 40% discount on eBooks and pBooks ]

The Comments Controller

Introduction

In a ticket-tracking application, tickets aren’t just there to provide information of a particular problem or suggestion; rather, they’re there to provide the workflow for it. The general workflow of a ticket is that a user will file it and it’ll be classified as a “new” ticket. When the developers of the project look at this ticket and decide to work on it, they’ll switch the state on the ticket to “open” and once they’re done mark it as “resolved”. If a ticket needs more information on it then another state such as “needs more info”. A ticket could also be a duplicate of another ticket or it could be something that the developers determine isn’t worthwhile putting in. In cases such as this the ticket may be marked as “duplicate” or “invalid,” respectively.

Explanation

The point here is: tickets have a workflow, and that workflow revolves around state changes. We’ll allow the admin users of this application to add states, but not to delete them. The reason for this is if an admin were to delete a state that was used then we’d have no record of that state ever existing. It’s best if once states are created and used on a ticket that they can’t be deleted.

To track the states, we’d let users leave a comment. With a comment, users will be able to leave a text message about the ticket and may also elect to change the state of the ticket to something else by selecting it from a drop down box. However, not all users will be able to leave a comment and change the state. We will protect both creating a comment and changing the state. By the time we’re done with all of this, the users of our application will have the ability to add comments to our tickets.

In order for a comment form to have somewhere to post we need to generate the CommentsController. We can do this by running this command:
[code]rails g controller comments[/code]
A create action in this controller will provide the receiving end for the comment form, so we should add this now. We’ll need to define two before_filters in this controller. The first is to ensure the user is signed in because we don’t want anonymous users creating comments for and another to find the Ticket object. This entire controller is shown in listing 1.

Listing 1 app/controllers/comments_controller.rb
[code]class CommentsController < ApplicationController
before_filter :authenticate_user!
before_filter :find_ticket
def create
@comment = @ticket.comments.build(params[:comment].merge(:user => current_user))
if @comment.save
flash[:notice] = "Comment has been created."
redirect_to [@ticket.project, @ticket]
else
flash[:alert] = "Comment has not been created."
render :template => "tickets/show"
end
end
private
def find_ticket
@ticket = Ticket.find(params[:ticket_id])
end
end

#A redirect_to with array
#B render :template[/code]
In this action we use the template option of render when our @comment.save returns false to render a template of another controller.

You would use the action option to render templates for the current controller. By doing this, the @ticket and @comment objects become available when the app/views/tickets/show.html.erb template is rendered.

If the object saves successfully, we redirect back to the ticket’s page by passing an Array argument to redirect_to, which redirects to a nested route similar to /projects/1/tickets/2.

By creating the controller, we’ve now got all the important parts needed to create comments. Let’s check this feature by running bundle exec cucumber features/creating_comments.feature. We’ll see that it’s able to create the comment but it’s unable to find the text within the #comments element on the page.

Then I should see “Added a comment!” within “#comments” scope ‘//*[@id = ‘comments’]’ not found on page (Capybara::ElementNotFound)

This is because we haven’t added the comments listing to the show template yet. Let’s do this by adding the code from listing 2 above the comment form.

Listing 2 app/views/tickets/show.html.erb
[code]<h3>Comments</h3>
<div id=‘comments’>
<% if @ticket.comments.exists? %> <co id=‘ch09_191_1’ />
<%= render @ticket.comments.select(&:persisted?) %>
<% else %>
There are no comments for this ticket.
<% end %>
</div>[/code]
Here, we create the element our scenario requires: one with an id attribute of comments. In this we check if there are no comments by using the exists?. This will do a very light query similar to this to check if there are any comments:

SELECT “comments”.”id” FROM “comments” WHERE (“comments”.ticket_id = 1) LIMIT 1
It only selects the “id” column from the comments table and limits the result set to 1. We could use empty? here instead, but that would load the comments association in its entirety and then check to see if the array is empty. By using exists?, we stop this potential performance issue from cropping up.

Inside this div, if there are comments, we call render and pass it the argument of @ticket.comments and on the end of that call select on it.

We use select here because we don’t want to render the comment object we’re building for the form at the bottom of the page. If we left off the select, @ticket.comments would include this new object and render a blank comment box. When we call select on an array, we can pass it a block which it will evaluate on all objects inside that array and return any element which makes the block evaluate to anything that is not nil or false.

The argument we pass to select is called a Symbol-to-Proc and is a shorter way of writing this:
{ |x| x.persisted? }

This is a new syntax versions of Ruby = 1.8.7 and used to be in Active Support in Rails 2. It’s a handy way of writing a short block.

The persisted? method checks if an object is persisted in the database by checking if it has its id attribute set and will return true if that’s the case and false if not.

By using render in this form, Rails will render a partial for every single element in this collection and will try to locate the partial using the first object’s class name. Objects in this particular collection are of the Comment, so the partial Rails will try to find will be at app/views/comments/_comment.html.erb, but we don’t have this file right now. Let’s create it and fill it with the content from listing 3.

Listing 3 app/views/comments/_comment.html.erb
[code]<%= div_for(comment) do %>
<h4><%= comment.user %></h4>
<%= simple_format(comment.text) %>
<% end %>[/code]
Here we’ve used a new method, div_for. This method generates a div tag around the content in the block and also sets a class and id attribute based on the object passed in. In this instance, the div tag would be this:
[code]
<div id="comment_1" class="comment">[/code]
The class method from this tag is used to style our comments so that they will look like figure 1 when the styles from the stylesheet are applied.

Figure 1 A commentWith this partial now complete, when we run our feature again by running bundle exec cucumber features/creating_comments.feature, it will all be passing!
[code]2 scenario (2 passed)
23 steps (23 passed)[/code]
Good to see. We have now got the base for users to be able to change the state of a ticket. Before we proceed further, we should make sure that everything is working as it should by running rake cucumber:ok spec and we should also commit our changes. When we run the tests we’ll see this output:
[code]47 scenarios (47 passed)
534 steps (534 passed)
# and
23 examples, 0 failures, 7 pending[/code]
Good stuff! Let’s only commit this now.
[code]git add .
git commit -m "Users can now leave comments on tickets"[/code]
Hey, we’ve got a new file here that begins with capybara in our commit output! This file is the file that was generated by Capybara to show us the page when we used the “Then show me the page” step. We should open our .gitignore file and now ignore this file by putting this line in the file:
[code]capybara*[/code]
This will ignore all capybara files. Let’s remove all the capybara files now and amend this commit and push our changes now.
[code]git rm capybara*
git commit –amend -m "Users can now leave comments on tickets"
git push[/code]
This newly amended commit will override our previous commit, and the push puts our code on GitHub, where it’s safe. With this push, we’ve completed the first half of our state select feature. We now have added the ability for users of our application to add comments.

Summary

We’ve enabled users to leave comments on tickets. This feature is useful because it provides a way for users of a project to have a discussion about a ticket and keep track of it.

Filed Under: Ruby Tagged With: Rails

Creating Tags using Rails

April 15, 2011 by Krishna Srinivasan Leave a Comment

This article is based on Rails 3 in Action, to be published Fall 2011. It is being reproduced here by permission from Manning Publications. Manning publishes MEAP (Manning Early Access Program,) eBooks and pBooks. MEAPs are sold exclusively through Manning.com. All pBook purchases include free PDF, mobi and epub. When mobile formats become available all customers will be contacted and upgraded. Visit Manning.com for more information. [ Use promotional code ‘java40beat’ and get 40% discount on eBooks and pBooks ]

Creating Tags

Introduction

Tags in a ticket-tracking application are extremely useful for making similar tickets easier to find and manage. In this article, we will create the interface for adding tags to a new ticket. This involves adding a new field to the new ticket page and defining a has_and_belongs_to_many association between the Ticket model and the not-yet-existent Tag model.

Creating tags feature

For this feature, we’re going to add a text field beneath the description field on the new ticket page, just like we see in figure 1.

Figure 1 The tag boxThe words we enter into this field will become the tags for this ticket and we should see them on the ticket page. At the bottom of features/creating_tickets.feature, we’ll add a scenario that creates a new ticket with tags, shown in listing 1.

Listing 1 features/creating_tickets.feature

[code]Scenario: Creating a ticket with tags
When I fill in "Title" with "Non-standards compliance"
And I fill in "Description" with "My pages are ugly!"
And I fill in "Tags" with "browser visual"
And I press "Create Ticket"
Then I should see "Ticket has been created."
And I should see "browser" in "#ticket #tags"
And I should see "visual" in "#ticket #tags"[/code]

When we run this scenario using bundle exec cucumber, it will fail features/creating_tickets.feature:48 declaring that it can’t find the “Tags” field. Good! It’s not there yet.

[code]And I fill in "Tags" with "browser visual"
cannot fill in, no text field, text area or password field
with id, name, or label ‘Tags’ found (Capybara::ElementNotFound)[/code]

We’re going to need to take the data from this field and process each word into a new Tag object and, for that reason, we’ll use a text_field_tag to render this field. text_field_tag is similar to a text_field tag, but it doesn’t have to relate to any specific object like text_field does; instead it will just output an input tag with the type attribute set to “text” and the name set to whatever name we give it.

Using text_field_tag

To define this field, we will put the following code underneath the p tag for the description in app/views/tickets/_form.html.erb.

[code]<%= f.label_tag :tags %>
<%= f.text_field_tag :tags, params[:tags] %>[/code]

This field will be sent through to TicketsController as simply params[:tags]. By specifying params[:tags] as the second argument to this method, we can repopulate this field when the ticket cannot be created due to it failing validation.

When we re run this scenario, it no longer complains about the missing “Tags” field, but now that it can’t find the tags area for our ticket:

[code]And I should see "browser" within "#ticket #tags"
scope ‘//*[@id = ‘ticket’]//*[@id = ‘tags’]’ not found …[/code]

We will need to define a #tags element inside the #ticket element so that this part of the scenario will pass. This element will contain the tags for our ticket, which our scenario will assert as actually visible.

Showing tags

We can add this new element to app/views/tickets/show.html.erb by adding this simple line underneath where we render the ticket’s description:

[code]<div id=’tags’><%= render @ticket.tags %></div>[/code]

This creates the #ticket #tags element our feature is looking for and will render the soon-to-be-created app/views/tags/_tag.html.erb partial for every element in the also-soon-to-be-created tags association on the @ticket object.

So what out of these two steps is our next one? We can run our scenario again to see that it cannot find the tags method for a Ticket object:

[code]undefined method ‘tags’ for #<Ticket:0x0..[/code]

This method is the tags method, which we’ll be defining with a has_and_belongs_to_many association between Ticket objects and Tag objects. It will be responsible for returning a collection of all the tags associated with the given ticket, much like a has_many would. It works in the opposite direction also: allowing us to find out what tickets have a specific tag.

Defining the tags association

We can define the association has_and_belongs_to_many on the Ticket model by using this line, placed after a new line after the has_many definitions inside our Ticket model:

[code]has_and_belongs_to_many :tags[/code]

This association will rely on a join table that doesn’t yet exist, called tags_tickets. This table contains only two fields, which are both foreign keys for tags and tickets. By using a join table, many tickets can have many tags, and vice versa.

When we rerun our scenario we’re told that there’s no constant called Tag yet:

[code]
uninitialized constant Ticket::Tag (ActionView::Template::Error)[/code]

In other words, there is no Tag model yet. We should define this now if we want to go any further.

The Tag model

Our Tag model will have a single field called name which should be unique. To generate this model and its related migration we will run the rails command like this:

[code] rails g model tag name:string –timestamps false [/code]

The timestamps option passed here determines whether or not the model’s migration is generated with timestamps. Because we’ve passed the value of false to this, there will be no timestamps added.

Before we run this migration, we will need to add the join table called tags_tickets to our database, which has two fields: one called ticket_id and the other tag_id. The table name is the pluralized names of the two models it is joining, sorted in alphabetical order. This table will have no primary key as we only need it to join the tags and tickets table. We are never going to look for individual records from this table.

To define the table we will put this tags_tickets in the self.up section of our db/migrate/[timestamp]_create_tags.rb migration:

[code]create_table :tags_tickets, :id => false do | t |
t.integer :tag_id, :ticket_id
end[/code]

The :id => false option passed to create_table here tells Active Record to create the table without the id field.

We should also add drop_table :tag_tickets to the self.down method in this migration too so that, if we need to, we can undo this migration. Next, we will run the migration on our development database by running rake db:migrate and our test database by running rake db:test:prepare. This will create the tags and tags_tickets tables. When we run this scenario again with bundle exec cucumber features/creating_tickets:48, it is now satisfied that the tags method is defined and has now moved on to whining that it can’t find the tag we specified:

[code]And I should see "browser" within "#ticket #tags"
Failed assertion, no message given. (MiniTest::Assertion)[/code]

This is because we’re not doing anything to associate the text from the “Tags” field to the ticket we’ve just created. We need to parse the content from this field into new Tag objects and then associate them with the ticket we are creating, which we’ll look at how to do right now.

Displaying a ticket’s tags

The params[:tags] in TicketsController’s create is the value from our “Tags” field on app/views/tickets/_form.html.erb. This is also the field we need to parse into Tag objects and associate them with the Ticket object we are creating.

To do this, we will alter the create action by adding this line directly after @ticket.save:

[code]@ticket.tag!(params[:tags])[/code]

This method will parse the tags from params[:tags], convert them into new Tag objects, and associate them with the ticket. We can define this new method at the bottom of our Ticket model like this:

[code]def tag!(tags)
tags = tags.split(" ").map do |tag|
Tag.find_or_create_by_name(tag)
end
self.tags << tags
end[/code]

On the first line here, we use the split method to split our string into an array, and then the map method to iterate through every value in the array. Inside the block for map, we use a dynamic finder to find or create a tag with a specified name. find_or_create_by methods will always return a record, whether it be a preexisting or a recently created one.

After all the tags have been iterated through, we assign them to a ticket by using the << method on the tags association. The tag! method we have just written will create the tags that we will display on the app/views/tickets/show.html.erb view by using this line:

[code]<%= render @ticket.tags %>[/code]

When we run this scenario again by running bundle exec cucumber features/creating_tickets.feature:48, we will see it’s this line that is failing with an error:
Missing partial tags/tag …

Therefore, the next step is to write the tag partial, which our feature has just complained about. To do this, we will put the following code in app/views/tags/_tag.html.erb:

[code]<span class=’tag’><%= tag.name %></span>[/code]

By wrapping the tag name in a span with the of tag, class, it will be styled as defined in our stylesheet. With this partial defined, this puts the final piece of the puzzle for this feature into place. When we run our scenario again by running bundleexeccucumber features/creating_tickets.feature:48 it passes:

[code]1 scenario (1 passed)
15 steps (15 passed)[/code]

Great! This scenario is now complete. When a user creates a ticket, they are able to assign tags to that ticket and they display along with the ticket’s information on the show action for TicketsController.

Figure 1 Look ma, a tag!We will now commit this change, but, of course, before we do, we’ll ensure that we haven’t broken anything by running rake cucumber:ok spec.

[code]53 scenarios (53 passed)
620 steps (620 passed)
# and
27 examples, 0 failures, 10 pending[/code]

Good to see that nothing’s blown up this time. Let’s commit this change.

[code]git add .
git commit -m "Users can tag tickets upon creation"
git push[/code]

Now that users are able to add a tag to a ticket when that ticket’s being created, we should also let them add tags to a ticket when they create a comment as well.

When a ticket’s discussion happens, new information may come about that would require that another tag be added to the ticket to group it into a different set. A perfect way to let our users do this would be to let them add it when they comment.

Summary

We’ve covered how to use a has_and_belongs_to_many association to define a link between tickets and tags. Tickets are capable of having more than one tag, but a tag is also capable of having more than one ticket assigned to it and, therefore, we use this type of association. A has_and_belongs_to_many could be used to associate people and the locations they’ve been.

Filed Under: Ruby Tagged With: Rails, Ruby

Introduction to Ruby On Rails

October 13, 2010 by Krishna Srinivasan Leave a Comment

Introduction

This article provides an introduction to Ruby on Rails. Ruby is a programming language that is interpreted and Rails is a framework written on top of Ruby for writing Web Applications. The article starts with an introduction to Ruby with respect to the basic syntax usage and provides plenty of samples for illustrating conditional constructs and looping constructs. Also the advanced concepts like Classes /Objects and modules will also explained with examples. The later section of the article explains Rails and it starts with the basics of the Model View Controller Architecture for writing Rails application.

Ruby Introduction

Ruby is an interpreted language that is easy to use and contains various object oriented features. In this section, we will see the basics of Ruby like variable declaration, conditional constructs, looping, classes/object etc. Readers who are new to ruby can be benefitted with the usage of Ruby in this section.

Hello Word

The following code snippet shows how to declare a variable and store some value in it. It later displays the variable’s value in the console.

[code]hello = "Hello World"
puts hello[/code]

Getting input from user

As seen in the last example, the function puts() can be used to output the content to the console and the method gets() is used to obtain the input from the user.

[code]puts("Enter a number");
number = gets();
puts("The number entered is #{number}");[/code]
Also have a look at how the variable’s value is used within strings. The pattern used will be ‘#{variableName}’ when this variable is used within double-quotes.

Declaring Arrays

Ruby supports the declaration of an array through the following syntax. The index of array location starts from zero.

[code]my_array = ["one", "two"];
puts my_array[1];
puts "Element at location 0 is #{my_array[0]}";

puts "Length of the array is is #{my_array.length}"

fruits = Array.new;
fruits[0] = "Apple";
fruits[1] = "Orange";
fruits[2] = "Grapes";

puts "Length of array is #{fruits.length}"[/code]
The length of the array – which tells the number of elements that an array is holding currently can be determined by calling ‘arrayObject.length’. Another way for declaring a dynamic array is to call ‘Array.new’ and the populate the elements with the normal syntax.

Conditional constructs

The following code snippet illustrates the usage of conditional constructs in Ruby. Note the usage of ‘if’, ‘elsif’ and ‘end’ and the usage of AND operations.

[code]pizza_price = 80;
if (pizza_price < 100)
puts "Price is less";
elsif (pizza_price > 100 &amp;&amp; pizza_price <200)
puts "Price is medium";
else
puts "Price is large"
end[/code]
The above code displays the price based on various ranges just to illustrate the usage of conditional constructs.

Looping

The usage of ‘for’ loop is given below. The code declares a array of programming languages and then iterates over the loop using ‘for’ loop.

[code]languages = ["C", "C++", "Java", ".NET"];

for language in languages
puts "Language is #{language}";
end[/code]

Function

Functions are used to define re-usable logic and it can be invoked multiple times. The usage of functions in Ruby is supported through ‘def’ keyword followed by the function name.

[code]def multiply(a, b)
result = multiply_with_return(a, b);
puts("Inside Multiply without return #{result}");
end

def multiply_with_return(a, b)

puts("Multiplying #{a} with #{b}");
result = a * b;
return result;
end

value = multiply_with_return(10, 10);
puts("Result from multiplying with return is #{value}");
multiply(5, 3);[/code]
Functions can support parameters also. In the above code, we have defined functions ‘multiply’ and ‘multiply_with_return’. The first function uses the parameters passed by the caller and displays the result within the function definition. The second function, after calculating the result, returns the result to the caller.

Classes and Objects

In this section, we will see the usage of classes and objects in Ruby. As known, class represents the template for creating objects which contains the combination of related operations and data.

[code]class Arithmetic
def initialize(a, b)
@a = a;
@b = b;
end

def add()
puts("Addition of #{@a} and #{@b} is #{@a + @b}");
end

def sub()
puts("Subtraction of #{@a} and #{@b} is #{@a – @b}");
end

def multiply()
puts("Multiplication of #{@a} and #{@b} is #{@a * @b}");
end

def divide()
puts("Dividing #{@a} with #{@b} results is #{@a / @b}");
end

end

one_two_arithmetic_object = Arithmetic.new(1, 2);
one_two_arithmetic_object.add();
one_two_arithmetic_object.sub();
one_two_arithmetic_object.multiply();
one_two_arithmetic_object.divide();[/code]
In the above code, we declare a class called ‘Arithmetic’ and supports related functions like ‘add’, ‘sub’, ‘multiply’ and ‘divide’ which does the basic mathematical operations. Callers can create objects for this class and can invoke the various available operations supported by this class. There is a special method available in the class declaration called ‘initialize’ which will be invoked automatically when an object is created for this class.
Also note the syntax for creating an object for the class. It is the class name followed by ‘new’. The parameters following the ‘new’ keyword are the initialization parameters. Here we have passed the initialization arguments ‘1’ and ‘2’ and hence the function ‘initialize’ will be called with these arguments.

Modules

Modules in ruby are used to group larger number of operations together. In the below example code, we have created two modules called ‘Number_Operations’ and ‘String_Operations’. Note that these modules are defined in files ‘module_number_operations.rb’ and ‘module_string_operations.rb’.

[code]module Number_Operations

def Number_Operations.display()
puts "Contains the utility operations related to numbers";
end

end

module String_Operations

def String_Operations.display()
puts "Contains the utility operations related to string";
end

end[/code]
The below code represents the usage of the modules, and for importing the modules within the client applications, the syntax ‘require ‘, should be used. Then the operations are used through the syntax ‘ModuleName.operationName’.

[code]require ‘module_string_operations’
require ‘module_number_operations’

puts String_Operations.display();
puts Number_Operations.display();[/code]

Rails Framework

Ruby is an interpreted programming language and Rails is a framework written on Ruby for building web applications. Rails prefer the approach on convention over configuration which means that when you create a web application using Rails framework, the supporting tools will provide a lot of sensible defaults so that developers can go and hook in only their customizations instead of writing everything from scratch. Also one can see the how faster it will be in developing web applications through Rails when compared with other popular Web application frameworks.
Rails framework follows the pattern of MVC which stands for Model-View –Controller architecture which is a popular standard/pattern for building web applications. When a client, typically a browser is making a request, it is the Controller which will be handling the request initially. Based on various request parameters, it will identity the type of request and forwards the request to the appropriate Model objects. There can be multiple Model objects that can be maintained in a Web application and it is the Controller’s responsibility to choose a particular Model object based on various parameters. The Model object represents the action part as well as the data handling part. It may hit the database or can perform any business actions with respect to the context and finally build the data suitable for getting it displayed.
In this section we will create a Rails starter application that will display some static text in the browser upon client’s request. Rails come with plentiful of utilities that make the developmental tasks easier. Have a look at the following command which will create a web application. The name of the application is ‘hello’ and the ‘new’ subcommand indicates that we want to create a new application.

[code]rails new hello –O[/code]
Executing the above command will produce an output similar to the following.

[code]create
create README
create Rakefile
create config.ru
create .gitignore
create Gemfile
create app
create app/controllers/application_controller.
create app/helpers/application_helper.rb
create app/views/layouts/application.html.erb
create app/mailers
create app/models
create config
create config/routes.rb
create config/application.rb
create config/environment.rb
create config/environments
create config/environments/development.rb
create config/environments/production.rb
create config/environments/test.rb
create config/initializers
create config/initializers/backtrace_silencers
create config/initializers/inflections.rb
create config/initializers/mime_types.rb
create config/initializers/secret_token.rb
create config/initializers/session_store.rb
create config/locales
create config/locales/en.yml
create config/boot.rb
create db
create db/seeds.rb
create doc
create doc/README_FOR_APP
create lib
create lib/tasks
create lib/tasks/.gitkeep
create log
create log/server.log
create log/production.log
create log/development.log
create log/test.log
create public
create public/404.html
create public/422.html
create public/500.html
create public/favicon.ico
create public/index.html
create public/robots.txt
create public/images
create public/images/rails.png
create public/stylesheets
create public/stylesheets/.gitkeep
create public/javascripts
create public/javascripts/application.js
create public/javascripts/controls.js
create public/javascripts/dragdrop.js
create public/javascripts/effects.js
create public/javascripts/prototype.js
create public/javascripts/rails.js
create script
create script/rails
create test
create test/performance/browsing_test.rb
create test/test_helper.rb
create test/fixtures
create test/functional
create test/integration
create test/unit
create tmp
create tmp/sessions
create tmp/sockets
create tmp/cache
create tmp/pids
create vendor/plugins
create vendor/plugins/.gitkeep[/code]
As you can see, the execution of the command creates a standard directory layout containing lots of files and folders. Since Ruby applications are MVC based, the folders ‘app/controllers’, ‘app/models’ and ‘app/views’ contain the files related to Controllers, Models and Views respectively. The ‘db’ folder contains information related to database for various environments like development, testing and production. The ‘configuration’ folder represents the application’s configuration information such as request-action mapping and other dependencies. Test cases related files come under the ‘test’ directory. The ‘public’ directory contains the viewable files that can be accessed directly by the client like html, images etc.
To launch the ‘hello’ application, go to the directory ‘hello’ (this new directory was created in the previous step) and type the following. Ruby installation comes with a pre-configured web server called ‘WEBrick’ and the following command starts the web server in the default port number ‘3000’.

[code]rails server
=> Booting WEBrick
=> Rails 3.0.0 application starting in development on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
[2010-10-09 00:36:29] INFO WEBrick 1.3.1
[2010-10-09 00:36:29] INFO ruby 1.9.2 (2010-08-18) [i386-mingw32]
[2010-10-09 00:36:29] INFO WEBrick::HTTPServer#start: pid=7816 port=3000[/code]
As soon as the server is started, the application can be accessed through the url ‘http://localhost:3000’. This shows the default page which is located in the folder ‘/public/index.html’. This application really doesn’t do anything other than creating the basic template for a web application with a default page. Let us add the basic functionality of creating a controller and a view. The controller takes the responsibility of intercepting the client’s request and it will redirect the control to the view. The view takes the control and the content of the view will be displayed to the browser. The following screen-shot will be displayed when the browser is accessed with the url ‘http://localhost:3000’.

Execute the following command for creating controller and an action. The ‘controller’ subcommand takes the controller name and the action name as arguments. Here the name of the controller is ‘hello’ and the name of the action is ‘hello_action’

[code]rails generate controller hello hello_action
create app/controllers/hello_controller.rb
route get "hello/hello_action"
invoke erb
exist app/views/hello
create app/views/hello/hello_action.html.erb
invoke test_unit
identical test/functional/hello_controller_test.rb
invoke helper
identical app/helpers/hello_helper.rb
invoke test_unit
identical test/unit/helpers/hello_helper_test.rb[/code]
The execution of the command creates the controller file ‘hello_controller.rb’ in the folder ‘/app/controllers’. Here is the listing for generated ‘hello_controller.rb’ file.

[code]class HelloController < ApplicationController
def hello_action
end
end[/code]

In the above code, the ‘HelloController’ class inherits from the base controller Application. A controller can have multiple actions and one such action provided is ‘hello_action’. This makes the application to be accessed through this way ‘/hello/hello_action’. We haven’t provided an implementation for the action and the default implementation is to look for a view file in the directory ‘/app/views//.html.erb’. The generator is sensible enough to create the file ‘hello_action.html.erb’ in the folder ‘/app/views/hello’. The content of this view file is given below,

[code lang=”html”]
<h1>Hello</h1>
<p>This is a starter application to setup things using Rails framework
[/code]

This is a starter application to setup things using Rails framework
Before accessing the application, delete the file ‘index.html’ present in the ‘/public’ directory, the presence of this file overrides the settings. Now accessing the application through the url ‘http://localhost:3000/hello/hello_action’ will display the following view.

 

Creating Multiple Controllers/Actions and Views

Now that we have a basic understanding on rails, we will extend the above concepts in creating an application that has multiple controllers, actions and views. This sample will illustrate this example by providing the option on displaying the home page of contacts and messages. We will start creating the application by executing the following command,

[code]rails new lister –O[/code]
This creates an application called ‘lister’. Change to the directory of ‘lister’. Now we want to create a controller for ‘contacts’ which will control the home page display and the listing for all contacts. Execute the following command for creating the controller called ‘contacts’.

[code]rails generate controller contacts contacts_home contacts_view[/code]
Other than creating a controller called ‘contacts’, the above command will create two more actions called ‘contacts_home’ and ‘contacts_view’. The execution of the above command would result in an output similar to the following.

[code]create app/controllers/contacts_controller.rb
route get "contacts/contacts_view"
route get "contacts/contacts_home"
invoke erb
create app/views/contacts
create app/views/contacts/contacts_home.html.erb
create app/views/contacts/contacts_view.html.erb
invoke test_unit
create test/functional/contacts_controller_test.rb
invoke helper
create app/helpers/contacts_helper.rb
invoke test_unit
create test/unit/helpers/contacts_helper_test.rb[/code]
The definition of the ‘contacts_controller’ is shown below. Note that other than the regular controller definitions, two action definitions are added to the file.

[code]class ContactsController < ApplicationController
def contacts_home
end
def contacts_view
end
end[/code]
The action ‘contacts_home’ is used to display the home page view for contacts. Here is view definition for ‘contacts_home’ found in ‘/app/views/contacts/contacts_view.html/erb’.

[code lang=”html”]
<html>
<head>
<title>Contacts Home</title>
</head>
<body>
<h1>Contacts Home</h1>
<br>
<br>
<h1>This is the home page for Contacts</h1>
<br>
<%= link_to "Go to Contacts View.", :action => "contacts_view" %>
<br>

</body>
</html>
[/code]
Note that the above view page defines the link to the contacts view and the usage of ruby scriptlets in the above between the symbols ‘<#=’ and ‘%>’. A predefined element ‘link_to’ is defined containing the action attribute expressed as ‘action’. The display name given to the action is ‘Go to Contacts View’. The action name is given as ‘contacts_view’, note that this action name should match the action name which is defined in the controller. The inclusion of this scriptlet will introduce a link in the html page and by clicking on the link, the user will be redirected to the ‘contacts_view’ page.
Access to the link ‘http://localhost:3000/contacts/contacts_home’ will display the following page in the browser.

The code listing for ‘contacts_view’ is given below. Note that this view page has some hard-coded contact entries to get them displayed in the browser. Also the view page includes a reference to the contacts home page.

All Contacts

Name Number
12345 David
67890 Jones
13469 Lisa

[code lang=”html”]
<html>
<head>
<title>Contacts View</title>
</head>
<body>
<h2>All Contacts</h2>

<table border = "1">
<tr>
<th>Name</th>
<th>Number</th>
</tr>

<tr>
<td>12345</td>
<td>David</td>
</tr>

<tr>
<td>67890</td>
<td>Jones</td>
</tr>
<tr>
<td>13469</td>
<td>Lisa</td>
</tr>
</table>
<br>
<%= link_to "Go to Contacts home.", :action => "contacts_home" %>
</body>
</html>

[/code]
Access to the link ‘http://localhost:3000/contacts/contacts_view’ will display the following page in the browser.

Execute the following command for creating the controller ‘messages’ with two actions ‘messages_home’ and ‘messages_view’.

[code]rails generate controller messages messages_home messages_view[/code]

The source code listing for messages_controller ,’messages_home’ and ‘messages_view’ is not included here and it will look similar to the one that we had already seen before. The URLs for accessing the home page and the listing for messages are ‘http://localhost:3000/messages /messages_home’ and ‘http://localhost:3000/messages /messages_view’ respectively.

Conclusion

This article provided an introduction to Ruby on Rails explaining the basics in writing Web applications using the Rails framework. Various code samples were discussed to illustrate the basics of Rails framework in writing controllers, actions and views.

Filed Under: Ruby Tagged With: Rails, Ruby

Develop Ruby on Rails applications fast using RadRails 1.0 Community Edition

November 11, 2009 by Krishna Srinivasan Leave a Comment

Aptana RadRails: An IDE for Rails Development


Develop Ruby on Rails applications fast using RadRails 1.0 Community Edition


Coming from a background of developing in languages such as Java, one of the
things that surprised me the most about the Ruby and Rails community, was the
common practice of not using an Integrated Development Environment. Most of the
members of the community, including the most relevant, were comfortable with just a
programmer’s editor.


At first I thought it was because, Ruby being a dynamic language, using a full IDE might
be an overkill. But then I thought of the PHP community, in which several IDEs are
popular, with PHP also being a dynamic language. So I still had to guess why using an
IDE was not a common practice within the Ruby on Rails world.


Nowadays, there is a growing list of IDEs with support for Ruby on Rails, but two
years ago the options were really scarce. Back then, I chose to use RadRails because it
worked on top of the Eclipse IDE—which was the tool I was already using for
other programming languages—and because it was the only free, open source, and
portable option.


Truth is, the first version of RadRails I used was very promising, but still a bit too basic.
It featured just a few specialized tools, Ruby syntax colorization, and a slow and faulty
code-assistance. As a result, the difference between RadRails and a good programmer’s
editor was not really significant. However, as Ruby on Rails gained popularity, RadRails
was vastly improved, and a lot of new features were added.


At the same time, several other IDEs started to provide support for Ruby too. Today,
even if many Ruby on Rails developers still don’t use an IDE, a growing number of
them already.


During these two years, I’ve been developing projects almost exclusively with Ruby on
Rails; and I developed all of them using RadRails. Of course I have been keeping an eye
on every new IDE with Ruby support, just to see if there were any reasons for changing,
but I still didn’t find any.


To me, writing this book is a way of contributing back to the RadRails project. I hope this
book will help the existing community of users of Aptana RadRails, and will also help
new users to start working with this tool. Besides, thanks to the Packt Open Source
Project Royalty Scheme, a part of the benefits will be directly paid as a royalty to the
RadRails project, so by purchasing this book you are funding a bit of the Community
Edition of Aptana RadRails.


What This Book Covers


This book will show you how to get the most of the Community Edition of Aptana
RadRails for developing Ruby on Rails projects. Apart from the features provided by
RadRails, the book will give you an overview of working with the Eclipse IDE,
and will show you how to use the Eclipse functionalities that are relevant for Ruby and
Rails development.


This book is not about the Ruby programming language or the Ruby on Rails framework.
Even if you don’t need to be an expert, you should already be familiar with the language
and the framework to get the most from this book.


Chapters 1 and 2 will show you how to install and configure Aptana RadRails, and will
help you find your way around the Eclipse IDE. If you have previous experience with
Eclipse , and you have already installed Aptana RadRails, then you can proceed directly
to Chapter 3.


Chapters 3 to 8 are a complete reference to each of the components of RadRails,
including all the configuration options.


Finally, in Chapter 9 you will find documentation about some complementary plugins
you can use for connecting to a database and for managing your source repositories.


You can find below a brief introduction to each of the chapters.


Chapter 1: This chapter will introduce you the concept of IDE and will give you a
general overview of what you can expect from Aptana RadRails. You will also find
instructions about how to install Aptana RadRails and the Eclipse IDE in your system.
Even if you should already be familiar with the installation of Ruby and Rails, the chapter
also provides a quick reference for installing Ruby and Ruby on Rails on Windows,
Linux, and OSX.


Chapter 2: In most cases, Aptana RadRails will work directly out of the box. However, in
some cases you will need to make a minimal configuration of the IDE. The first part of
this chapter will show you the basic configuration of RadRails.


Chapter 3: Two of the basic tools RadRails provides are the Ruby Explorer and the
Console View. With the Ruby explorer you will be able to browse the structure of your
projects and perform any kind of file-related operations, including working with the local
history of your files. The console view will display the output of most of the processes
we will launch from RadRails. Apart from learning how to use these views, we will show
how to use Generators and Rake Tasks from Aptana RadRails to create a simple demo
application. You will also learn how to start and stop your servers and how to use the
built-in browser to watch your application in action.


Chapter 4 explains in detail all the built-in capabilities of RadRails for developing Ruby
code. You will learn to use the Ruby Editor to write your source code, to navigate
between the different classes and files, and to get the most out of code completion and the
code templates.


Chapter 5: One of the strong points of Aptana RadRails is the great support for the clientside
of your application: JavaScript, HTML, and CSS. In this chapter you will learn how
to write Rails views mixing together Ruby code with HTML or JavaScript and getting
assistance for all of the languages.


Chapter 6: When an application grows large, it’s always a good idea to have a way of
debugging the potential errors. This chapter will show you how to use RadRails’ built-in
debugger for interacting with your code at run time. You will learn to start a server or a
stand-alone script in debug mode, how to set breakpoints , and how to intercept any Ruby
exceptions. The debugger will also allow you to walk through your code, to examine the
values of any variables and expressions, and even to execute arbitrary code at run time by
using the Display view.


Chapter 7: Apart from the coding and debugging, Aptana RadRails provides a number of
specialized tools to make the development and management of your application easier. In
the context of Eclipse, each of these tools is called a View. In this chapter, you will learn
how to use the different views to browse the Ruby and Rails documentation, manage and
monitor your servers, install gems and plugins, launch generators and rake tasks, use code
annotations, keep track of warnings and to-do lists, evaluate regular expressions, and run
your tests. If you prefer to use the command line, then you will learn how to take
advantage of the built-in Rails Shell, in which you can get auto-completion for the most
used Ruby and Rails commands directly at the command line. This chapter will also
show you how to use your IDE to control external servers such as Apache or MySQL.


Chapter 8: Out of the box, Aptana RadRails provides a fully working environment.
However, many of its components allow for some configuration. This chapter is a
complete reference to all the preferences you can set to change the user experience when
using RadRails.


Chapter 9: Aptana RadRails bundles together plenty of interesting features for the
developer. However, since the focus is on Ruby on Rails, there are some general aspects
of the development of a project that are not covered by RadRails. Fortunately, since the
underlying platform is the Eclipse IDE, we have a virtually unlimited number of
complementary plugins to choose from. This chapter will give you a general overview of
the Eclipse plugins ecosystem, and will also explain in detail how to use two of the
plugins you might want to use when developing. DBViewer is a plugin you can use to
connect to your database from the IDE. This chapter will show you how to set up the
plugin, and how to use it for examining and modifying your database structure and
contents. Subclipse is a plugin to connect to Subversion repositories. By using Subclipse
you will have repository access directly from your IDE. Besides, the built-in features of
Subclipse will help you examine and merge changes in a much more comfortable way
than using the Subversion command line.


RadRails Views


By now you should be comfortable with the general interface of Eclipse and
RadRails. You know already how to create Rails projects, write and debug Ruby code
and views, and work with HTML, JavaScript, and CSS files. We could say most of
our programming needs are fulfilled with that.


When developing a Rails project, there are more things to do than the source code
itself. We have to start, stop, and monitor our servers, generate code templates, run
our test suites, install plugins and gems, generate documentation, keep control
of to-do items, or run Rake tasks for different purposes—database migrations,
for example.


RadRails provides different views for supporting these tasks that are a part of
the development but not of the coding itself. And, of course, it does it so we can
control everything from within the IDE without having to go back to the
command-line interface.


We already had a glimpse of some of these features when using the Generators,
Rake, or Servers views briefl y when we needed them in previous chapters. Now you
will learn how to take full advantage of all the RadRails views, to help you take care
of routine processes and just focus on getting things done.


Opening the RadRails Views


Some of the views that we will go through in this chapter are available as part of
the Rails default perspective, which means you don’t need to do anything special
to open them; they will appear as tabbed views in a pane at the bottom of your
workbench. Just look for the tab name of the view you want to see and click on it to
make it visible.


However, there are some views that are not opened by default, or maybe you closed
them at some point accidentally, or maybe you changed to the Debug perspective
and you want to display some of the RadRails views there. When you need to open
a view whose tab is not displaying, you can go to the Window menu, and select the
Show View option.

If you are in the Rails perspective, all the available views will be displayed in that
menu, as you can see in the screenshot above. When opening this menu from a
different perspective, you will not see the RadRails views here, but you can select
Other… as we did in previous chapters. If this is the case, in the Show View dialog,
most of the views will appear under the Ruby category, except for the Generators,
Rails API, and Rake Tasks views, which are located under Rails.


Documentation Views


As happens with any modern programming language, Ruby has an extensive
API. There are lots of libraries and classes and even with Ruby being an intuitive
language with a neat consistent API, often we need to read the documentation.


As you probably know, Ruby provides a standard documentation format called
RDoc, which uses the comments in the source code to generate documentation. We
can access this RDoc documentation in different ways, mainly in HTML format
through a browser or by using the command-line tool RI. This produces a plain-text
output directly at the command shell, in a similar way to the man command in a
UNIX system.


RadRails doesn’t add any new functionalities to the built-in documentation, but
provides some convenient views so we can explore it without losing the context of
our project’s source.


Ruby Interactive (RI) View


This view provides a fast and comfortable way of browsing the local documentation
in the same way as you would use RI from the command line.

You can look either for a class or a method name. Just start typing at the input box
at the top left corner of the view and the list below will display the matching entries.
That’s a nice improvement over the command line interface, since you can see the
results as you type instead of having to run a complete search every time.


If you know the name of both the class and the method you are looking for, then
you can write them using the hash (pound) sign as a separator. For example, to get
the documentation for the sum method of the class Enumerable you would write
Enumerable#sum.


The documentation will display in the right pane, with a convenient highlighting of
the referenced methods and classes. Even if the search results of RI don’t look very
attractive compared to the output of the HTML-based documentation views, RI has
the advantage of searching locally on your computer, so you can use it even when
working off-line.

Pages: 1 2 3 4

Filed Under: Ruby Tagged With: Rails

Ruby on Rails Web Mashup Projects

October 6, 2009 by itadmin Leave a Comment

Ruby on Rails Web Mashup Projects

A step-by-step tutorial to building web mashups

A web mashup is a new type of web application that uses data and services from one or more external sources to build entirely new and different web applications. Web mashups usually mash up data and services that are available on the Internet—freely, commercially, or through other partnership agreements. The external sources that a mashup uses are known as mashup APIs.

This book shows you how to write web mashups using Ruby on Rails—the new web application development framework. The book has seven real-world projects—the format of each project is similar, with a statement of the project, discussion of the main protocols involved, an overview of the API, and then complete code for building the project. You will be led methodically through concrete steps to build the mashup, with asides to explain the theory behind the code.

What This Book Covers

The first chapter introduces the concepts of web mashups to the reader and provides a general introduction to the benefits and pitfalls of using web mashups as standalone applications or as part of existing web applications.

The first project is a mashup plugin into an existing web application that allows users to find the location of the closest facility from a particular geographic location based on a specified search radius. The location is mapped and displayed on Google Maps.

The second project is another mashup plugin. This plugin allows users to send messages to their own list of recipients, people who are previously unknown to the website, on behalf of the website. The project uses Google Spreadsheets and EditGrid to aggregate the information, and Clickatell and Interfax to send SMS messages and faxes respectively.

The third project describes a mashup plugin that allows you to track the sales ranking and customer reviews of a particular product from Amazon.com. The main API used is the Amazon E-Commerce Service (ECS).

The fourth project shows you how to create a full-fl edged Facebook application that allows a user to perform some of the functions and features of a job board. This mashup uses Facebook, Google Maps, Daylife, Technorati and Indeed.com APIs.

The fifth project shows you how to create a full web mashup application that allows users to view information on a location. This is the chapter that uses the most mashup APIs, including Google Maps, FUTEF, WebserviceX, Yahoo! Geocoding services, WeatherBug, Kayak, GeoNames, Flickr, and Hostip.info.

The sixth project describes a mashup plugin that allows an online event ticketing application to receive payment through Paypal, send SMS receipts, and add event records in the customer’s Google Calendar account. The APIs used are Google Calendar, PayPal, and Clickatell.

The final project shows a complex mashup plugin used for making corporate expense claims. It allows an employee to submit expense claims in Google Docs and Spreadsheets, attaching the claims form and the supporting receipts. His or her manager, also using Google Docs and Spreadsheets, then approves the expense claims and the approved claims are retrieved by the mashup and used to reimburse the employee through
PayPal. It uses the PayPal APIs and various Google APIs.

‘Find closest’ mashup plugin

What does it do?

This mashup plugin allows your Rails website or application to have an additional feature that allows your users to find the location of the closest facility from a particular geographic location based on a specified search radius. This mashup plugin integrates with your existing website that has a database of locations of the facilities.

Building a kiosk locator feature for your site

Your company has just deployed 500 multi-purpose payment kiosks around the country, cash cows for the milking. Another 500 more are on the way, promising to bring in the big bucks for all the hardworking employees in the company. Naturally your boss wants as many people as possible to know about them and use them. The problem is that while the marketing machine churns away on the marvels and benefits of the kiosks, the customers need to know where they are located to use them. He commands you:


“Find a way to show our users where the nearest kiosks to him are, and directions to reach them!”

What you have is a database of all the 500 locations where the kiosks are located, by their full address. What can you do?

Requirements overview

Quickly gathering your wits, you penned down the following quick requirements:

  1. Each customer who comes to your site needs to be able to find the closest kiosk to his or her current location.
  2. He or she might also want to know the closest kiosk to any location.
  3. You want to let the users determine the radius of the search.
  4. Finding the locations of the closest kiosks, you need to show him how to reach them.
  5. You have 500 kiosks now, (and you need to show where they are) but another 500 will be coming, in 10s and 20s, so the location of the kiosks need to be specified during the entry of the kiosks. You want to put all of these on some kind of map.

Sounds difficult? Only if you didn’t know about web mashups!

Design

The design for this first project is rather simple. We will build a simple database application using Rails and create a main Kiosk class in which to store the kiosk information including its address, longitude, and latitude information. After populating the database with the kiosk information and address, we will use a geolocation service to discover its longitude and latitude. We store the information in the same table. Next, we will take the kiosk information and mash it up with Google Maps and display the kiosks as pushpins on the online map and place its information inside an info box attached to each pushpin.

Mashup APIs on the menu

In this chapter we will be using the following services to create a ‘find closest’ mashup plugin:

  • Google Maps APIs including geocoding services
  • Yahoo geocoding services (part of Yahoo Maps APIs)
  • Geocoder.us geocoding services
  • Geocoder.ca geocoding services
  • Hostip.info

Google Maps

Google Maps is a free web-based mapping service provided by Google. It provides a map that can be navigated by dragging the mouse across it and zoomed in and out using the mouse wheel or a zoom bar. It has three forms of views—map, satellite and a hybrid of map and satellite. Google Maps is coded almost entirely in JavaScript and XML and Google provides a free JavaScript API library that allows developers to integrate Google Maps into their own applications. Google Maps APIs also provide geocoding capabilities, that is, they able to convert addresses to longitude and latitude coordinates.

We will be using two parts of Google Maps:

  • Firstly to geocode addresses as part of GeoKit’s APIs
  • Secondly to display the found kiosk on a customized Google Maps map

Yahoo Maps

Yahoo Maps is a free mapping service provided by Yahoo. Much like Google Maps it also provides a map that is navigable in a similar way and also provides an extensive set of APIs. Yahoo’s mapping APIs range from simply including the map directly from the Yahoo Maps website, to Flash APIs and JavaScript APIs. Yahoo Maps also provides geocoding services. We will be using Yahoo Maps geocoding services as part of GeoKit’s API to geocode addresses.

Geocoder.us

Geocoder.us is a website that provides free geocoding of addresses and intersections in the United States. It relies on Geo::Coder::US, a Perl module available for download from the CPAN and derives its data from the TIGER/Line data set, public-domain data from the US Census Bureau. Its reliability is higher in urban areas but lower in the other parts of the country. We will be using Geocoder.us as part of GeoKit’s API to geocode addresses.

Geocoder.ca

Geocoder.ca is a website that provides free geocoding of addresses in the United States and Canada. Like Geocoder.us. it uses data from TIGER/Line but in addition, draws data from GeoBase, the Canadian government-related initiative that provides geospatial information on Canadian territories. We will be using Geocoder.ca as part of GeoKit’s API to geocode addresses.

Hostip.info

Hostip.info is a website that provides free geocoding of IP addresses. Hostip.info offers an HTTP-based API as well as its entire database for integration at no cost. We will be using Hostip.info as part of GeoKit’s API to geocode IP addresses.

GeoKit

GeoKit is a Rails plugin that enables you to build location-based applications. For this chapter we will be using GeoKit for its geocoding capabilities in two ways:

  • To determine the longitude and latitude coordinates of the kiosk from its given address
  • To determine the longitude and latitude coordinates of the user from his or her IP address

GeoKit is a plugin to your Rails application so installing it means more or less copying the source files from the GeoKit Subversion repository and running through an installation script that adds certain default parameters in your environment.rb file.

To install the GeoKit, go to your Rails application folder and execute this at the command line:

[code]
$./script/plugin install svn://rubyforge.org/var/svn/geokit/trunk
[/code]

This will copy the necessary files to your RAILS_ROOT/vendor/plugins folder and run the install.rb script.

Configuring GeoKit

After installing GeoKit you will need to configure it properly to allow it to work. GeoKit allows you to use a few sets of geocoding APIs, including Yahoo, Google, Geocoder.us, and Geocoder.ca.

These geocoding providers can be used directly or through a cascading failover sequence. Using Yahoo or Google requires you to register for an API key but they are free. Geocoder.us is also free under certain terms and conditions but both Geocoder.us and Geocoder.ca have commercial accounts. In this chapter I will briefl y go through how to get an application ID from Yahoo and a Google Maps API key from Google.

Getting an application ID from Yahoo

Yahoo’s application ID is needed for any Yahoo web service API calls. You can use the same application ID for all services in the same application or multiple applications or one application ID per service.

To get the Yahoo application ID, go to https://developer.yahoo.com/wsregapp/index.php and provide the necessary information. Note that for this application you don’t need user authentication. Once you click on submit, you will be provided an application ID.

Getting a Google Maps API key from Google

To use Google Maps you will need to have a Google Maps API key. Go to http://www.google.com/apis/maps/signup.html. After reading the terms and conditions you will be asked to give a website URL that will use the Google Maps API.

For geocoding purposes, this is not important (anything will do) but to display Google Maps on a website, this is important because Google Maps will not display if the URL doesn’t match. However all is not lost if you have provided the wrong URL at first; you can create any number of API keys from Google.

Configuring evironment.rb

Now that you have a Yahoo application ID and a Google Maps API key, go to environment.rb under the RAILS_ROOT/config folder. Installing GeoKit should have added the following to your environment.rb file:

[code]
# Include your application configuration below
# These defaults are
used in GeoKit::Mappable.distance_to and in acts_as_mappable
GeoKit::default_units = :miles
GeoKit::default_formula = :sphere
# This is the timeout value in seconds to be used for calls to the
geocoder web
# services. For no timeout at all, comment out the setting. The
timeout unit is in seconds.
# GeoKit::Geocoders::timeout = 3
# These settings are used if web service calls must be routed through
a proxy.
# These setting can be nil if not needed, otherwise, addr and port
must be filled in at a minimum. If the proxy requires authentication,
the username and password can be provided as well.
GeoKit::Geocoders::proxy_addr = nil
GeoKit::Geocoders::proxy_port = nil
GeoKit::Geocoders::proxy_user = nil
GeoKit::Geocoders::proxy_pass = nil
# This is your yahoo application key for the Yahoo Geocoder
# See http://developer.yahoo.com/faq/index.html#appid and
http://developer.yahoo.com/maps/rest/V1/geocode.html
GeoKit::Geocoders::yahoo = <YOUR YAHOO APP ID>
# This is your Google Maps geocoder key.
# See http://www.google.com/apis/maps/signup.html and
http://www.google.com/apis/maps/documentation/#Geocoding_Examples
GeoKit::Geocoders::google = <YOUR GOOGLE MAPS KEY>
# This is your username and password for geocoder.us
# To use the free service, the value can be set to nil or false. For
usage tied to an account, the value should be set to
username:password.
# See http://geocoder.us and
http://geocoder.us/user/signup
GeoKit::Geocoders::geocoder_us = false
# This is your authorization key for geocoder.ca.
# To use the free service, the value can be set to nil or false. For
usage tied to an account, set the value to the key obtained from
Geocoder.ca
# See http://geocoder.ca and
http://geocoder.ca/?register=1
GeoKit::Geocoders::geocoder_ca = false
# This is the order in which the geocoders are called in a failover
scenario
# If you only want to use a single geocoder, put a single symbol in
the array.
# Valid symbols are :google, :yahoo, :us, and :ca
# Be aware that there are Terms of Use restrictions on how you can
use the various geocoders. Make sure you read up on relevant Terms of
Use for each geocoder you are going to use.
GeoKit::Geocoders::provider_order = [:google,:yahoo]
[/code]

Go to the lines where you are asked to put in the Yahoo and Google keys and change the values accordingly. Make sure the keys are within apostrophes.

Then go to the provider order and put in the order you want (the first will be tried; if that fails it will go to the next until all are exhausted):

[code]
GeoKit::Geocoders::provider_order = [:google,:yahoo]
[/code]

This completes the configuration of GeoKit.

YM4R/GM

YM4R/GM is another Rails plugin, one that facilitates the use of Google Maps APIs. We will be using YM4R/GM to display the kiosk locations on a customized Google Map. This API essentially wraps around the Google Maps APIs but also provides additional features to make it easier to use from Ruby. To install it, go to your Rails application folder and execute this at the command line:

[code]
$./script/plugin install svn://rubyforge.org/var/svn/ym4r/Plugins/GM/trunk/ym4r_gm
[/code]

During the installation, the JavaScript files found in the RAILS_ROOT/vendors/plugin/javascript folder will be copied to the RAILS_ROOT/public/javascripts folder.

A gmaps_api_key.yml file is also created in the RAILS_ROOT/config folder. This file is a YAML representation of a hash, like the database.yml file in which you can set up a test, development, and production environment. This is where you will put in your Google Maps API key (in addition to the environment.rb you have changed earlier).

For your local testing you will not need to change the values but once you deploy this in production on an Internet site you will need to put in a real value according to your domain.

What we will be doing

As this project is a mashup plugin, normally you would already have an existing Rails application you want to add this to. However for the purpose of this chapter, I show how the mashup can be created on a fresh project. This is what we will be doing:

  • Create a new Rails project
  • Install the Rails plugins (GeoKit and YM4R/GM) that will use the various mashup APIs
  • Configure the database access and create the database
  • Create the standard scaffolding
  • Populate the longitude and latitude of the kiosks
  • Create the find feature
  • Display the found kiosk locations on Google Maps

Creating a new Rails project

This is the easiest part:

[code]
$rails Chapter2
[/code]

This will create a new blank Rails project.

Installing the Rails plugins that will use the various mashup APIs

In this mashup plugin we’ll need to use GeoKit, a Ruby geocoding library created by Bill Eisenhauer and Andre Lewis, and YM4R/GM—a Ruby Google Maps mapping API created by Guilhem Vellut. Install them according to the instructions given in the section above.

Next, we need to create the database that we will be using.

Configuring database access and creating the database

Assuming that you already know how database migration works in Rails, generate a migration using the migration generator:

[code]
$./script/generate migration create_kiosks
[/code]

This will create a file 001_create_kiosks.rb file in the RAILS_ROOT/db/migrate folder. Ensure the file has the following information:

[code]
class CreateKiosks < ActiveRecord::Migration
def self.up
create_table :kiosks do |t|
t.column :name, :string
t.column :street, :string
t.column :city, :string
t.column :state, :string
t.column :zipcode, :string
t.column :lng, :float
t.column :lat, :float
end
end
def self.down
drop_table :kiosks
end
end
[/code]

GeoKit specifies that the two columns must be named lat and lng. These two columns are critical to calculating the closest kiosks to a specific location.

Now that you have the migration script, run it to create the Kiosk table in your RAILS_ROOT folder:

Now that you have the migration script, run migrate to create the Kiosk table in your RAILS_ROOT folder:

[code]
$rake db:migrate
[/code]

This should create the database and populate the kiosks table with a set of data. If it doesn’t work please check if you have created a database schema with your favorite relational database. The database schema should be named chapter2_development. If this name displeases you somehow, you can change it in the RAILS_ROOT/config/database.yml file.

Creating scaffolding for the project

You should have the tables and data set up by now so the next step is to create a simple scaffold for the project. Run the following in your RAILS_ROOT folder:

[code]
$./script/generate scaffold Kiosk
[/code]

This will generate the Kiosk controller and views as well as the Kiosk model. This is the data model for Kiosk, in the kiosk.rb file. This is found in RAILS_ROOT/app/models/.

[code]
class Kiosk < ActiveRecord::Base
def address
"#{self.street}, #{self.city}, #{self.state}, #{self.zipcode}"
end
end
[/code]

Just add in the address convenience method to have quick access to the full address of the kiosk. This will be used later for the display in the info box.

Populating kiosk locations with longitude and latitude information

Before we begin geolocating the kiosks, we need to put physical addresses to them. We need to put in the street, city, state, and zipcode information for each of the kiosks. After this, we will need to geolocate them and add their longitude and latitude information. This information is the crux of the entire plugin as it allows you to find the closest kiosks.

In addition you will need to modify the kiosk creation screens to add in the
longitude and latitude information when the database entry is created.

Populate the database with sample data

In the source code bundle you will find a migration file named 002_populate_kiosks.rb that will populate some test data (admittedly less than 500 kiosks) into the system. We will use this data to test our plugin. Place the file in RAILS_ROOT/db/migrate and then run:

[code]
$rake db:migrate
[/code]

Alternatively you can have some fun entering your own kiosk addresses into the database directly, or find a nice list of addresses you can use to populate the database by any other means.

Note that we need to create the static scaffold first before populating the database using the migration script above. This is because the migration script uses the Kiosk class to create the records in the database. You should realize by now that migration scripts are also Ruby scripts.

Bulk adding of longitude and latitude

One of the very useful tools in Ruby, also used frequently in Rails, is rake. Rake is a simple make utility with rake scripts that are entirely written in Ruby. Rails has a number of rake scripts distributed along with its installation, which you can find out using this command:

[code]
$rake –tasks
[/code]

Rails rake tasks are very useful because you can access the Rails environment, including libraries and ActiveRecord objects directly in the rake script. You can create your own customized rake task by putting your rake script into the RAILS_ROOT/lib/tasks folder.

We will use rake to add longitude and latitude information to the kiosks records that are already created in the database.

Create an add_kiosk_coordinates.rake file with the following code:

[code]
namespace :Chapter2 do
desc ‘Update kiosks with longitude and latitude information’
task :add_kiosk_coordinates => :environment do
include GeoKit::Geocoders

kiosks = Kiosk.find(:all)
begin
kiosks.each { |kiosk|
loc = MultiGeocoder.geocode(kiosk.address)

kiosk.lat = loc.lat
kiosk.lng = loc.lng
kiosk.update
puts "updated kiosk #{kiosk.name} #{kiosk.address} =>
[#{loc.lat}, #{loc.lng}]"
}
rescue
puts $!
end
end
end
[/code]

In this rake script you first include the Geocoders module that is the main tool for discovering the coordinate information. Then for each kiosk, you find its longitude and latitude and update the kiosk record.

Run the script from the console in the RAILS_ROOT folder:

[code]
$rake Chapter2:add_kiosk_coordinates
[/code]

Depending on your network connection (running this rake script will of course require you to be connected to the Internet) it might take some time. Run it over a long lunch break or overnight and check the next day to make sure all records have a longitude and latitude entry. This should provide your mashup with the longitude and latitude coordinates of each kiosk. However your mileage may differ depending on the location of the kiosk and the ability of the geocoding API to derive the coordinates from the addresses.

Adding longitude and latitude during kiosk creation entry

Assuming that you have a kiosks_controller.rb already in place (it would be generated automatically along with the rest of the scaffolding), you need to add in a few lines very similar to the ones above to allow the kiosk created to have longitude and latitude information.

First, include the geocoders by adding GeoKit after the controller definition, in kiosks_controller.rb.

[code]
class KiosksController < ApplicationController
include GeoKit::Geocoders
[/code]

Next, add in the highlighted lines in the create method of the controller.

[code]
def create
@kiosk = Kiosk.new(params[:kiosk])
loc = MultiGeocoder.geocode(@kiosk.address)
@kiosk.lat = loc.lat
@kiosk.lng = loc.lng

if @kiosk.save
flash[:notice] = ‘Kiosk was successfully created.’
redirect_to :action => ‘list’
else
render :action => ‘new’
end
end
[/code]

Finally, modify the update method in the controller to update the correct longitude and latitude information if the kiosk location changes.

[code]
def update
@kiosk = Kiosk.find(params[:id])
address = "#{params[:kiosk][:street]}, #{params[:kiosk][:city]},
#{params[:kiosk][:state]}"
loc = MultiGeocoder.geocode(address)
params[:kiosk][:lat] = loc.lat
params[:kiosk][:lng] = loc.lng
if @kiosk.update_attributes(params[:kiosk])
flash[:notice] = ‘Kiosk was successfully updated.’
redirect_to :action => ‘show’, :id => @kiosk
else
render :action => ‘edit’
end
end
[/code]

Creating the find closest feature

Now that you have the kiosk data ready, it’s time to go down to the meat of the code. What you’ll be creating is a search page. This page will have a text field for the user to enter the location from which a number of kiosks closest to it will be displayed. However, to be user-friendly, the initial location of the user is guessed and displayed on the text field.

Create a search action in your controller (called search.rhtml, and place it in RAILS_ROOT/app/views/kiosks/) to find your current location from the IP address retrieved from your user.

[code]
def search
loc = IpGeocoder.geocode(request.remote_ip)
@location = []
@location << loc.street_address << loc.city << loc.country_code
end
[/code]

The remote_ip method of the Rails-provided request object returns the originating IP address, which is used by GeoKit to guess the location from Hostip.info. The location is then used by search.rhtml to display the guessed location.

Note that if you’re running this locally, i.e. if you are browsing the application from your PC to a locally running server (for example, off your PC as well), you will not get anything. To overcome this, you can use a dynamic DNS service to point an Internet domain name to the public IP address that is assigned to your PC by your ISP. You will usually need to install a small application on your PC that will automatically update the DNS entry whenever your ISP-assigned IP address changes. There are many freely available dynamic DNS services on the Internet.

When accessing this application, use the hostname given by the dynamic DNS service instead of using localhost. Remember that if you’re running through an internal firewall you need to open up the port you’re starting up your server with. If you have a router to your ISP you might need to allow port forwarding.

This is a technique you will use subsequently in Chapters 5 and 6.

Create a search.rhtml file and place it in the RAILS_ROOT/app/view/kiosks folder with the following code:

[code]
<h1>Enter source location</h1>
Enter a source location and a radius to search for the closest kiosk.
<% form_tag :action => ‘find_closest’ do %>
<%= text_field_tag ‘location’, @location.compact.join(‘,’) %>
<%= select_tag ‘radius’, options_for_select({‘5 miles’ => 5, ’10
miles’ => 10, ’15 miles’ => 15}, 5) %>
<%= submit_tag ‘find’ %>
<% end %>
[/code]

Here you’re asking for the kiosks closest to a specific location that are within a certain mile radius. We will be using this information later on to limit the search radius.

After that, mix-in the ActsAsMappable module into the Kiosk model in kiosk.rb.

[code]
class Kiosk < ActiveRecord::Base
acts_as_mappable
end
[/code]

This will add in a calculated column called (by default) distance, which you can use in your condition and order options. One thing to note here is that the ActsAsMappable module uses database-specific code for some of its functions, which are only available in MySQL and PostgresSQL.

Next, create the find_closest action to determine the location of nearest kiosks.

[code]
def find_closest
@location = MultiGeocoder.geocode(params[:location])
if @location.success
@kiosks = Kiosk.find(:all,
:origin => [@location.lat, @location.lng],
:conditions => "distance < #{params[:radius]}",
:order=>’distance’)
end
end
[/code]

The ActsAsMappable module mixed in also overrides the find method to include an originating location, either based on a geocode-able string or a 2-element array containing the longitude/latitude information. The returned result is a collection of kiosks that are found with the given parameters.

Finally create a simple find_closest.rhtml view template (and place it in the RAILS_ROOT/app/view/kiosks/ folder) to display the kiosks that are retrieved. We’ll add in the complex stuff later on.

[code]
<h1><%= h @kiosks.size %> kiosks found within your search radius</h1>
<ol>
<% @kiosks.each do |kiosk| %>
<li><%= kiosk.name%><br/></li>
<% end %>
</ol>
[/code]

Do a quick trial run and see if it works.

[code]
$./script/server
[/code]

Then go to http://localhost:3000/kiosks/search. If you have some data, put in a nearby location (e.g. from our source data: San Francisco) and click on ‘find’. You should be able to retrieve some nearby kiosks.

Displaying kiosks on Google Maps

Now that you know where the kiosks are located, it’s time to show them on Google Maps. For this we’ll be using the YM4R/GM plugin. If you haven’t installed this plugin yet, it’s time to go back and install it.

To add display to Google Maps, you will need to change the find_closest action as well as the find_closest view template. First, add the find_closest action in the kiosks_controller.rb:

[code]
def find_closest
@location = MultiGeocoder.geocode(params[:location])
if @location.success
@kiosks = Kiosk.find(:all,
:origin => [@location.lat, @location.lng],
:conditions => ["distance < ?", params[:radius]],
:order=>’distance’)
@map = GMap.new("map_div")
@map.control_init(:large_map => true, :map_type => true)
# create marker for the source location
@map.icon_global_init( GIcon.new(:image =>
"http://www.google.com/mapfiles/ms/icons/red-pushpin.png",
:shadow => "http://www.google.com/
mapfiles/shadow50.png",
:icon_size => GSize.new(32,32),
:shadow_size => GSize.new(37,32),
:icon_anchor => GPoint.new(9,32),
:info_window_anchor => GPoint.new(9,2),
:info_shadow_anchor =>
GPoint.new(18,25)),
"icon_source")
icon_source = Variable.new("icon_source")
source = GMarker.new([@location.lat, @location.lng],
:title => ‘Source’,
:info_window => "You searched for kiosks
<br>#{params[:radius]} miles around this source",
:icon => icon_source)
@map.overlay_init(source)
# create markers one for each location found
markers = []
@kiosks.each { |kiosk|
info = <<EOS
<em>#{kiosk.name}</em><br/>
#{kiosk.distance_from(@location).round} miles away<br/>
<a href="http://maps.google.com/maps?saddr=#{u(@location.to_
geocodeable_s)}&daddr=#{u(kiosk.address)}>directions here from
source</a>
EOS
markers << GMarker.new([kiosk.lat, kiosk.lng], :title =>
kiosk.name, :info_window => info)
}
@map.overlay_global_init(GMarkerGroup.new(true, markers),"kiosk_
markers")
# zoom to the source
@map.center_zoom_init([@location.lat, @location.lng], 12)
end
end
[/code]

Google Maps API is a JavaScript library and YM4R/GM code is a library that creates JavaScript scripts to interact and manipulate the Google Maps API. Almost all classes in the library correspond with an equivalent Google Maps API class, so it is important that you are also familiar with the Google Maps API. The online documentation comes in very useful here so you might want to open up the Google Maps reference documentation (http://www.google.com/apis/maps/documentation/reference.html) as you are coding.

Let’s go over the code closely.

The first line creates a GMap object that is placed inside a

tag with the id map_div while the second line sets some control options.

[code]
@map = GMap.new("map_div")
@map.control_init(:large_map => true, :map_type => true)
[/code]

The next few lines then create a GMarker object from the source location that the user entered that uses a specific icon to show it then overlays it on the map. There are several options you can play around with here involving setting the image to be shown as the marker. For this chapter I used a red-colored pushpin from Google Maps itself but you can use any image instead. You can also set the text information window that is displayed when you click on the marker. The text can be in HTML so you can add in other information including images, formatting, and so on.

[code]
# create marker for the source location
@map.icon_global_init( GIcon.new(:image =>
"http://www.google.com/mapfiles/ms/icons/red-pushpin.png",
:shadow => "http://www.google.com/
mapfiles/shadow50.png",
:icon_size => GSize.new(32,32),
:shadow_size => GSize.new(37,32),
:icon_anchor => GPoint.new(9,32),
:info_window_anchor => GPoint.new(9,2),
:info_shadow_anchor =>
GPoint.new(18,25)), "icon_source")
icon_source = Variable.new("icon_source")
source = GMarker.new([@location.lat, @location.lng],
:title => ‘Source’,
:info_window => "You searched for kiosks
<br>#{params[:radius]} miles around this source",
:icon => icon_source)
@map.overlay_init(source)
[/code]

The lines of code after that go through each of the located kiosks and create a GMarker object then overlay it on the map too. For each kiosk location, we put in an info window that describes the distance away from the source location and a link that shows the directions to get from the source to this kiosk. This link goes back to Google and will provide the user with instructions to navigate from the source location to the marked location.

Note that you need to URL encode the location/address strings of the source and kiosks, so you need to include ERB::Util as well (along with GeoKit::Geocoders). This is the u() method. In kiosks_controller.rb,add:

[code]
include ERB::Util
[/code]

then add the following (beneath the code entered above):

[code]
# create markers one for each location found
markers = []
@kiosks.each
{ |kiosk|
info = <<EOS
<em>#{kiosk.name}</em><br/>
#{kiosk.distance_from(@location).round} miles away<br/>
<a href="http://maps.google.com/maps?saddr=#{u(@location.
to_geocodeable_s)}&daddr=#{u(kiosk.address)}>directions here from
source</a>
EOS
markers << GMarker.new([kiosk.lat, kiosk.lng],
:title => kiosk.name, :info_window => info)
}
@map.overlay_global_init(GMarkerGroup.new(true, markers),
"kiosk_markers")
[/code]

Finally the last line zooms in and centers on the source location.

[code]
# zoom to the source
@map.center_zoom_init([@location.lat, @location.lng], 12)
[/code]

Now let’s look at how the view template is modified to display Google Maps. The bulk of the work has already been done by YM4R/GM so you need only to include a few lines.

[code lang=”html”]
<h1><%= h @kiosks.size %> kiosks found within your search radius</h1>
<ol>
<% @kiosks.each do |kiosk| %>
<li><%= kiosk.name%><br/></li>
<% end %>
</ol>
<%= GMap.header %>
<%= javascript_include_tag("markerGroup") %>
<%= @map.to_html%>
<%= @map.div(:width => 500, :height => 450)%>
[/code]

Gmap.header creates the header information for the map, including YM4R/GM and Google Maps API JavaScript files. We are also using GMarkerGroups so we need to include the GMarkerGroup JavaScript libraries. Next, we need to initialize the map by calling map.to_html. Finally we’ll need to have a div tag that is the same as the one passed to the GMap constructor in the controller (map_div). This is done by calling the div method of the GMap object. To size the map correctly we will also need to pass on its dimensions (height and width here).

And you’re ready to roll! Although the page doesn’t display the best layout, you can spice things up by adding the necessary stylesheets to make the view more presentable.

Summary

What we’ve learned in this chapter is to create a mashup with Ruby on Rails on a number of mapping and geocoding providers including Yahoo, Google, geocoder. us, geocoder.ca, and hostip.info. We learned to create a mashup that gives us a map of the closest kiosks to a particular location, given an existing database of kiosks that have location addresses. This is just an introduction to the synergistic value that mashups bring to the table, creating value that was not available in individual APIs. When they are all put together, you have a useful feature for your website.

Filed Under: Ruby Tagged With: Rails

Building Dynamic Web 2.0 Websites with Ruby on Rails

September 29, 2009 by itadmin Leave a Comment

Building Dynamic Web 2.0 Websites with Ruby on Rails

Ruby on Rails is an open-source web application framework ideally suited to building business applications, accelerating and simplifying the creation of database-driven websites. It has been developed on the Ruby platform.

This book is a tutorial for creating a complete website with Ruby on Rails (RoR). It will teach you to develop database-backed web applications according to the Model-View-Controller pattern. It will take you on a joy ride right from installation to a complete dynamic website. All the applications discussed in this book will help you add exciting features to your website. This book will show you how to assemble RoR’s features and leverage its power to design, develop, and deploy a fully featured website.

What This Book Covers

Chapter 1 gives you an overview of the features of Ruby and RoR, as well as providing the various ways of installing, configuring, and testing both Ruby and RoR.

Chapter 2 introduces you to the basics of Ruby as well as the main concepts and components of RoR.

Chapter 3 makes you understand the design of tables according to the conventions of RoR, creation of scaffolds for tables, and changing the scaffolds according to the requirements.

Chapter 4 gives you details about how to set up the User Management module for the website called TaleWiki.

Chapter 5 makes you familiar with the Login Management and Comment Management modules for TaleWiki.

Chapter 6 introduces you to the Migrations and Layouts involved in setting up the template for TaleWiki.

Chapter 7 describes the tagging functionality being implemented for the enhanced search usability.

Chapter 8 provides you with the implementation of AJAX for TaleWiki.

Chapter 9 deals with the development of an interface for the administration.

Chapter 10 gives you the steps for deploying the website.

Gathering User Comments

In the last chapter, we saw how to set up User Management and Role Management for TaleWiki. However, we did not set up the Login Management based on Users. So, it was work only half done. To complete the task, we will set up Login Management in this chapter. It will not only authenticate a user but also provide the session management.

Secondly, we will look at how to gather user comments for a particular story. We will start with the functionalities to be provided by the Comment Gathering module. We will then move on to the database design for the module. After that we will not only set up the Login Management but also modify the Tale Management so that the User and Tales can be related. We will wrap up with the implementation of the Comment Gathering module. Let’s gets started.

Understanding the Requirements

In this chapter, we will be tackling two problems—managing the user authentication as well as the session management and accepting comments from other users for a particular tale. So we can divide the requirements into two:

  • Login Management
  • Comment management

The Login Management module will also provide the solution to the problem of Tale management that evolved during the development of User management. As the tales table refers to the users table, without a user id a new tale cannot be submitted. The Login management will provide us the user id corresponding to the new tales. Also, it will tell us who has commented on a particular tale. Let us see how.

Login Management

As the name suggests, the main functionality the Login Management will provide will be managing the logins. However, managing logins is not a single task. It is dependent on others tasks or operations as well. So, the overall functionalities we will be developing as part of Login management are:

  • Authenticating the User: We can allow only the registered users to access the functionalities of TaleWiki. This operation will ensure that the user is a registered user before he or she tries to enter the TaleWiki.
  • Setting the Session: Once the user is found to be authentic, then we have to maintain his/her authenticity until he/she logs out. The authenticity can be maintained by this functionality.
  • Checking Roles: Each User is assigned a Role. So we will need to check whether a particular functionality—such as viewing details of another user—is a part of the Role. This functionality will check the User’s Role whenever he/she tries to access any functionality provided by TaleWiki.
  • Invalidating Session: When a user logs out, all the details of the user in the current session need to be cleared out. This functionality will clear out all the details of the user, including whether the user is authentic or not.

Now that we have defined the functionalities of Login management, let us move on to the next set of tasks—managing the comments.

Managing the Comments

It is natural for a person to comment upon whatever he or she reads. So, it is necessary to provide a way for users to comment on a particular story. The comments can be of two types—threaded and non-threaded. In threaded comments, one comment can be posted as a response for another comment. If the first comment is removed, then all its child comments will also be removed. If we go for non-threaded
comments, then each comment is considered an individual. So if one is deleted, others are not affected.

The Comment Management module will do the same. The functionalities that the Comment Management module will provide are:

  • Adding a Comment: When a user wants to comment on a particular story, he or she can use this functionality. A user can comment on many stories. Comments are not threaded. That means a comment cannot be a response for another comment. Each comment is considered an individual.
  • Deleting a Comment: If an administrator finds a comment offensive or feels that comments are very old, this functionality can be used to delete such comments. Only the administrator will have access to this functionality.
  • Viewing Comments: Using this functionality, a user can read all the comments submitted for a particular story. It will be available for all users. In addition, the comments will be shown in the list view and the details view. In list view, the comments will be shown for each story, and in the details view, all the details including the date and complete text of the comment will be shown.

We are not providing a way to modify a posted comment. That is because comments are considered one time and brief view of what the user thinks. Hence, no functionality will be provided for the modification of comments. That wraps up the requirements of the Login and Comment Management modules. Next, let us work on the database design for the modules.

Designing the Database

As you would have already guessed, our next step will be designing the database. However, unlike the modules that we developed previously, we will be designing the database only for one of the two modules. The Login management module doesn’t require a table because its functionalities are based on the users and roles tables. So we will have to design the table for the Comment management module only. Just like the previous chapter, the steps for designing the database are:

  • Designing the E-R Model
  • Deriving the Schemas
  • Creating the Tables

Whenever a new module is added, some of the existing E-R models need to be refined, and consequently the corresponding schemas and tables will be changed accordingly. In the case of Comment management, this holds true as you will see as we go through the steps. So here we go.

Designing the E-R Model

As the name suggests, the Comment Management module will have data related to the comments submitted by the user. What is this data apart from the comment itself? To answer this, first let us try to define the functionality of the Comment Management module in one line. ‘Comment management will manage comments submitted by a user for a particular story’—that’s how what will look like. The important point here is ‘comments submitted by a user for a particular story’. We have three main entities—Comments, Users, and Stories. Story and User entities have already been discussed in Chapters 3 and 4. So let us look at the Comments entity. The attributes for comments will include the date on which the comment has been added and the title of the comment. In short, the Comments entity will have the following attributes:

  • Id—the unique number to identify each comment
  • Comment body—the text of the comment
  • Date—the date on which comment was added
  • User—the user who has added the comment
  • Story—the story on which the comment has been made

The entity diagram for the Comments entity will be as follows:

Coming back to our one line definition, we know that the User, Story, and Comments entities are related. The question is how are they related? The answer is there in the one line definition itself. First, let us consider the User entity. The definition says ‘comments submitted by a user’. That means one user can submit many comments. Hence, the User entity has a one-to-many relationship with the Comments entity. The relationship will be as follows in terms of an E-R diagram:

The next part of the definition tells us ‘comments for a story’. This means that one story can have many comments. In other words, the Comments entity is related to the Story entity through a many-to-one relationship. The Story entity will be at the ‘one’ end and the Comments entity will be at the ‘many’ end of the relationship. The diagram will look like as follows:

When looking at all the entities with their attributes and relationships, the picture will be as follows:

The next step obviously is deriving the schema. Here it comes.

Deriving the Schema

We have the complete information about the attributes and relationships of the Comments entity. The main point about this entity is that unlike the User entity it doesn’t introduce any changes in the existing schemas. The reason is that the Comment entity is dependent on other entities and not vice versa. The schema will be as follows:

Here Story and User both have their own schemas. So their Ids will be the foreign keys in the table. Now, we can develop the table.

Creating the Tables

There is only one table to be created. Apart from the attributes, the comments table (keeping with the naming convention), will have two foreign key references—one to the users table and another to the tales table. Including these, the SQL query will be as follows:

[code]
CREATE TABLE `comments` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`comment_body` TEXT NOT NULL ,
`submission_date` DATE NOT NULL ,
`tale_id` INT NOT NULL,
`user_id` INT NOT NULL,
CONSTRAINT `fk_comments_users` FOREIGN KEY (`user_id`) REFERENCES
users( `id`) ,
CONSTRAINT `fk_comments_tales` FOREIGN KEY (`tale_id`) REFERENCES
tales( `id`)
) ENGINE = innodb;
[/code]

That completes the table definition. By this time you will have started to think that if RoR is so productive, why do we still have to use the SQL statements to create tables?. There is another way—the Ruby way. We will see that in the next chapter where we will convert all the table creation statements using Ruby. Now that the required table has been defined, let us develop the modules starting with the Login management.

Developing the Login Management Module

Even though Login and session handling are separate functionalities from User management, they depend on the same table—user. Also, the functionalities are more alike than different. Hence, instead of creating a new Controller, we will be using the UserController itself as the Controller for the Login module. Keeping this point in mind, let us look at the steps involved in developing the Login Management, which are:

  • Creating the Login page
  • Implementing the Authentication Method
  • Setting up the Session
  • Applying Authorization

Leaving aside the first step, all other steps mainly focus on the Controller. Here we go.

Creating the Login Page

We need a login page with textboxes for user name and password in which users can put their credentials and submit to the login authenticator (fancy name for the action method that will contain the logic to authenticate the user). That’s what we are going to create now. The convention for any website is to show the login page when the user enters the URL without any specific page in mind. RoR also follows this convention.
For example, if you enter the URL as http://localhost:3000/user, it displays the list of users. The reason is that the index action method of the UserController class calls the list method whenever the aforementioned URL is used. From this, we can understand two things—first, the default action method is index, and second, the first page to be shown is changeable if we change the index method.

What we need is to show the login page whenever a user enters the URL http://localhost:3000/user. So let’s change the index method. Open the user_controller.rb file from the app/views/user folder and remove all the statements from the body of the index method so that it looks like as follows:

[code]
def index
end
[/code]

Next, let us create an index.rhtml file, which will be shown when the index method is called. This file will be the login page. In the app/views/user folder, create an index.rhtml file. It will be as follows:

[code]
<%= form_tag :action=> ‘authenticate’%>
<table >
<tr align="center" class="tablebody">
<td>User name:</td>
<td><%= text_field("user", "user_name",:size=>"15" ) %></td>
</tr>
<tr align="center" class="tablebody">
<td>Password:</td>
<td><%= password_field("user",
"password",:size=>"17" ) %></td>
</tr>
<tr align="center" class="tablebody">
<td></td>
<td><input type="submit" value=" LOGIN " /></td>
</tr>
</tabale>
[/code]

It uses two new form helpers—text_field and password_field. The text_field creates a text field with the name passed as the parameter, and the password_field creates a password field again with the name passed as the parameter. We have passed the authenticate method as the action parameter so that the form is submitted to the authenticate method. That completes the login page creation. Next, we will work on the authenticate method.

Implementing the Authenticate method

Authenticating a user essentially means checking whether the user name and password given by the user corresponds to the one in database or not. In our case, the user gives us the user name and password through the login page. What we will be doing is checking whether the user is in database and does the password that we got corresponds to the password stored in the database for the user? Here, we will be working on two levels:

  • Model
  • Controller

We can put the data access part in the action method that being the Controller itself. But it will create problems in the future if we want to add something extra to the user name/password checking code. That’s why we are going to put (or delegate) the data access part into Model.

Model

We will be modifying the User class by adding a method that will check whether the user name and password provided by the user is correct or not. The name of the method is login. It is as follows:

[code]
def self.login(name,password)
find(:first,:conditions => ["user_name = ? and password =
?",name, password])
end
[/code]

It is defined as a singleton method of the User class by using the self keyword. The singleton methods are special class-level methods. The conditions parameter of the find method takes an array of condition and the corresponding values. The find method generates an SQL statement from the passed parameters. Here, the find method finds the first record that matches the provided user_name and password. Now, let us create the method that the Controller will call to check the validity of the user. Let us name it check_login. The definition is as follows:

[code]
def check_login
User.login(self.user_name, self.password)
end
[/code]

This function calls the login method. Now if you observe closely, check_login calls the login function. One more point to remember—if a method ‘test’ returns a value and you call ‘test’ from another method ‘test1,’ then you don’t need to say ‘return test’ from within ‘test1’.The value returned from ‘test’ will be returned by ‘test1’ implicitly. That completes the changes to be done at the Model level. Now let us see the changes at the Controller-level.

Controller

In the Controller for User—UserController—add a new method named authenticate. The method will first create a User object based on the user name and password. Then it will invoke check_login on the newly created User object. If check_login is successful, that is, it does not return nil, then the user is redirected to the list view of Tales. Otherwise, the user is redirected to the login page itself. Here is what the method will look like:

[code]
def authenticate
@user = User.new(params[:user])
valid_user = @user.check_login
if logged_in_user
flash[:note]="Welcome "+logged_in_user.name
redirect_to(:controller=>’tale’,:action => "list")
else
flash[:notice] = "Invalid User/Password"
redirect_to :action=> "index"
end
end
[/code]

The redirect_to method accepts two parameters—the name of the Controller and the method within the Controller. If the user is valid, then the list method of TaleController is called, or in other words, the user is redirected to the list of tales. Next, let us make it more robust by checking for the get method. If a user directly types a URL to an action, then the get method is received by the method. If any user does that, we want him/her to be redirected to the login page. To do this, we wrap up the user validation logic in an if/else block. The code will be the following:

[code]
def authenticate
if request.get?
render :action=> ‘index’
else
@user = User.new(params[:user])
valid_user = @user.check_login
if valid_user
flash[:note]="Welcome "+valid_user.user_name
redirect_to(:controller=>’tale’,:action => ‘list’)
else
flash[:notice] = "Invalid User/Password"
redirect_to :action=> ‘index’
end
end
end
[/code]

The get? method returns true if the URL has the GET method else it returns false. That completes the login authentication part. Next, let us set up the session.

In Ruby, any method that returns a Boolean value—true or false—is suffixed with a question mark (?). The get method of the request object returns a boolean value. So it is suffixed with a question mark (?).

Setting up the Session

Once a user is authenticated, the next step is to set up the session to track the user. Session, by definition, is the conversation between the user and the server from the moment the user logs in to the moment the user logs out. A conversation is a pair of requests by the user and the response from the server. In RoR, the session can be tracked either by using cookies or the session object. The session is an object provided by RoR. The session object can hold objects where as cookies cannot. Therefore, we will be using the session object. The session object is a hash like structure, which can hold the key and the corresponding value. Setting up a session is as easy as providing a key to the session object and assigning it a value. The following code illustrates this aspect:

[code]
def authenticate
if request.get?
render :action=> ‘index’
else
@user = User.new(params[:user])
valid_user = @user.check_login
if valid_user
session[:user_id]=valid_user.id
flash[:note]="Welcome "+valid_user.user_name
redirect_to(:controller=>’tale’,:action => ‘list’)
else
flash[:notice] = "Invalid User/Password"
redirect_to :action=> ‘index’
end
end
end
[/code]

That completes setting up the session part. That brings us to the last step—applying authorization.

Applying Authorization

Until now, we have authenticated the user and set up a session for him/her. However, we still haven’t ensured that only the authenticated users can access the different functionalities of TaleWiki. This is where authorization comes into the picture. Authorization has two levels—coarse grained and fine grained. Coarse grained authorization looks at the whole picture whereas the fine grained authorization looks at the individual ‘pixels’ of the picture. Ensuring that only the authenticated users can get into TaleWiki is a part of coarse grained authorization while checking the privileges for each functionality comes under the fine grained authorization. In this chapter, we will be working with the coarse grained authorization.

The best place to apply the coarse grained authorization is the Controller as it is the central point of data exchange. Just like other aspects, RoR provides a functionality to easily apply any kind of logic on the Controller as a whole in the form of filters. To jog your memory, a filter contains a set of statements that need to be executed before, after (or before and after) the methods within the Controllers are executed.

Our problem is to check whether the user is authenticated or not, before any method in a Controller is executed. The solution to our problem is using a ‘before filter’. But we have to apply authorization to all the Controllers. Hence, the filter should be callable from any of the Controller. If you look at the definition of a Controller, you can find such a place. Each Controller is inherited from the ApplicationController. Anything placed in ApplicationController will be callable from other Controllers. In other words, any method placed in ApplicationController becomes global to all the Controllers within your application. So, we will place the method containing the filter logic in ApplicationController.

To check whether a user is authentic or not, the simplest way is to check whether a session exists for that person or not. If it exists, then we can continue with the normal execution. Let us name it check_authentic_user. The implementation will be as follows:

[code]
def check_authentic_user
unless session[:user_id]
flash[:notice] = "Please log in"
redirect_to(:controller => "user", :action =>
"index")
end
end
[/code]

It checks for the user_id key in a session. If it is not present, the user is redirected to the login page. Place the code in the application.rb file as a method of ApplicationController. Next, let us use it as a filter. First, we will tell UserController to apply the filter for all the action methods except index and authenticate methods. Add the following statement to the UserController. It should be the first statement after the starting of the Controller class.

[code]
class UserController < ApplicationController
before_filter :check_authentic_user, :except =>[ :index, :authenticate
]
:
:
end
[/code]

Similarly, we will place the filter in other Controllers as well. However, in their case, there are no exceptions. So TaleController will have:

[code]
class TaleController < ApplicationController
before_filter :check_authentic_user
:
:
end
[/code]

GenreController and RoleController will be the same as TaleController. Thus, we have completed the ‘applying authorization’ part for the time being. Now, let’s tie up one loose end—the problem of adding a new tale.

Tying Up the Loose Ends

When we developed the User management, the Tale management was affected as the tales table has a many-to-one relationship with the users table. Now we can solve the problem created by the foreign key reference. First, open the user.rb file and add the following statement indicating that it is at the ‘one’ end of the relationship:

[code]
has_many :tale
[/code]

After addition of the statement, the class will look like the following:

[code]
class User < ActiveRecord::Base
validates_presence_of :user_name, :password, :first_name,
:last_name, :age, :email, :country
validates_uniqueness_of :user_name
validates_numericality_of :age
validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-
z0-9]+\.)+[a-z]{2,})\Z/i
belongs_to :role
has_many :tale
def check_login
User.login(self.name, self.password)
end
def self.login(name,password)
find(:first,:conditions => ["user_name = ? and password
=?",name, password])
end
end
[/code]

Next, add the following statement to the tale.rb file:

[code]
belongs_to :user
[/code]

The file will look like as follows:

[code]
class Tale < ActiveRecord::Base
validates_presence_of :title, :body_text, :source
belongs_to:genre
belongs_to :user
end
[/code]

Next, open the tale_controller.rb file. In the create method, we need to add the user’s id to the tale’s user id reference so that the referential integrity can be satisfied. For that, we will get the current user’s id from the session and set it as the value of the user_id attribute of the tale object. The create method will look like as follows, after doing the changes:

[code]
def create
@tale = Tale.new(params[:tale])
@tale.genre_id=params[:genre]
@tale.user_id=session[:user_id]

@tale.status="new"
if @tale.save
flash[:notice] = ‘Tale was successfully created.’
redirect_to :action => ‘list’
else
render :action => ‘new’
end
end
[/code]

That’s it. The ‘loose ends’ related to the User management are tied up. Now let us move onto the Comment Management module.

Developing the Comment Management Module

From the description of functionalities, we know that the module needs to support only three operations—add, view, and delete. The steps for developing the module are almost the same:

  • Generating the Scaffold
  • Modifying the Model
  • Refining the View
  • Customizing the Controller

We have changed the order of refining the view and customizing the Controller steps. That’s what I meant by ‘almost the same’. Let’s get into the development.

Generating the Scaffold

Open the RoR prompt using use_ruby command, and enter the following command:

[code]
C:\InstantRails\rails_apps\talewiki>ruby script/generate scaffold Comment comment list show new create destroy
[/code]

You will get the following screen:

If the scaffold command is reused, then it will not rewrite the existing files unless you specify the -force parameter. We need only new, list, and delete functionalities. So, we have specified the actions that we need—list, show for listing of comments, new and create for adding, and delete for deleting. However, it will still create the stubs and links that need to be tackled at the View level. First, let us do the required modifications at the Model level.

Modifying the Model

First, we have to tell RoR which fields should not be empty. For that, add the validates_presence_of method with :comment_body as the argument in the comment.rb file. After addition, the code shall be as follows:

[code]
class Comment < ActiveRecord::Base
validates_presence_of :comment_body
end
[/code]

Next, we have to tell that the comments table is at the ‘many’ end of the relationship with both tales and users table. For that, add a belongs_to declaration to the comment.rb file.

[code]
class Comment < ActiveRecord::Base
validates_presence_of :comment_body
belongs_to :tale
belongs_to :user
end
[/code]

The next step is to tell both the users and the tales table that they are at the ‘one’ end of the relationship. For that, open the user.rb and tale.rb files, and add the has_many declaration. After the additions, the code will be as follows for user.rb:

[code]
class User < ActiveRecord::Base
validates_presence_of :user_name, :password, :first_name,
:last_name, :age, :email, :country
validates_uniqueness_of :user_name
validates_numericality_of :age
validates_format_of :email, :with => /\A([^@\s]+)@((?:[-az0-
9]+\.)+[a-z]{2,})\Z/i
belongs_to :role
has_many :tale
has_many :comment
def check_login
User.login(self.name, self.password)
end

def self.login(name,password)
find(:first,:conditions => ["user_name = ? and password
=?",name, password])
end
end
[/code]

For tale.rb, here is the code:

[code]
class Tale < ActiveRecord::Base
validates_presence_of :title, :body_text, :source
belongs_to:genre
belongs_to :user
has_many :comment
end
[/code]

That completes the changes to be done at the Model level. Next, let us refine the View.

Refining the View

Comments will be given for a story. That means the page displaying a tale will have a link to add comments. This also means that the Comment management module is not a ‘standalone’ module like others, as it will not have its own menu when we decide upon the template. Now coming back to links to the comments in the tale display page, for what functionalities do we need the links? The answer is two—adding a comment and listing the comment. The add comment link will lead to the ‘New Comment’ page, and the view comments link will lead to the list view of the comments. Now let us see what are the problems—each comment needs a user id and the id of the tale for which the comment is being added. The listing of comments needs only the id of the tale. As user id is available from the session, we have to add only the tale id as a part of the link. That is what we are going to do.

Open the show.rhtml file from the app/views/tale directory. It contents are as follows

[code lang=”html”]
<% for column in Tale.content_columns %>
<p>
<%= column.human_name %>: <%=h @tale.send(column.name) %>
</p>
<% end %>
<%= link_to ‘Edit’, :action => ‘edit’, :id => @tale %>
<%= link_to ‘Back’, :action => ‘list’ %>
[/code]

Now let us add two more links—one for adding a comment and another for listing the comments:

[code lang=”html”]
<% for column in Tale.content_columns %>
<p>
<%= column.human_name %>: <%=h @tale.send(column.name) %>
</p>
<% end %>

<%= link_to ‘Edit’, :action => ‘edit’, :id => @tale %>
<%= link_to ‘Back’, :action => ‘list’ %>
<%= link_to ‘Add Comment’,:controller=>’comment’, :action => ‘new’, :
id => @tale.id %>
<%= link_to ‘View Comments’,:controller=>’comment’, :action => ‘list’,
:id => @tale.id %>
[/code]

The next change we have to do is remove the edit option from the viewing part of the comments. So open the list.rhtml file from the app/views/comments. The code will be as follows:

[code lang=”html”]
<h1>Listing comments</h1>
<table>
<tr>
<% for column in Comment.content_columns %>
<th><%= column.human_name %></th>
<% end %>
</tr>

<% for comment in @comments %>
<tr>
<% for column in Comment.content_columns %>
<td><%=h comment.send(column.name) %></td>
<% end %>
<td><%= link_to ‘Show’, :action => ‘show’, :id => comment %></td>
<td><%= link_to ‘Edit’, :action => ‘edit’, :id => comment %></td>
<td><%= link_to ‘Destroy’, { :action => ‘destroy’, :id => comment
}, :confirm => ‘Are you sure?’, :method => :post %></td>
</tr>
<% end %>
</table>
<%= link_to ‘Previous page’, { :page => @comment_pages.current.
previous } if @comment_pages.current.previous %>
<%= link_to ‘Next page’, { :page => @comment_pages.current.next } if @
comment_pages.current.next %>
<br />
<%= link_to ‘New comment’, :action => ‘new’ %>
[/code]

Delete the tags that link to the Edit and New Comment functionalities. We do not need anyone adding a comment without reading the story. After deletions, the code will be as follows:

[code lang=”html”]
<h1>Listing comments</h1>
<table>
<tr>
<% for column in Comment.content_columns %>
<th><%= column.human_name %></th>
<% end %>
</tr>
<% for comment in @comments %>
<tr>
<% for column in Comment.content_columns %>
<td><%=h comment.send(column.name) %></td>
<% end %>
<td><%= link_to ‘Show’, :action => ‘show’, :id => comment %></td>
<td><%= link_to ‘Destroy’, { :action => ‘destroy’, :id => comment
}, :confirm => ‘Are you sure?’, :method => :post %></td>
</tr>
<% end %>
</table>
<%= link_to ‘Previous page’, { :page => @comment_pages.current.
previous } if @comment_pages.current.previous %>
<%= link_to ‘Next page’, { :page => @comment_pages.current.next } if @
comment_pages.current.next %>
<br />
[/code]

That completes the refinement to be done to the VIEW. Now let’s modify the Controller.

Customizing the Controller

Open the comment_controller.rb file and in the new method add the tale_id to the session object so that the method looks like the following:

[code]
def new
@comment = Comment.new
session[:tale_id]=params[:id]
end
[/code]

Now in the create method, let us get the tale_id and the user_id from the session, and pass it to the comment object. We have used the session object because the tale_id is coming as a part of the get request, which will be available only to the new method and not the create method. After the changes, the create method will be as follows:

[code]
def create
@comment = Comment.new(params[:comment])
@comment.tale_id=session[:tale_id]
@comment.user_id=session[:user_id]
if @comment.save
flash[:notice] = ‘Comment was successfully created.’
redirect_to :action => ‘list’
else
render :action => ‘new’
end
end
[/code]

We do not want to show the list of comments, once a comment has been added. Therefore, we will redirect the user to the tale’s list once a comment has been added successfully.

[code]
def create
@comment = Comment.new(params[:comment])
@comment.user_id=session[:user_id]
@comment.tale_id=session[:tale_id]
if @comment.save
flash[:notice] = ‘Comment was successfully created.’
redirect_to :controller=>’tale’, :action => ‘list’
else
render :action => ‘new’
end
end
[/code]

Apart from this, we have to change the list method so that it finds that only those
comments are selected for which the tale_id has been passed through the link. So
let us modify the paginate method in the list method to add a condition. After
modification, the list method will be as follows:

[code]
def list
@comment_pages, @comments = paginate :comments, :
conditions=>[‘tale_id = ?’,
params[:id]] :per_page => 10
end
[/code]

As you can see, the paginate method takes the table to paginate, the condition which is optional and the number of items to be shown per page as arguments.

And that completes our current work on the Comment management module. Now it is testing time!

Testing the Module

Let us start with the authorization part. Give the following URL at the address bar:

[code]
http://localhost:3000/tale
[/code]

If you get the following screen, it means authorization is working fine:

Next let us test the login functionality. Firstly, give the wrong User name and Password (give anything). If you get the following screen, then the changes are working fine:

Now, give the correct User name/Password combination. I am giving tester as User name and testing as password. If you get the following screen, then authentication is working fine, and also the redirection is doing what it is supposed to do.

Now click on the list link of the first tale and you will get the following screen:

On the detail page, click on the Add Comment link. The following screen will be displayed:

Give the following inputs:

Comment Body—This is a test.

Submission Date—(leave the default date)

Now click on Create. Then, if you get the following screen you can rest assured that everything is working as planned.

Now click on the Show link again and select the View Comments link. If you get the following screen, then the functionality is working:

These tests tell us that the changes we did are working fine. And that completes our ‘endeavour’ on gathering the user comments.

Summary

We have completed Login management and Comment management. Login management was one of the loose ends from the User management part. Now we can concentrate on enhancing the developed modules. These enhancements include custom template creation, the logout option, database-independent table creation, and other features that need to be completed before moving on to developing the new functionalities. These enhancements will implemented in the next chapter. So keep reading!

Filed Under: Ruby Tagged With: Rails

Follow Us

  • Facebook
  • Pinterest

As a participant in the Amazon Services LLC Associates Program, this site may earn from qualifying purchases. We may also earn commissions on purchases from other retail websites.

JavaBeat

FEATURED TUTORIALS

Answered: Using Java to Convert Int to String

What is new in Java 6.0 Collections API?

The Java 6.0 Compiler API

Copyright © by JavaBeat · All rights reserved