Saturday, February 28, 2009

Sequel Models

In our last post here, we showed how we could use Sequel to create, access, and update a database with Ruby. In this post, we'll use Sequel models to do much the same thing. The nice thing about this is that we will be able to access our database using automatically generated ruby classes created from the database tables themselves. The code itself is very straightforward and I've commented it fairly completely. Here it is ...


require 'rubygems'
require 'sequel'

# Create an in-memory database
DB = Sequel.sqlite

# Create a new Country table with columns of
# id, name, and population.
DB.create_table :countries do
primary_key :id
column :name, :text, :unique=>true
column :population, :integer
end

# Create a Country model.
class Country < Sequel::Model; end

# Add some countries using our model. Note the id tag
# should get filed in automatically.
Country.create(:name => 'U.S.A.', :population => 250000000)
Country.create(:name => 'Mexico', :population => 251000000)
Country.create(:name => 'Canada', :population => 252000000)
Country.create(:name => 'France', :population => 300000000)

# U.S.A note the values are accessed as a hash of the column names.
usa = Country[:name => 'U.S.A.']
puts "USA: #{usa[:name]} #{usa[:population]}"

# or as attributes (should print the same as above).
puts "USA population: #{usa.name} #{usa.population}"

# Populations under (or equal to) 251 million
puts "Populations <= 251000000"
Country.filter(:population <= 251000000).each{|country| puts "Country #{country[:name]} #{country[:population]}" }

# Change the USA population (should print 100 more than the above).
usa.population = 250000100
puts "USA population: #{usa.name} #{usa.population}"

# Populations under (or equal to) 251 million. Note that U.S
# is not changed.
puts "Populations <= 251000000"
Country.filter(:population <= 251000000).each{|country| puts "Country #{country[:name]} #{country[:population]}" }

usa.update(:population => 250000200)
puts "USA: #{usa.name} #{usa.population}"

# Populations under (or equal to) 251 million
puts "Populations <= 251000000"
Country.filter(:population <= 251000000).each{|country| puts "Country #{country[:name]} #{country[:population]}" }

# Get the country with the ID of 3.
country = Country[3]
puts "Country 3 #{country[:name]} #{country[:population]}"

# Populations under (or equal to) 251 million
puts "Populations <= 251000000"
Country.filter(:population <= 251000000).each{|country| puts "Country #{country[:name]} #{country[:population]}" }



The one thing in all of this to note is that simply changing the model as we do in line 37 and print in 38 does not change the database backing as we can see by running our filter a few lines later. We need to use the update command as we do in line 45 before this will work. You can check out all the model based API here. Next we'll use a database model in Ramaze.

As always, let me know of any questions that you have.

Tuesday, February 24, 2009

Starting Sequel

We're going to take a bit of a detour here from our Ramaze studies to learn about Sequel. We'll use Sequel in future Ramaze posts for hooking up to a database, but as normal, I want to strip this down to the bare bones. First, you'll need to install two things, the database sqlite and the gem for Sequel. You can get sqlite here and then install Sequel by doing a gem install sequel. Once you've done that, you should be able to run the following code (an update of the example code on the Sequel site):

require 'rubygems'
require 'sequel'

# Open the sqlite database countries.db if it exists
# or create it if it doesn't
DB = Sequel.sqlite("countries.db")

# Check if the countries table exists in the database
# and create it if it doens't. Add a primary key "id"
# and two other columns name and population. The name
# is a text field and must be unique while the population
# is an integer field.
if !DB.table_exists? :countries
DB.create_table :countries do # Create a new table
primary_key :id
column :name, :text, :unique=>true
column :population, :integer
end

else
puts "Countries database already exists."
end

# Create a new dataset with the countries and populations that are
# currently in the database.
countries = DB[:countries]

# Populate the table with some countries and fake populations initially. If we
# can't add them (we get an error), assume that they've already been added.
begin
countries << {:name => 'U.S.A', :population => 250000000}
countries << {:name => 'Mexico', :population => 251000000}
countries << {:name => 'Canada', :population => 252000000}
rescue
puts "Countries already added to database."
end

# Print out the initial countries/population in
# reverse order (i.e. largest to smallest).
puts "Initial countries / population"
countries.reverse_order(:population).print

