If you've been following my emails on the Ramaze and Sequel lists, you've probably realized that I've been working on a Ramaze application for a library. This project came about when our company decided that we needed a library and we started looking for software. There wasn't anything that really met our needs so I decided to take a stab at writing it myself. It's a pretty good sized application now and still not "finished", but I thought in these next few posts, I'd lay out some of the things that I've learned while working on it in smaller pieces than the whole application.
The first thing I'd like to show is the search function. This is much more simple than I thought it would be. What we'll do is create some books with titles and descriptions. We'll add a page that allows the user to input some search terms and then another page that displays these results. The results will be based on whether any of the search terms are found in either the title or the description.
First let's create the database with our migration. Here's the code for this. Note that we're adding the data as well as creating the database so we don't have to create the data using another page.
Class.new(Sequel::Migration) do
def up
create_table(:books) do
primary_key :id
String :title
String :description
end
from(:books).insert(:title => 'Programming Ruby', :description => 'A great Ruby book')
from(:books).insert(:title => 'Agile Web Developement with Rails', :description => 'A book about Ruby on Rails')
from(:books).insert(:title => 'Cryptonomicon', :description => 'A book about a Unix sys admin')
from(:books).insert(:title => 'The C Programming Language', :description => 'A book about programming in C')
end
def down
drop_table(:books)
end
end
We've seen this before. We create the table called "books" (note the plural) with text columns for the title and the description. Our down method, just drops the table. In the middle we add four books with their titles and descriptions.
Next let's look at our models. Jeremy Evans, the Sequel maintainer/guru, recommends having a single models.rb in our models directory to make it easier to use irb to test things. I've taken to doing this and it works quite well. Here our model is very simple (empty). We name the model Book (singular of the table books) and derive it from Sequel::Model. This will give us access to books table. Here's the actual code:
class Book < Sequel::Model
end
The controller, controllers/main_controller.rb is also quite simple. It has an index method that only sets the title and a second method search_results that "calculate" the results and save them to the @search_response variable for use in the view/search.xhtml view. We also go ahead and save the @search_string so we can display that we can display that also. The last line, that calculates the search_response, probably needs a bit of explanation.
We're going use the "grep" method on the Book dataset. We will pass an array with :title and :description to let the method know which columns of Book we're interested in. The next piece, we take the search_string and split it into an array. We then "map" the array generating something that will look like:
%ruby % %rails %
for a search string of "ruby rails". You can read about the grep function in a Sequel dataset
here.
Here's the controller code:
class MainController < Ramaze::Controller
map '/'
layout :page
def index
@title = "Search Example"
end
def search_results
@title = "Search Example - Results"
@search_string = request[:search]
@search_response = Book.grep([:title, :description], @search_string.split.map{|x| "%#{x} %"}).all
end
end
The search.xhtml contains the form to type in the search term(s) and submit it. On the form we use the action attribute to send the results to the search_results method in the controller. We use a "get" method so the results are passed on the URL (allowing bookmarking as Gavin pointed out when I asked how to do this incorrectly in a Ramaze thread. I had he and Clive steer me in the right direction though). The URL will look something like http://localhost:7000/search_results?search=ruby+rails when you're searching type "ruby rails" in the text box. Here's the view/index.xhtml:
<form id="search" action="search_results" method="get">
<fieldset>
<legend> Search </legend>
<div>
<label for="search">Search:</label>
<input id="search" name="search" type="text" />
<br/>
<input type="submit" value="Search" />
</div>
</fieldset>
</form>
The page to display the search response, view/search_results.xhtml, is also pretty simple. It takes the results saved in @search_response by the search_results method in the controller, loops through them and displays each of the books and their descriptions. Here's the code:
#{flashbox}
<br/>
<?r if @search_response.each && @search_response.size > 0 ?>
Results found for "#{@search_string}." <br/><br/>
<?r @search_response.each do | book | ?>
Title: #{book.title}
<br/>
Description: #{book.description}
<br/><br/>
<?r end ?>
<?r else ?>
No results found for "#{@search_string}".
<?r end ?>
<br/>
Finally, here's the layout (layout/page.xhtml):
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<link rel="stylesheet" type="text/css" href="/page.css"/>
<title>#@title</title>
</head>
<body>
<div id="header">
<h1>Search Example</h1>
</div>
<div id="content">
#@content
</div>
<div id="footer" style="text-align: center;">
<h5> Powered by Ramaze </h5>
</div>
</body>
</html>
and the CSS file (not formatted):
/* Header CSS */
#header {
background:#9DA9EE;
color: white;
margin-bottom: 0;
padding: 0.25em;
}
/* Footer CSS */
#footer {
background:#9DA9EE;
color: black;
}
All told, pretty simple and easy after someone points you in the right direction anyway.
As always, let me know if you have questions in the comments and I'll do my best to answer them.