# Input additional country population pairs separated by white space. You can
# type "exit" or "quit" to stop inputting the pairs. There is no error checking
# here so don't make any.
puts "Put in country / population pairs separated by white space. Type exit or quit to stop."
print "country population >> "
while ((line = gets.chomp!) !~ /(exit|quit)/i) do
begin
country, population = line.split
countries << {:name => country, :population => population}
print "country population >> "
rescue
# There was an error here. This will happen if there's a
# duplicate country for one thing.
puts "Could not add country/population"
end
end

# Print out the number of countries currently
# in the database.
puts "Country count: #{countries.count}"

# Print out the records in descending order by population.
countries.reverse_order(:population).print

# Print out the average population.
puts "The average population is: #{countries.avg(:population)}"



I've tried to document the code so that it's easily understandable. We first open (or create) the countries.db database and add a table with three columns, id, name, and population. Then we open a dataset where we can add our country/population data and then add three countries and populations (I have no idea how close or far these numbers are). If we get an error, we simply print the fact that we've already populated the table (the most likely error). After that, we print out everything in the database in order of largest to smallest population. We then allow the user to add their own countries/populations (as many as they like) followed by an "exit" or "quit". Finally we print out the number of countries and their populations largest to smallest again.

In the next post, we'll use the Sequel model to map ruby code to database tables.

Let me know of any questions.

Monday, February 16, 2009

Logging in Ramaze

I probably should have tossed this one into yesterday's post on models, but didn't think about it at the time. With Ramaze you can print out debug messages to the console in the same way that Ramaze itself does. Here's the authorize() with some additional lines of code of the form Ramaze::Log.debug "Entering authorize()".


# This class is a very simple authorize class that
# contains a single class method that checks a
# login/password pair and if they are hello/world
# returns authorized as true and otherwise false.
#
class Authorize
def self.authorize(login, password)
Ramaze::Log.debug "Entering authorize()"
if ((login == 'hello') && (password == 'world'))
Ramaze::Log.debug "authorize(): password validated"
authorized = true
else
Ramaze::Log.debug "authorize(): password not validated"
authorized = false
end
end
end



There are also a couple of other forms of this type of message for warning messages Ramaze::Log.warn "Warning message" and Ramaze::Log.info "Informational message". The code above will print out a debug message when we enter the authorize() method and then another when we decide to authorize or not.

You can turn off various messages (info, warning, and debug) using the following, Ramaze::Log.ignored_tags = [:info, :debug]. This particular piece will turn off the info and debug messages. You can add :warn if you want everything turned off or remove :info or :debug.

Sunday, February 15, 2009

Using a Simple Model

Models are the "M" in the MVC pattern along with "View" and "Controller". We've used both views and controllers so far, but we haven't seen any models yet. For this post, we'll take our last post, Accessing Data From Forms and add a very simple model for "authorizing" the login ID and password. Here's our main ruby code usingmodels.rb:


require 'rubygems'
require 'ramaze'

require 'models/authorize'

# This example is based on the previous "Accessing Data From Forms" example.
# It has two methods (index and logged_in). The index method will take data
# from a form and call the authorize method. If the authorize method returns
# true we will set the session and flash variables, and then finally redirect
# to the logged_in screen where it will display the flash message and the
# login/password values. If it returns false we will set the flash variable
# and stay on the index page.
class MainController < Ramaze::Controller
# Use page.xhtml in the view directory for layout
layout :page

# You can access it now with http://localhost:7000/
# This should display a form with a login and a password as
# well as a "Login" button in your browser.
# For anything that is entered, we will save the values
# in the session, set the session variable, set the flash
# variable, and redirect to the logged_in screen.
def index

# Make sure we're getting here from a post request.
if request.post?
# Use the name= portion of the input form to grab the data
# from the request variable and save it in the session
# hash table.
session[:loginID] = request[:loginID]
session[:pw] = request[:pw2]

# Check the login and password.
if Authorize.authorize(session[:loginID], session[:pw])
# Set the flash message which will only be available in the next
# screen. In this case that will be the logged_in screen.
flash[:message] = "Successfully logged in!!!"

# Redirect to the logged_in screen.
redirect Rs(:logged_in)
else
# The login could not be authorized. Set the flash message
# and stay on this page (index/login).
flash[:message] = "Incorrect password, please try again!!!"
end
end
end

# Show the message in the flash variable and print out the
# login ID and password. Typically, you wouldn't want to do
# this in a real environment.
def logged_in
"#{flash[:message]} #{session[:loginID]} : #{session[:pw]}"
end
end

Ramaze.start


As you can see it is pretty much the same as the previous example except we make a call to the Authorize.authorize() method which is brought in with the require 'models/authorize' in line 4. It this returns true, we move on (are redirected) to our logged_in action/view as before and display the success message and the login/password pair (still not a good idea). If the Authorize.authorize() returns false, we set a flash message that says it was an incorrect password and we don't redirect anywhere but stay on the same page (no redirect).

Here's the Authorize.authorize()


# This class is a very simple authorize class that
# contains a single class method that checks a
# login/password pair and if they are hello/world
# returns authorized as true and otherwise false.
#
class Authorize
def self.authorize(login, password)
if ((login == 'hello') && (password == 'world'))
authorized = true
else
authorized = false
end
end
end


If you haven't done much ruby programming, you may be wondering about a couple of things. First, there's the def self.authorize(login, password). This is the way you declare a class as opposed to an instance method in Ruby. In this case, we want to be independent of any particular instance of Authorize, so we use this form. The other interesting thing here is that there's no return statement. Ruby (like Perl) returns the last thing that's "mentioned" which in this case is the authorized variable. This code should be placed in a models directory underneath your main directory.

Here's our new view/index.xhtml:


#{flashbox}
<form id="login" method="post">
<div>
<!-- for= goes with id=, the name= is placed in the request variable. -->
<label for="nick">Login:</label>
<input id="nick" name="loginID" type="text"
/>
<br/>
<label for="pw1">Password:</label>
<input id="pw1" name="pw2" type="password"
/>
<br/>
<input type="submit" value="Login" />
</div>
<
/form>


This is exactly the same as in our previous example with the exception of the #{flashbox}. This will display our flash message in the view. Remember that the flash message is only available on the next redirect so this will only appear when we go to the page and it has just been set as in an authorization failure. The first time we hit the page, nothing should be displayed. Try it with both the single correct login/password and some other pairs and watch what happens.

Our page.xhtml hasn't changed so I won't repost it here, you can grab it out of one of the previous posts.

As always, let me know in the comments of any questions.

Thursday, February 12, 2009

Accessing Data From Forms

SohDubom was asking in a comment from an earlier post about accessing data from forms. I told him I'd do a post on that and so here it is. We'll, as is usual, start from an existing post, in this case the Using Different .xhtml Files for Different Pages. Let's try to simulate a login page with a couple of different fields, the normal login and password. We'll go ahead and grab the values, save them, and then redirect to a new page. On the new page, we'll print a message and then print out the login and password to show they're available. Best practices however do not allow the plain text printing of a password or for that matter skipping any kind of validation. This is just for the sake of an example. Here's the ruby code:


require 'rubygems'
require 'ramaze'

# This example is based on the previous "Using Different .xhtml Files for
# Different Pages" example. It has two methods (index and
# logged_in). The index method will take data from a form, set the session
# and flash variables, and then finally redirect to the logged_in screen where
# it will display the flash message and the login/password values.
class MainController < Ramaze::Controller
# Use page.xhtml in the view directory for layout
layout :page

# You can access it now with http://localhost:7000/
# This should display a form with a login and a password as
# well as a "Login" button in your browser.
# For anything that is entered, we will save the values
# in the session, set the session variable, set the flash
# variable, and redirect to the logged_in screen.
def index

# Make sure we're getting here from a post request.
if request.post?
# Use the name= portion of the input form to grab the data
# from the request variable and save it in the session
# hash table.
session[:loginID] = request[:loginID]
session[:pw] = request[:pw2]

# Set the flash message which will only be available in the next
# screen. In this case that will be the logged_in screen.
flash[:message] = "Successfully logged in!!!"

# Redirect to the logged_in screen.
redirect Rs(:logged_in)
end
end

# Show the message in the flash variable and print out the
# login ID and password. Typically, you wouldn't want to do
# this in a real environment.
def logged_in
"#{flash[:message]} #{session[:loginID]} : #{session[:pw]}"
end
end

Ramaze.start



Our page.xhtml hasn't changed at all:


<html>
<head> <title>Using Forms</title> </head>
<body>
#@content
<h5> Powered by Ramaze </h5>
</body>
</html>



and finally here's the index.xhtml which contains the form and input boxes:

<form id="login" method="post">
<div>
<!-- for= goes with id=, the name= is placed in the request variable. -->
<label for="nick">Login:</label>
<input id="nick" name="loginID" type="text" />
<br/>
<label for="pw1">Password:</label>
<input id="pw1" name="pw2" type="password" />
<br/>
<input type="submit" value="Login" />
</div>
</form>




So, first the user puts in a login and password and then hits the login button. The index method takes over from that point and grabs the login and password from the request hash table which uses the name= value from the input field in index.xhtml as the key. It then stores the values in the session variable which is availble everywhere. It also sets the flash variable which will be available in the next page only. Finally, it redirects to the logged_in page which returns the flash and the login and password to be rendered as the @content. In a real system, you would validate the login and password probably using a model and there's a fair chance (as noted above) that you wouldn't display the password in the logged_in screen.

As always, let me know if you have questions or comments.

Ramaze.net Note

We got onto the front page of Ramaze's main page. Pretty cool!

Sunday, February 8, 2009

Using Different .xhtml Files for Different Pages

In this post, I'll show how each method in a controller can use a different .xhtml inside of the master page.xhtml. We'll start with the code from our last post, Passing Data From Views To Controllers. Here's the ruby code usedifferentxhtml.rb:

require 'rubygems'
require 'ramaze'

# This example is based on the previous "passing data to views" example.
# It has the same three methods (index, page1, and page2) but each of
# these only sets the title. Everything else comes from either the
# page.xhtml file or the individual index.xhtml, page1.xhtml, or
# page2.xhtml.
class MainController < Ramaze::Controller

# Use page.xhtml in the view directory for layout
layout :page

# You can access it now with http://localhost:7000/
# All we're going to do is set the title in here and
# everything else will come from our index.xhtml file.
def index
@title = "Index Page"
end

# You can access it now with http://localhost:7000/
# All we're going to do is set the title in here and
# everything else will come from our page1.xhtml file.
def page1
@title = "Page 1"
end

# You can access it now with http://localhost:7000/
# All we're going to do is set the title in here and
# everything else will come from our page2.xhtml file.
def page2
@title = "Page 2"
end
end

Ramaze.start


Here's our page.xhtml which is the same as in our last post:

<html>
<head> <title>#@title</title> </head>
<body>
<h1>#@title</h1>
#@content
<h5> Powered by Ramaze </h5>
</body>
</html>


Here's our index.xhtml:

Hello, World! <br/>
<a href="#{Rs(:page1)}">Page 1</a>
<a href="#{Rs(:page2)}">Page 2</a>


Here's our page1.xhtml

Page 1 <br/>
<a href="#{Rs(:index)}">Hello world</a>
<a href="#{Rs(:page2)}">Page 2</a>


and finally, out page2.xhtml

Page 2 <br/>
<a href="#{Rs(:index)}">Hello world</a>
<a href="#{Rs(:page1)}">Page 1</a>


As you can see, all we've really done is move some code from our controller to the view. Ramaze uses the templating system Ezamar by default and that is what will be used here to put everything together.

One additional thing to note, if you have a second controller and it's mapped to say map 'xyz', then you will have to either put the templates in the view/xyz or name them xyz__index.xhtml, xyz__page1.xhtml, etc.

Wednesday, February 4, 2009

Passing Data To Views From Controllers

Often we're going to need to get information from the Controller to the Views and typically this data will come from Models. Here we're just going to set something in the Controller and then show how to use it in a View. We'll start with our code for Using page.xhtml. In each of the methods, we set the @title value. This value, accessed using #@title can then be used in the View, in this case page.xhtml to display. We use it to set the title and as a header. Here's the ruby code

require 'rubygems'
require 'ramaze'

# This example is based on the previous "multiple pages" example.
# It has the same three methods (index, page1, and page2) but adds
# links on each page to the other two pages. For linking it uses
# the Ramaze Rs helper for linking within the same controller. The
# other item that is different is that instead of the content
# being in double quotes, we use the %{} form which is the same
# as the double quotes or as %Q{}. They just make it easier to
# include quotes in the string.
#
# Additionally, this adds the use of a layout using
# page.xhtml from the view directory. The layout uses the @title
# set in each of the methods (index, page1, and page2) to
# put a title on the page and to set it in the <h1> tags.
class MainController < Ramaze::Controller

# Use page.xhtml in the view directory for layout
layout :page

# You can access it now with http://localhost:7000/
# This should output
# Hello, World!
# twice in your browser and provide links to the other two pages.
# The first time it uses the @title in the <h1> tags and the second
# comes from the content provided by the method.
def index
@title = "Index Page"
%{
Hello, World! <br/>
<a href="#{Rs(:page1)}">Page 1</a>
<a href="#{Rs(:page2)}">Page 2</a>
}
end

# You can access it now with http://localhost:7000/page1
# This should output
# "Page 1!"
# twice in your browser and provide links to the other two pages.
# The first time it uses the @title in the <h1> tags and the second
# comes from the content provided by the method.
def page1
@title = "Page 1"
%{
Page 1 <br/>
<a href="#{Rs(:index)}">Hello world</a>
<a href="#{Rs(:page2)}">Page 2</a>
}
end

# You can access it now with http://localhost:7000/page2
# This should output
# "Page 2!"
# twice in your browser and provide links to the other two pages.
# The first time it uses the @title in the <h1> tags and the second
# comes from the content provided by the method.
def page2
@title = "Page 2"
%{
Page 2 <br/>
<a href="#{Rs(:index)}">Hello world</a>
<a href="#{Rs(:page1)}">Page 1</a>
}
end
end

Ramaze.start


and here's page.xhtml

<html>
<head> <title>#@title</title> </head>
<body>
<h1>#@title</h1>
#@content
<h5> Powered by Ramaze </h5>
<
/body>
</html>


As before, put the page.xhtml into a view directory under where ever you put the ruby code passdatatoviews.rb. Then run using ruby passdatatoviews.rb

As always, feel free to leave questions in the comments section.

Tuesday, February 3, 2009

blogfmt.rb

Here's the code for formatting using my new script.

#!/usr/bin/ruby
# == Synopsis
# Modifies a file to put it into a Maraku format for
# syntax highlighting
#
# == Usage
# ruby blogfmt.rb file1 file2 ... filen
#
# == Author
# Scott LaBounty
#
# == Copyright
# Copyright(c) 2009 Scott LaBounty
#
#

require 'getoptlong'
require 'rdoc/usage'
require 'maruku'

##
# Main Program
#

if __FILE__ == $0

# Set up the command line options
opts = GetoptLong.new(
["--verbose", "-v", GetoptLong::NO_ARGUMENT],
["--help", "-h", GetoptLong::NO_ARGUMENT]
)

# Set the default values for the options
verbose = false

# Parse the command line options. If we find one we don't recognize
# an exception will be thrown and we'll rescue with a RDoc::usage
begin
opts.each do | opt, arg|
case opt
when "--help"
RDoc::usage
when "--verbose"
verbose = true
end
end
rescue
RDoc::usage
end


s = ""
ARGV.each do | fileName |
puts "fileName = #{fileName}" if verbose

# Make sure the file exists
if (File.exist?(fileName)) then

# Open the file
File.open(fileName) do | file |

# For each line in the file
file.each_line do | line |
s += " #{line}"
end

end
else
puts "Could not find #{fileName}"
end
end
# Add in the Maraku format for ruby syntax
s += '{:lang=ruby html_use_syntax=true}'

# Convert the file we read in to HTML.
h = Maruku.new(s).to_html_document

# Remove everything up to and including the <body> tag.
h.sub!(/^.*<body>/m, "")

# Remove the closing <body> tag and everything past.
h.sub!(/<\/body>.*$/m, "")

# Print the string which should be everything between the <pre> ... </pre>
puts h
end


Blogger magically started highlighting everything this morning, so I'm going to assume that it's good, although it's only been tested on our hello.rb example and itself. If anyone uses this, let me know how it works for you.

The code itself is based on a simplistic cat program that I wrote a while back. I tend to use it regularly as a base for programs that loop through files, do something to each line, and then print the (possibly) modified line back out.

Monday, February 2, 2009

Sytax Highlighting Testing

Well, I didn't have this quite as well understood as I thought (as you may have gathered from posts going up and down). I actually wrote a script to handle the syntax highlighting mentioned earlier, but when I posted it, the highlighting didn't show up. It's a bit painful to test, because the highlighting doesn't show up until posting (preview doesn't work). So, to see anything I have to put it up and if it doesn't work, pull it back down. There may be a few of these over the next few days while I try to track this down.