Tech Tidbits - Ruby, Ruby On Rails, Merb, .Net, Javascript, jQuery, Ajax, CSS...and other random bits and pieces.

Sunday, December 30, 2007

How to pass variables thru a DataGrid hyperlink column


<asp:HyperLink NavigateUrl='<%# "SomePage.aspx?someparam=" + DataBinder.Eval(Container, "DataItem.PropertyID") %>' runat="server" />

Wednesday, December 19, 2007

Ruby Blog

I've decided to create a Ruby only blog, though I'll keep cross posting here for now...who knows, I may change my mind and just keep the one blog here...

spariamsgems

Viewing .CHM files on Mac OS X - Chmox

I tried several Mac options and had no luck (xchm froze, libchmxx has a utility called chmdissect - it worked, but no images)...finally, I struck gold with Chmox.

1) Install CHMLIB - http://www.jedrea.com/chmlib/

% ./configure
% make
% sudo make install

2) Install Chmox - http://chmox.sourceforge.net/

The binary is a standard dmg file...click on the dmg file and move the application over to your application directory.

Monday, December 10, 2007

Sending E-mail with Ruby Net::SMTP

All you need to know is here:
http://www.ruby-doc.org/stdlib/libdoc/net/smtp/rdoc/classes/Net/SMTP.html

def send_email(from, from_alias, to, to_alias, subject, message)

msg = <<EOF
From: #{from_alias} <#{from}>
To: #{to_alias} <#{to}>
Subject: #{subject}

#{message}
EOF

Net::SMTP.start('localhost') do |smtp|
smtp.send_message(msg, from, to)
end

end

Or if your mail server is on another machine:

Net::SMTP.start(mail_server, port, helo_domain) do |smtp|
smtp.send_message msg, from, to
end

For example:

smtp.send_message('your.smtp.server', 25, 'mail.from.domain')

Wednesday, November 21, 2007

ASP.NET Calendar Control - Misc

How to: Display Selected Dates from a Database in the Calendar Control

Hide days that aren't in current month:

protected void Calendar1_DayRender(object sender, DayRenderEventArgs e)
{
if (e.Day.Date.Month != Calendar1.VisibleDate.Month)
e.Cell.Text = "";
}


Disable link for particular day (the 23rd, in this case):

protected void cal_DayRender(object source, DayRenderEventArgs e)
{
if (e.Day.Date.Day == 23)
{
e.Cell.Controls.Clear();
e.Cell.Text = e.Day.DayNumberText;
e.Cell.BackColor = System.Drawing.Color.Gainsboro;
}
}

Tuesday, November 20, 2007

ASP.NET HyperLink Control: mailto link


<asp:HyperLink ID="EmailLink" text='<%# Eval("UserEmail") %>' NavigateUrl='<%# Eval("UserEmail","mailto:{0}")%>' runat="server" target="_blank" />


With a "Subject"


<asp:HyperLink ID="EmailLink" text='<%# Eval("UserEmail") %>' NavigateUrl='<%# Eval("UserEmail","mailto:{0}?Subject=YourQuestion")%>' runat="server" target="_blank" />

Saturday, November 17, 2007

Identify Button Clicked in GridView

I had a GridView with a ButtonField used to Select a record to display a User's details in a DetailsView. I wanted a second button to display different form for changing a Users's password, since I wanted that to be a separate option.

GridView (usersGrid):

<asp:ButtonField CommandName="Select" Text="Select" />
<asp:TemplateField>
<ItemTemplate>
<asp:LinkButton runat="server" ID="changePasswordButton" Text="Change Password" OnClick="usersGrid_SelectRow" />
</ItemTemplate>
</asp:TemplateFild>


Code Behind:

protected void usersGrid_SelectRow(object sender, EventArgs e)
{
GridViewRow row = (GridViewRow)((LinkButton)sender).Parent.Parent;
usersGrid.SelectedIndex = row.RowIndex;

MyDatabase.DBUtil = new MyDatabase.DBUtil();
changePasswordDetails.DataSource = DB.GetUser(Int16.Parse(usersGrid.SelectedValue.ToString());
changePasswordDetails.ChangeMode(DetailsViewMode.ReadOnly);
userDeatils.Visible = false;
changePasswordDetails.Visible = true;
changePasswordDetails.DataKeyNames = new string[] { "UserID" };
changePasswordDetails.DataBind();
}

Friday, November 9, 2007

.NET Smart Delete (Details View Client-side delete confirmation)

I was working on a database-driven web site and I needed a way to make sure that the admin couldn't delete all the admin user accounts (that was simple enough), but I also wanted a javascript confirmation popup when an admin did delete accounts.

I found exactly what I was looking for on Dino Esposito's WebLog on his post ASP.NET Today: Smart Delete:


DetailsView and FormView controls support delete operations and delegate the execution to the underlying data source control. If the data source control is configured to execute the delete operation, all works fine, otherwise an exception is thrown.

The DetailsView generates command buttons automatically and doesn't expose them directly to page code. How can you add a bit of Javascript code to ask for confirmation? Here's the code.


protected void DetailsView1_ItemCreated(object sender, EventArgs e)
{
// Test FooterRow to make sure all rows have been created
if (DetailsView1.FooterRow != null)
{
// The command bar is the last element in the Rows collection
int commandRowIndex = DetailsView1.Rows.Count-1;
DetailsViewRow commandRow = DetailsView1.Rows[commandRowIndex];

// Look for the DELETE button
DataControlFieldCell cell = (DataControlFieldCell) commandRow.Controls[0];
foreach(Control ctl in cell.Controls)
{
LinkButton link = ctl as LinkButton;
if (link != null)
{
if (link.CommandName == "Delete")
{
link.ToolTip = "Click here to delete";
link.OnClientClick = "return confirm('Do you really want to delete this record?');";
}
}
}
}


The ItemCreated event doesn't provide any information about the row being created. However, it can be figured out that the footer row is always created and is always the last row to be created. If the FooterRow object is not null, you can conclude that all rows have been created, including the command bar. The command bar is the first row after the data rows and is stored in the Rows collection--it's the last element in the collection. The command bar is a table row (type is DetailsViewRow) and contains a cell (type is DataControlFieldCell). The cell contains as many link buttons (type is DataControlLinkButton) as there are commands. Delete, Edit, New, Update, Cancel are the command names used and useful to identify the right button.

The FormView is fully templated and lets you manually define appearance and behavior of command buttons. If you place a custom button, or want to use a custom command name for a standard button (Edit, New, Delete), here's how to detect the click on the button. You start by adding a handler for ItemCommand. The code below shows how to deal with a custom Delete button that checks if the bound data source control is enabled for deletion before proceeding.


protected void FormView1_ItemCommand(object sender, FormViewCommandEventArgs e)
{
if (e.CommandName == "SmartDelete")
{
IDataSource obj = (IDataSource) FindControl(FormView1.DataSourceID);
DataSourceView view = obj.GetView("DefaultView");
if (!view.CanDelete) {
Response.Write("Sorry, you can't delete");
return;
}
else
FormView1.DeleteItem();
}
}

Tuesday, November 6, 2007

Formatting Date Field in Grid View

Due to a bug fix in .NET 2.0, HtmlEncode must be set to False for date formatting to give expected results:


<asp:BoundField DataField="AgendaDate" HeaderText="Agenda Date" SortExpression="AgendaDate" DataFormatString="{0:yyyy-MM-dd}" HtmlEncode=False />


In a Details View using TemplateFields:


<ItemTemplate>
  <asp:Label ID="agendaDateLabel" runat="server" Text='<%# Eval("AgendaDate", "{0:yyyy-MM-dd}") %>'></asp:Label>
</ItemTemplate>

Saturday, November 3, 2007

SQL Server 2005 Check Constraint

I wanted to use something like the MySQL Enum constraint in SQL Server to limit the values allowed in a column...

I ended up using a separate table and a foreign key for this, but never the less, here's how to use the Check constraint, in my case, to limit RecordStatus to contain only 'Active' or 'Inactive':


CREATE TABLE NewsItems (
NewsItemID int IDENTITY NOT NULL PRIMARY KEY,
RecordStatus nvarchar(10) NOT NULL DEFAULT 'ACTIVE' CHECK (RecordStatus IN ('Active', 'Inactive'))
);

Friday, October 19, 2007

ASP .NET - C# Decimal Comparison

A little more shopping cart hacking. Here's how do to Decimal comparisons in C#:


public static decimal GetShippingCost()
{
decimal sum;
decimal shippingCost;
sum = GetSubtotal() - GetCouponDiscount();
if (sum < 0)
sum = 0;

if ( Decimal.Compare(sum, 26.95m) <= 0 )
shippingCost = 8.95m;
else if ( Decimal.Compare(sum, 35.95m) <= 0 )
shippingCost = 11.95m;
else if ( Decimal.Compare(sum, 45.95m) <= 0 )
shippingCost = 12.95m;
else if ( Decimal .Compare(sum, 55.95m) <= 0 )
shippingCost = 16.95m;
else if ( Decimal.Compare(sum, 74.95m) <= 0 )
shippingCost = 18.95m;
else if ( Decimal.Compare(sum, 99.95m) <= 0 )
shippingCost = 20.95m;
else if ( Decimal.Compare(sum, 119.95m) <= 0 )
shippingCost = 22.95m;
else if (Decimal.Compare(sum, 139.95m) <= 0 )
shippingCost = 24.95m;
else if (Decimal.Compare (sum, 159.95m) <= 0 )
shippingCost = 26.95m;
else if (Decimal.Compare(sum, 199.95m) <= 0 )
shippingCost = 29.95m;
else if ( Decimal.Compare(sum, 249.95m ) <= 0 )
shippingCost = 32.95m;
else if (Decimal.Compare(sum, 299.95m) <= 0 )
shippingCost = 34.95m;
else
shippingCost = 34.95m;

return shippingCost;
}

ASP .NET - Redirecting to HTTPS

On a project where I had to install a third party shopping cart, one issue that came up was to make sure all the pages ran under https (many hard coded links used "http").

I put the following code in the Global.asax file:


protected void Application_BeginRequest(Object sender, EventArgs e)
{
if (Request.Url.Port != 443)
{
Response.Redirect ("https://www.yourdomain.com" + Request.Url.AbsolutePath, true);
}
}


This uses


Request.IsSecureConnection


to determine if the call was made over SSL.

So


if (Request.IsSecureConnection)
{
Response.Write("HTTPS");
}
else
{
Response.Write("HTTP");
}



Another way to write this:


if (!Request.IsSecureConnection)
{
string serverName =HttpUtility.UrlEncode(Request.ServerVariables["SERVER_NAME"]);
string filePath = Request.FilePath;
Response.Redirect("https://" + serverName + filePath);
}

Thursday, October 18, 2007

Classic ASP: Sending e-mail (CDONTS -> CDOSYS)

One of today's jobs was fixing a mail script running on classic ASP that died after being moved to a new server. Attempting to send an email resulted in this error:


Microsoft VBScript runtime error '800a01ad'

ActiveX component can't create object: 'CDONTS.Newmail'

/aspmailer.asp, line 45


CDONTS has been deprecated and replaced with CDOSYS.

I updated aspmailer.asp (a somewhat redundantly named script) to use CDOSYS. After the update, I got this error:


The "SendUsing" configuration value is invalid.


So I still need a bit more code.

This site provided the needed fix:

http://classicasp.aspfaq.com/email/how-do-i-send-e-mail-with-cdo.html


<!--
METADATA
TYPE="typelib"
UUID="CD000000-8B95-11D1-82DB-00C04FB1625D"
NAME="CDO for Windows 2000 Library"
-->
<%
Set cdoConfig = CreateObject("CDO.Configuration")

With cdoConfig.Fields
.Item(cdoSendUsingMethod) = cdoSendUsingPort
.Item(cdoSMTPServer) = "<enter_mail.server_here>"
.Update
End With

Set cdoMessage = CreateObject("CDO.Message")

With cdoMessage
Set .Configuration = cdoConfig
.From = "from@me.com"
.To = "to@me.com"
.Subject = "Sample CDO Message"
.TextBody = "This is a test for CDO.message"
.Send
End With

Set cdoMessage = Nothing
Set cdoConfig = Nothing
%>


Replace <enter_mail.server_here> with the name or IP of your SMTP mail server ("localhost" may do the trick).

The other example provided is as follows:


<%
sch = "http://schemas.microsoft.com/cdo/configuration/"

Set cdoConfig = CreateObject("CDO.Configuration")

With cdoConfig.Fields
.Item(sch & "sendusing") = 2 ' cdoSendUsingPort
.Item(sch & "smtpserver") = "<enter_mail.server_here>"
.update
End With

Set cdoMessage = CreateObject("CDO.Message")

With cdoMessage
Set .Configuration = cdoConfig
.From = "from@me.com"
.To = "to@me.com"
.Subject = "Sample CDO Message"
.TextBody = "This is a test for CDO.message"
.Send
End With

Set cdoMessage = Nothing
Set cdoConfig = Nothing
%>

Wednesday, September 19, 2007

Ruby on Rails - Legacy MySQL Database

I'm rewriting a company admin tool written in mod_perl (Catalyst) and replacing it with a new one using Ruby on Rails. At the moment, I have to keep the legacy database/schema. Most of the old tables don't use auto-incrementing keys, which means I won't get the Rails Active::Record magic and I need to work around the keys.

Here's an example table:

mysql> describe clients;
+--------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+-------------+------+-----+---------+-------+
| client_abbrv | varchar(5) | NO | PRI | | |
| client_name | varchar(50) | YES | | NULL | |
+--------------+-------------+------+-----+---------+-------+

To get this to work with Rails, I had to make a few small changes.

I made one small change in the view, which was setting the name of the primary key field. I changed "client_abbrv" to "id" in the form:

app/views/client/new.rhtml

<%= error_messages_for 'client' %>

<fieldset>
<legend>Add a Client
<% form_tag :action => "create" do %>
<p>
<label for="client_client_name">Client Name:
<%= text_field 'client', 'client_name' %>
</p>
<p>
<label for="client_id">Client Abbrv:
<%= text_field 'client', 'id' %>
</p>
<p>
<%= submit_tag 'Add' %>
</p>
<% end %>
</fieldset>


For the model, I had set the primary key with set_primary_key.

app/models/clients.rb

class Client < ActiveRecord::Base
set_primary_key "client_abbrv"
end


Luckily, the table was already pluralized. If it had been "client," then I would have had to add this line:


def self.table_name() "client" end


For the controller, I had to change the create method:

app/controllers/client_controller.rb

def create
@client = Client.new
@client.id = params[:client][:id]
@client.client_name = params[:client][:client_name]
if @client.save
flash[:notice] = "Client was successfully created."
redirect_to :action => 'list'
else
render :action => 'new'
end
end


That replaced the old create method where I had the normal:

@client = Client.new(params[:client])


When using set_primary_key, you insert data using "id" and typically select data using the actual field name, "client_abbrv" in this case.

Here's what the controller is doing for the create method:


dsparling-imbps-computer:~/my/rubydev/uclick-admin dsparlingimbp$ script/console
Loading development environment.
>> client = Client.new
=> #<Client:0x32b5e14 @new_record=true, @attributes={"client_name"=>nil}>
>> client.id = "abc"
=> "abc"
>> client.client_name = "ABC Company"
=> "ABC Company"
>> client.valid?
=> true
>> client.save
=> true

Saturday, September 1, 2007

Upgrading to PHP5 on Mac OS X 10.4

I've been running PHP5 for ages on my Linux box, but my new (for me) MacBook Pro had PHP4...um, time for an upgrade. Well, easy enough...

I downloaded the PHP5 tarball from http://www.entropy.ch/software/macosx/php/ and did the install.

Before the install, I commented out all the php4 lines in my apache config file(/etc/httpd/httpd.conf).

The entropy package installed under /usr/local/php5 and it added a new config file (/private/etc/httpd/users/+entropy-php.conf), which is included by the main apache httpd.conf file...restart apache, that's it!

Tuesday, August 28, 2007

RMagick on Mac OSX

When installing RMagick, I found that X11 hadn't been installed. It's on the developer CD, which I didn't have handy, so here's what I did.

1) read the notes here:

http://developer.apple.com/opensource/tools/runningx11.html

2) Downloaded and installed Xcode and X11SDK from Apple Developers Center.

3) I still needed X11.app, so I downloaded and installed that from:

http://xanana.ucsc.edu/xtal/x11.html

4) Downloaded and installed rm_install from:


http://rubyforge.org/projects/rmagick/


This will install ImageMagick and RMagick with all dependencies.

I originally had downloaded ImageMagick binary and got this error when trying to install RMagick gem:

"Can't install RMagick. Can't find libMagick or one of the dependent libraries. Check the config.log file for more detailed information."

Info on the error here:


http://rubyforge.org/forum/message.php?msg_id=4066

Sunday, August 5, 2007

Ruby on Rails - script/console

A nice and easy way to check you validation rules...

With a table called users with fields user_name, password, and email, we can add simple validation to the model User.


validates_uniqueness_of :user_name, :email
validates_length_of :user_name, :within => 4..20
validates_length_of :password, :within => 4..20
validates_length_of :email, :maximum => 50
validates_format_of :user_name,
:with => /^[A-Z0-9_]*$/i,
:message => "must contain only letters, " +
"numbers and underscores"
validates_format_of :email,
:with => /@/,
:message => "must be a valid email address"



Then using script/console, we can check our validation:

First, try to add a bad record:


$ script/console
Loading development environment.
>> user = User.new(:user_name => "pooh bear",
?> :password => "pb",
?> :email => "poohbear_at_100akerwood.com")
=> #"pooh bear", "password"=>"pb", "email"=>"poohbear_at_100akerwood.com"}>
>> user.save
=> false


Then you can inspect the errors individually with errors.on method:

>> user.errors.on(:user_name)
=> "must contain only letters, numbers and underscores"
>> user.errors.on(:password)
=> "is too short (minimum is 4 characters)"
>> user.errors.on(:email)
=> "must be a valid email address"


or all at once with errors.full_messages method:

>> user.errors.full_messages
=> ["Screen name must contain only letters, numbers and underscores", "Password is too short (minimum is 4 characters)", "Email must be a valid email address"]
>>

Monday, July 16, 2007

PHP PEAR: MDB2

The PEAR DB class has been deprecated and replaced with MDB2.

MDB2 doesn't come with the MySQL driver out to the box, so you need to install that separately:


$ pear install MDB2
$ pear install MDB2_Driver_mysql


Here's a simple select, using the default fetchmode of MDB2_FETCHMODE_ORDERED.


<?
require_once "MDB2.php";

$dsn = 'mysql://my_username:@my_password@127.0.0.1/my_db';

$options = array(
'debug' => 2,
'result_buffering' => false,
);

$mdb2 =& MDB2::factory($dsn, $options);
if (PEAR::isError($mdb2)) {
die($mdb2->getMessage());
}

// Proceed with a query...
$res =& $mdb2->query('SELECT * FROM my_table');

// Always check that result is not an error
if (PEAR::isError($res)) {
die($res->getMessage());
}

// Iterate through result set (assume default fetchmod is MDB2_FETCHMODE_ORDERED)
while (($row = $res->fetchRow())) {
echo $row[0] . " " . $row[1] . "<br/>\n";
}

?>


The three possible fetchmode values are:
1) MDB2_FETCHMODE_ORDERED (query results returned as array - column numbers are keys)
2) MDB2_FETCHMODE_ASSOC (query results returned as hash/associative array - column names are keys)
3) MDB2_FETCHMODE_OBJECT (query results returned as object - column names as properties)


To change the default fetchmode and use MDB2_FETCHMODE_ASSOC instead, use the setFetchMode method:


$mdb2->setFetchMode(MDB2_FETCHMODE_ASSOC);


and then iterate through the results:


while ($row = $res->fetchRow()) {
echo $row['column_name_a'] . ' ' . $row['column_name_b'] . "<br/>\n";
}


Or you can change it per call:


while ($row = $res->fetchRow(MDB2_FETCHMODE_ASSOC)) {
echo $row['client_abbrv'] . ' ' . $row['client_name'] . "<br/>\n";
}


Here's an example of the first method:


<?
require_once "MDB2.php";

$dsn = 'mysql://my_username:@my_password@127.0.0.1/my_db';

$options = array(
'debug' => 2,
'result_buffering' => false,
);

$mdb2 =& MDB2::factory($dsn, $options);
if (PEAR::isError($mdb2)) {
die($mdb2->getMessage());
}

// Set the fetchmode
$mdb2->setFetchMode(MDB2_FETCHMODE_ASSOC);

// Proceed with a query...
$res =& $mdb2->query('SELECT * FROM my_table');

// Always check that result is not an error
if (PEAR::isError($res)) {
die($res->getMessage());
}

// Iterate through result set
while ($row = $res->fetchRow()) {
echo $row['column_name_a'] . ' ' . $row['column_name_b'] . "<br/>\n";
}

?>


You can insert data with a prepared statement:


<?
require_once "MDB2.php";

$dsn = 'mysql://my_username:@my_password@127.0.0.1/my_db';

$options = array(
'debug' => 2,
'result_buffering' => false,
);

$mdb2 =& MDB2::factory($dsn, $options);
if (PEAR::isError($mdb2)) {
die($mdb2->getMessage());
}

// Insert
$statement = $mdb2->prepare('INSERT INTO clients VALUES (?, ?)');
$data = array($var1, $var2);
$statement->execute($data);
$statement->free();
?>


These are just the basics...read all the goodness here:

http://pear.php.net/manual/en/package.database.mdb2.php

Saturday, July 14, 2007

PHP PEAR: MDB2 Error: connect failed (localhost MySQL)

I ran into a persistent connect error when trying to connect to localhost MySQL database with MDB2.

Instead of:

dsn: mysql://my_username:my_password@localhost/my_db

use:

dsn: mysql://my_username:my_password@127.0.0.1/my_db

Thursday, July 5, 2007

Viewing Ruby Gem documentation

1) Command line

$ ri <gem_name>

2) Browser (http://localhost:8808)

$ gem_server

Emulate PHP5 str_split() in PHP4

/* emulate the php5 str_split function
* array str_split( string $string [, int $split_length])
* Converts a string to an array
* If the optional split_length param is specified, the returned array
* will be broken down into chunks with each being split_length in
* length, otherwise each chuck will be one character in length.
* False is returned if split_length is less than 1. If the split_length
* length exceeds the length of string, the entire string is returned
* as the first (and only) array element.
*/

If you want to use optional split_length:

function str_split($str, $l=1) {
$str_array = explode("\r\n", chunk_split($str,$l));
return $str_array;
}


Or optionally, if you just need the simple split (split_length=1):

function str_split($str) {
$str_array=array();
$len=strlen($str);
for($i=0; $i<$len; $i++) {
$str_array[]=$str{$i};
}
return $str_array;
}

Gem List

Here's a command to list all your installed Gems:

gem list | grep '^[a-zA-Z]'

Friday, June 8, 2007

APT Quick Reference

I found this handy apt-get Phil's APT Quick Reference...


Download and install package:

apt-get install package




Update apt's list of available packages:

apt-get update




Upgrade all installed packages with upgrades available:


apt-get upgrade




Upgrade to new distro, or in general upgrade anything available for update including core system packages:

apt-get dist-upgrade




Uninstall package:

apt-get remove package





Uninstall package, and its config files, and don't leave it in the database as 'uninstalled':

apt-get --purge remove package




Search for a package like pattern:

apt-cache search pattern





Get information on installed package:

dpkg -s package




Find which installed package file belongs to:

dpkg -S inetd.conf





List which files are in package:

apt-file list package




Find out what packages provide file:

apt-file search file





Run a command (such as ./configure or make) and have all necessary packages installed as needed:

auto-apt run command




Unless specified, the package in reference doesn't have to be already installed for these to work... but the database needs to be updated. You can update the appropriate database via:

apt-get update

or

auto-apt update

or

apt-file update


Ruby on Rails/Mongrel/Ubuntu

I've been using Ubuntu lately and decided to install Ruby on Rails and Mongrel.


$ sudo apt-get install ruby irb rdoc


Or download the source for the latest version (apt-get installs 1.8.4, but the latest source is 1.8.6):


$ tar zxvf ruby-1.8.6.tar.gz
$ cd ruby-1.8.6
$ ./configure
$ make
$ sudo make install


Once ruby is installed, I typically use Gems if I can.

Download the latest Gems at http://rubyforge.org/frs/?group_id=126 (0.9.4 in this case) and then:


$ tar xzvf rubygems-0.9.4.tgz
$ cd rubygems-0.9.4
$ sudo ruby setup.rb


Install Rails:

sudo gem install rails --include-dependencies


Install Mongrel and Mongrel cluster gem plugin:

sudo gem install mongrel --include-dependencies
sudo gem install mongrel_cluster


Install Apache 2.2

$ tar zxvf httpd-2.2.4.tar.gz
$ cd httpd-2.2.4
$ ./configure \
--prefix=/usr/local/apache \
--enable-rewrite \
--enable-headers \
--enable-ssl \
--enable-proxy \
--with-mpm=prefork
$ make
$ sudo make install


I got this error when running configure:

Configuring Apache Portable Runtime Utility library...

checking for APR-util... yes
configure: error: Cannot use an external APR-util with the bundled APR
make: *** [apache] Error 1


Adding this option fixed the problem:

--with-included-apr


Running Apache with a single Mongrel Instance

$ mongrel_rails start -p 8000 -e production -d


Add to apache conf (httpd.connf)

ServerName hostname.com
ServerAlias www.hostname.com

ProxyPass / http://www.hostname.com:8000/
ProxyPassReverse / http://www.hostname.com:8000/
ProxyPreserveHost on


Restart Apache and it will forward requests for www.hostname.com on port 80 to Mongrel server running on port 8000.

Thursday, June 7, 2007

Javascript/Ajax Libraries

I've been working on a few Ajax and DOM scripting projects at work using prototype.js and scriptaculous. Now we're looking at a "Web 2.0" upgrade for some of our sites, so I'm looking at several libraries:

Prototype JavaScript Framework
script.aculo.us
The Dojo Toolkit
Rico
Yahoo! UI Library (YUI)
mootools framework
MochiKit
JQuery

There are many, many more...
OSA Foundation - Survey of Ajax/JavaScript Libraries

Here's an accoridion demo I created with Rico:
My Rico Accordion Demo

Monday, June 4, 2007

DocBook

I've been tackling some documentation at work and decided to take a look at docbook.

There are a few good resources to start with:


With DocBook, you can use SGML (Dsssl styesheets) or XML (XSL stylesheets). I've chosen to work with XML. "DocBook XSL" provides the details of all the tools that are needed. At the moment I'm working on a Mac laptop, using the following:



I installed everything in my home directory and worked from there, but LSB proposal is:

  • DTD: /usr/share/sgml/docbook/xml-dtd-4.5

  • XSL: /usr/share/sgml/docbook/docbook-xsl-1.72.0/



Create a DocBook XML file (book.xml, for example)

<?xml version="1.0"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5/EN"
"/path/to/dtd/docbook-xml-4.5/docbookx.dtd">
<book>
<bookinfo>
<title>My Book Title</title>
</bookinfo>

<chapter>
<title>Chapter Name</title>

<para>Some text</para>
</chapter>
</book>


Procss to generate html:

$ xsltproc --output book.html /path/to/docbook-xsl-1.72.0/html/docbook.xsl book.xml


You can validate XML using xmllint (parser won't necessarily do it).

$ xmllint --noout book.xml

No output will be seen unless there is an error.

I haven't covered other formats (like converting to pdf using FOP), Java parsers, catalogs, makefiles, and Ant.

Speaking of Ant, here's a resource for DocBook and Ant:

DocBook and Ant.

I've already started looking at using Cocoon with DocBook, but that will be material for another post...

WebSphere Application Server V6.1 on Linux

I recently downloaded the full-featured trial download of WebSphere Application Server V6.1 to my old Fedora Core 5 laptop.

The installation was amazingly easy...


$ tar zxvf websphere.tar.gz
$ cd WAS/install
$ java -jar WAS/setup.jar
$ cd /opt/IBM/WebSphere/AppServer/bin
$ ./startServer server1


After starting WebSphere, you can check out the sample application:


http://localhost:9080/WSsamples

Friday, May 25, 2007

Audio file conversion

Here's a collection of small scripts for converting audio file formats using lame, mpg123, and mplayer.

wav2mp3.pl

#!/usr/bin/perl
use strict;
use warnings;

my $in = "/home/doug/music/somedir/";
my $out = $in;

opendir DH_IN, $in || die "Can't open $in: $!";
foreach my $name (sort readdir(DH_IN)) {
next unless $name =~ /\.wav$/;
print "NAME: $name\n";
my($base_name) = ($name =~ /^(.*)\.wav$/);
my $in_file = "${in}$base_name.wav";
my $out_file = "${out}$base_name.mp3";
`lame -b 192 $in_file $out_file`;
}
close DH_IN;


wav2mp3.sh

#!/bin/sh
bitrate="192"
for wavfile in *.wav
do
echo "input: $wavfile"
mp3file=`echo $wavfile | sed s/\\.wav/.mp3/`
echo "output: $mp3file"
lame -b $bitrate $wavfile $mp3file
done


mp32wav.sh

#!/bin/sh
for mp3file in *.mp3
do
echo "input: $mp3file"
wavfile=`echo $mp3file | sed s/\\.mp3/.wav/`
echo "output: $wavfile"
mpg123 -w $wavfile $mp3file
done


wma2mp3.sh

#!/bin/sh

if [ -z "$1" ]
then
echo "Usage: wma2mp3 "
exit 1
fi

name=`echo $1 | tr ' ' '_' | tr '[A-Z]' '[a-z]'`

name="`basename "$name" .wma`.mp3"

cp "$1" $name

mplayer -vo null -vc dummy -af resample=44100 -ao pcm:waveheader $name && lame -m s -h --vbr-new audiodump.wav -o $name


m4a2mp3.sh

#!/bin/sh

MPLAYER=mplayer
LAME=lame

for m4a in *.m4a
do

mp3=`echo "$m4a" | sed -e 's/m4a$/mp3/'`
wav="$m4a.wav"

if [ ! -e "$mp3" ]; then
[ -e "$wav" ] && rm "$wav"
mkfifo "$wav" || exit 255
mplayer "$m4a" -ao pcm:file="$wav" &>/dev/null &
lame "$wav" "$mp3"
[ -e "$wav" ] && rm "$wav"
fi
done

Finding installed Perl modules

I don't recall where I came across this little script, but it's quite handy for finding installed Perl modules along with their versions.


#!/usr/bin/perl
use strict;

use ExtUtils::Installed;
my $instmod = ExtUtils::Installed->new();
foreach my $module ($instmod->modules()) {
my $version = $instmod->version($module) || "???";
print "$module -- $version\n";
}

Wednesday, May 16, 2007

iBATIS - part 1

Very basic and simple iBATIS tutorial for a simple SELECT:

Download iBATIS from http://ibatis.apache.org/

Unpack the tar ball and install the jar file (it's now a single file - something like ibatis-2.3.0.677.jar) somewhere on your classpath...or if using with Tomcat or another servlet container, add it to "/WEB-INF/lib" in your for the webapp.

Database (MySQL)

mysql> describe book;
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| title | varchar(50) | NO | | | |
| authors | varchar(50) | YES | | NULL | |
| editor | varchar(50) | YES | | NULL | |
| chapters | text | YES | | NULL | |
| page_count | int(11) | YES | | NULL | |
| status | char(1) | NO | | P | |
| price | decimal(5,2) | YES | | NULL | |
+------------+--------------+------+-----+---------+----------------+

mysql> SELECT * FROM book;
+----+----------------------+---------+--------+----------+------------+--------+-------+
| id | title | authors | editor | chapters | page_count | status | price |
+----+----------------------+---------+--------+----------+------------+--------+-------+
| 1 | Learning Perl 3rd Ed | NULL | NULL | NULL | NULL | P | 29.99 |
| 2 | Learning Java 4th Ed | NULL | NULL | NULL | NULL | P | 29.99 |
+----+----------------------+---------+--------+----------+------------+--------+-------+


SqlMapConfig.xml

PUBLIC "-//iBATIS.com//DTD SQL Map Config 2.0//EN"
"http://www.ibatis.com/dtd/sql-map-config-2.dtd">
<sqlMapConfig>

<!-- These settings control SqlMap configuration details -->
<settings
useStatementNamespaces="true"
/>

<transactionManager type="JDBC">
<dataSource type="SIMPLE">
<property name="JDBC.Driver" value="com.mysql.jdbc.Driver"/>
<property name="JDBC.ConnectionURL" value="jdbc:mysql://localhost/bookdb"/>
<property name="JDBC.Username" value="yourusername"/>
<property name="JDBC.Password" value="yourpassword"/>
</dataSource>
</transactionManager>

<!-- Identify all SQL Map XML files to be loaded by this SQL map -->
<sqlMap resource="BookMap.xml" />

</sqlMapConfig>


BookMap.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap
PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN"
"http://www.ibatis.com/dtd/sql-map-2.dtd">
<sqlMap namespace="Book">

<resultMap id="get-Result" class="Book">
<result property="id" column="id"/>
<result property="title" column="title"/>
</resultMap>

<select id="getById" parameterClass="string" resultMap="get-Result">
SELECT * FROM book WHERE (id = #value#)
</select>

<select id="getAll" parameterClass="string" resultMap="get-Result">
SELECT * FROM book WHERE (status = 'P') ORDER BY title
</select>

</sqlMap>


Book.java

import com.ibatis.sqlmap.client.SqlMapClient;
import java.sql.Date;

public class Book {

public int getId()
{
return id;
}

public void setId(int id)
{
this.id = id;
}

public String getTitle()
{
return title;
}

public void setTitle(String title)
{
this.title = title;
}

public char getStatus()
{
return status;
}

public void setStatus(char status)
{
this.status = status;
}

private Integer id;
private String title;
private char status;
}


BookTest.java

import com.ibatis.sqlmap.client.*;
import com.ibatis.common.resources.Resources;

import java.io.Reader;
import java.util.List;
import java.util.Iterator;

public class BookTest {
public static void main(String arg[]) throws Exception {
String resource = "SqlMapConfig.xml";
Reader reader = Resources.getResourceAsReader(resource);
SqlMapClient sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);
List bookList = sqlMap.queryForList("Book.getAll",null);
System.out.println("Selected " + bookList.size() + " records.");

if(bookList.size() == 0){
System.out.println("OOOPS - bookList size is 0\n");
}
Iterator books = bookList.iterator();
StringBuffer bookBuffer = new StringBuffer();
while(books.hasNext()){
Book bookX = (Book)books.next();
bookBuffer.append("ID: " + bookX.getId() + " TITLE: " + bookX.getTitle() + "\n");
}
String bookOptions = bookBuffer.toString();
System.out.println(bookOptions);
}
}



$ java BookTest
Selected 2 records.
ID: 2 TITLE: Learning Java 4th Ed
ID: 1 TITLE: Learning Perl 3rd Ed

Sunday, May 13, 2007

Tomcat - How to run stand alone on port 80

With Apache, all ports below 1024 are privileged ports, so normally to run Tomcat as a stand alone server, you'd have to run as root, which is a Very Bad Idea. Using the jsvc interface, it is possible to bind Tomcat to a privileged port, such as port 80.

First, change server.xml to use port 80:


$ cd $CATALINA_HOME/conf
$ sudo vi server.xml


and make this change:


<!--<Connector port="8080" protocol="HTTP/1.1" -->
<Connector port="80" protocol="HTTP/1.1"


With Tomcat 5 & 6, there will be a jsvc.tar.gz file in the bin directory of your $CATALINA_HOME directory.


$ cd $CATALINA_HOME/bin
$ sudo tar zxvf jsvc.tar.gz
$ cd jsvc-src
$ sudo autoconf
$ sudo ./configure
$ sudo make
$ sudo cp jsvc ..


I had to add a few packages on Ubuntu to compile jsvc...

$ sudo apt-get install libc6-dev g++ gcc
$ sudo apt-get install make
$ sudo apt-get install autoconf


You can start Tomcat as "tomcat" user (use which ever existing non-privileged user you want, or create a new user):


$ cd $CATALINA_HOME
$ sudo ./bin/jsvc -cp ./bin/bootstrap.jar \
-user tomcat \
-outfile ./logs/catalina.out \
-errfile ./logs/catalina.err \
org.apache.catalina.startup.Bootstrap


There is a sample starup script in the $CATALINA_HOME/bin/jsvc-src/native directory, which can be modified to work on your system:


$ cd /etc/init.d
$ sudo cp tomcat tomcat.bak (if needed, and make sure it will start on the run levels you need for startup on server boot)
$ sudo cp $CATALINA_HOME/bin/jsvc-src/native/Tomcat5.sh ./tomcat
$ sudo vi tomcat (edit for your system)


Now you can start Tomcat:


$ sudo /etc/init.d/tomcat start


More info can be found at Apache Jakarta Project - commons daemon (Java Service)

Sunday, April 29, 2007

Hibernate 3 Tutorial

Here's a simple Hibernate tutorial which will guide you through installation, configuration, and use of Hibernate 3.2, HSQLDB, Ant, and Java on Linux...

This is a rather contrived example and nothing you would see in the real world, but I'll probably expand on this example in later posts.

You'll need the following to run this Hibernate example:

Apache Ant
Hibernate
Hibernate Tools
Hsqldb

As of April 23, 2007, these were the latest downloads:

apache-ant-1.7.0-bin.tar.gz
hibernate-3.2.2.ga.tar.gz
HibernateTools-3.2.0.beta9a.zip
hsqldb_1_8_0_7.zip

I installed these under /usr/local on my laptop (Fedora), but you can install them anywhere that works for you.

So I ended up with this directory structure:

/usr/local/apache-ant-1.7.0
/usr/local/hibernate-3.2
/usr/local/HibernateTools
/usr/local/hsqldb

Note: Manually create the HibernateTools directory and unzip the HibernateTools zip file there.

Create a project directory where you'll place your project files. I created a project directory called "Book" in my home directory (/home/doug/Book).

Here's the project file layout:


Book -
build.properties
build.xml
classes -
data -
src -
hibernate.cfg.xml
log4j.properties
com -
dougsparling -
AddBook.java
Book.hbm.xml
Book.java
ListBooks.java


Database files will build under the data directory. The classes directory will be created during the Ant build process, so you don't need to create that directory manually.

1) Ant build script


Includes targets to create schema/database tables, prepare project files and directories, and compile java source code, as well as a target to add books to the database and another target to read books from databasae.

Book/build.xml
-------------------

<?xml version="1.0"?>
<project name="book">

<!-- properties -->
<property file="build.properties"/>

<property name="src" value="src"/>
<property name="class" value="classes"/>
<property name="data" value="data"/>
<property name="hibernate.tools"
value="${hibernate.tools.home}${hibernate.tools.path}"/>

<!-- project classpath -->
<path id="project.classpath">
<pathelement location="${src}"/>
<pathelement location="${class}"/>
<pathelement location="${hibernate.home}/hibernate3.jar"/>
<fileset dir="${hibernate.home}/lib" includes="**/*.jar"/>
<pathelement location="${hsql.home}/lib/hsqldb.jar"/>
</path>

<!-- Hibernate tools classpath -->
<path id="tools.classpath">
<path refid="project.classpath"/>
<pathelement location="${hibernate.tools}/hibernate-tools.jar"/>
</path>

<!-- task def for Hibernate Tools -->
<taskdef name="htools"
classname="org.hibernate.tool.ant.HibernateToolTask"
classpathref="tools.classpath"/>

<!-- Create runtime subdirectories -->
<target name="prepare">
<mkdir dir="${class}"/>

<!-- Copy property files and O/R mappings needed at runtime -->
<copy todir="${class}">
<fileset dir="${src}">
<include name="**/*.properties"/>
<include name="**/*.cfg.xml"/>
</fileset>
</copy>
</target>

<!-- Generate schemas for all mapping files -->
<target name="schema" depends="prepare">
<htools destdir="${data}">
<classpath refid="tools.classpath"/>
<configuration
configurationfile="${src}/hibernate.cfg.xml"/>
<hbm2ddl drop="true" outputfilename="book.sql"/>
</htools>
</target>

<!-- Compile java source code -->
<target name="compile" depends="prepare">
<javac srcdir="${src}"
destdir="${class}"
debug="on"
optimize="off"
deprecation="on"
classpathref="project.classpath"/>
</target>

<!-- Add a book (create and persist) -->
<target name="addBooks" depends="compile">
<java classname="com.dougsparling.AddBook"
classpathref="project.classpath"/>
</target>

<!-- Display persisted data -->
<target name="listBooks" depends="compile">
<java classname="com.dougsparling.ListBooks"
classpathref="project.classpath"/>
</target>

</project>


2) build.properties


This file is included in the build.xml file. build.properties contains paths to the installed libraries.

Book/build.properties
-----------------------------------------

# Path to the hibernate install directory
hibernate.home=/usr/local/hibernate-3.2

# Path to the hibernate-tools install directory
hibernate.tools.home=/usr/local/HibernateTools

# Path to hibernate-tools.jar relative to hibernate.tools.home
hibernate.tools.path=
/plugins/org.hibernate.eclipse_3.2.0.beta9a/lib/tools

# Path to the HSQL DB install directory
hsql.home=/usr/local/hsqldb


3) Hibernate configuration file


Database connection information mappings.

Book/src/hibernate.cfg.xml
--------------------------------

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.url">
jdbc:hsqldb:file:data/book;shutdown=true
</property>
<property name="hibernate.connection.driver_class">
org.hsqldb.jdbcDriver
</property>
<property name="hibernate.connection.username">sa</property>
<property name="hibernate.connection.password"></property>
<property name="hibernate.connection.pool_size">0</property>
<property name="hibernate.dialect">
org.hibernate.dialect.HSQLDialect
</property>
<property name="hibernate.show_sql">false</property>

<!-- mapping resources -->
<mapping resource="com/dougsparling/Book.hbm.xml"/>

</session-factory>
</hibernate-configuration>


4) Logging


Set up logging. You can use the file that comes in the etc directory of the HibernateTools in the unpacked archive file.

Book/src/log4j.properties

5) Book class


POJO class to represent a book

Book/src/com/dougsparling/Book.java
-----------------------------------------

package com.dougsparling;

public class Book {
private int id;
private String title;

public Book(String title) {
this.title = title;
}

Book() {
}

public int getId() {
return this.id;
}

public void setId(int id) {
this.id = id;
}

public String getTitle() {
return this.title;
}

public void setTitle(String title) {
this.title = title;
}
}


6) Object mapping for Book class



Book/src/com/dougsparling/Book.hbm.xml
-------------------------------------------

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

<class name="com.dougsparling.Book" table="book">

<id name="id" type="int" column="id">
<generator class="native"/>
</id>

<property name="title" type="string"/>

</class>
</hibernate-mapping>


7) AddBook class


Code to add a book to database

Book/src/com/dougsparling/AddBook.java
--------------------------------------------

package com.dougsparling;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

import com.dougsparling.Book;

public class AddBook {
public static void main(String[] args) {

String title = "A Book";

SessionFactory factory =
new Configuration().configure().buildSessionFactory();
Session session = factory.openSession();
session.beginTransaction();

Book book = new Book(title);

session.save(book);
session.getTransaction().commit();
session.close();
}
}


8) ListBooks class


Code to read books from database

Book/src/com/dougsparling/ListBooks.java
----------------------------------------------

package com.dougsparling;

import java.util.Iterator;
import java.util.List;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

import com.dougsparling.Book;

public class ListBooks {
public static void main(String[] args)
{
SessionFactory factory =
new Configuration().configure().buildSessionFactory();
Session session = factory.openSession();

List books = session.createQuery("from Book").list();
System.out.println("Found " + books.size() + " books(s):");

Iterator i = books.iterator();
while(i.hasNext()) {
Book book = (Book)i.next();
System.out.println(book.getTitle());
}

session.close();
}
}


9) Ant build


At this point it's time to run Ant against the build.xml file.


kl93@oiche:/home/kl93/Book$ ant schema
kl93@oiche:/home/kl93/Book$ ant addBooks
kl93@oiche:/home/kl93/Book$ ant listBooks

Saturday, April 28, 2007

Perl - DateTime

Our servers were recently switched from using local time to UTC. This had an affect on our content, causing it to show several hours early since content availability is based on local time. In the past we had relied on Date::Calc on other various date manipulations using epoch seconds. Having recently worked on two projects with Catalyst, I had become familiar with the Perl module DateTime. Not only was converting UTC to local time a breeze, DateTime required far fewer lines of code, not to mention the ease and readability of date manipulations.

The standard Unix date command shows the server is on UTC:

kl93@oiche/home/kl93:$ date
Sat Apr 28 13:10:15 UTC 2007


or in Perl:

my $date = localtime(time);
print "$date\n";


returns

2007-04-28T13:16:29


Now, using DateTime module:

#!/usr/bin/perl
use strict;
use DateTime;
my $dt = DateTime->now();
print $dt->datetime(), "\n";


Output:

kl93@oiche:/home/kl93$ perl datetime1.pl
2007-04-28T13:23:15


To get local time, you can either set the desired time zone when you create the DateTime object:

#!/usr/bin/perl
use strict;
use DateTime;
my $dt = DateTime->now(time_zone => 'America/Chicago');
print $dt->datetime(), "\n";


or set it after you've created the object:

#!/usr/bin/perl
use strict;
use DateTime;
my $dt = DateTime->now();
$dt->set_time_zone('America/Chicago');
print $dt->datetime(), "\n";


Now we have local time for US Central:

kl93@oiche:/home/kl93$ perl datetime2.pl
2007-04-28T08:29:56


Using a named time zone like 'America/Chicago' is nice, as it will calculate DST changes. However, it's not recommended for dates far in the future (read the Pod).

You can also use "today" in place of "now," which will only initialize the date, not the time:


#/usr/bin/perl
use strict;
use DateTime;
my $dt1 = DateTime->today();
print "dt1: ", $dt1->datetime(), "\n";
my $dt2 = DateTime->now();
print "dt2: ", $dt2->datetime(), "\n";



kl93@oiche:/home/kl93$ perl datetime3.pl
dt1: 2007-04-28T00:00:00
dt2: 2007-04-28T17:53:32


You can also initialize the DateTime object to any valid date by passing passing parameters to the constructor:

#!/usr/bin/perl
use strict;
use DateTime;
my $dt = DateTime->new( year => 2001,
month => 4,
day => 23,
hour => 10,
minute => 45,
second => 30,
time_zone => 'America/Chicago',
);
print $dt->datetime(), "\n";


We can see that the date has been initialized to April 23, 2005 at 10:45:30 (am):

kl93@oiche:/home/kl93$ perl datetime4.pl
2001-04-23T10:45:30


There are several accessors - a quick perusal of the Pod shows:

$dt->year;
$dt->month; # 1-12 - also mon
$dt->day; # 1-31 - also day_of_month, mday
$dt->day_of_week; # 1-7 (Monday is 1) - also dow, wday
$dt->hour; # 0-23
$dt->minute; # 0-59 - also min
$dt->second; # 0-61 (leap seconds!) - also sec
$dt->day_of_year; # 1-366 (leap years) - also doy
$dt->day_of_quarter; # 1.. - also doq
$dt->quarter; # 1-4


I won't cover everything, that's what the Pod is for, but a few handy features include easy date formating and date calculations.

A few formatting built-ins:

$dt->ymd; # 2007-04-28
$dt->ymd('/'); # 2007/04/28
$dt->ymd('') # 20070428

$dt->mdy; # 04-28-2007
$dt->mdy('/'); # 04/28/2007
$dt->mdy(''); # 04282007

$dt->dmy; # 28-04-200
$dt->dmy('/'); # 28/04/2007
$dt->dmy(''); # 28042007

$dt->hms; # 12:33:45


And if you need it, you can use 'strftime' for further formatting:

$dt->strftime("%A, %B %d, %Y"); # Saturday, April 28, 2007


A few date manipulations:

You can update an existing DateTime object to another date:

#!/usr/bin/perl
use strict;
use DateTime;
my $dt = DateTime->new( year => 2001,
month => 4,
day => 23,
hour => 10,
time_zone => 'America/Chicago',
);
print $dt->ymd, "\n";

$dt->set( year => 2007 );
$dt->set( month => 11 );
$dt->set( day => 30 );
print $dt->ymd, "\n";


We can see that the date was changed from 2001-04-23 to 2007-11-30:

kl93@oiche:/home/kl93$ perl datetime5.pl
2001-04-23
2007-11-30


You can find x number of years, months, days from a date by using "add" and "subtract" on your DateTime object.

To find one month from today:

#!/usr/bin/perl
use strict;
use DateTime;
my $dt = DateTime->now();
print $dt->datetime(), "\n";
print $dt->add(months => 1), "\n";


Output:

kl93@oiche:/home/kl93$ perl datetime6.pl
2007-04-28T14:05:35
2007-05-28T14:05:35


Or one month previous:

#!/usr/bin/perl
use strict;
use DateTime;
my $dt = DateTime->now();
print $dt->datetime(), "\n";
print $dt->subtract(months => 1), "\n";


Output:

kl93@oiche:/home/kl93$ perl datetime7.pl
2007-04-28T14:07:42
2007-03-28T14:07:42


One note - DateTime objects are hash references, so if you want to keep your original DateTime object unchanged, you need to clone your object and then use "add" or "subtract" to get the calculated date.

Here we simply copy $dt1 and update $dt2.

#/usr/bin/perl
use strict;
use DateTime;
my $dt1 = DateTime->now();
print "dt1: ", $dt1->datetime(), "\n";
my $dt2 = $dt1;
$dt2->add(months => 1), "\n";
print "dt2: ", $dt2->datetime(), "\n";
print "dt1: ", $dt1->datetime(), "\n";


Note that once $dt2 is modified, $dt1 is modified as well (they both "point" to the same DateTime object:

kl93@oiche:/home/kl93$ perl datetime8.pl
dt1: 2007-04-28T17:19:46
dt2: 2007-05-28T17:19:46
dt1: 2007-05-28T17:19:46


To prevent this, use the clone method to create a totally new DateTime object:

#/usr/bin/perl
use strict;
use DateTime;
my $dt1 = DateTime->now();
print "dt1: $dt1->datetime(), "\n";
my $dt2 = $dt1->clone;
$dt2->add(months => 1), "\n";
print "dt2: ", $dt2->datetime(), "\n";
print "dt1: ", $dt1->datetime(), "\n";


Now $dt1 remains unchanged:

kl93@oiche:/home/kl93$ perl datetime9.pl
dt1: 2007-04-28T17:23:39
dt2: 2007-05-28T17:23:39
dt1: 2007-04-28T17:23:39


Find out more about the DateTime module at the Perl DateTime Wiki or at CPAN (written by Dave Rolsky)

Saturday, April 21, 2007

Java/Perl - Command-line arguments

Command-line arguments are passed to a Perl program in the array @ARGV.

Perl

#!/usr/bin/perl
use strict;
use warnings;

my $num_args = @ARGV;
print "Length of \@ARGV array: $num_args\n";
for(my $i = 0; $i %lt; $num_args; $i++) {
print "Argument $i = |$ARGV[$i]|\n";
}

# another way to loop through arguments
my $count = 0;
for my $arg(@ARGV) {
print "Argument $count = |$arg|\n";
$count++;
}


Output:


[kl93@localhost ]$ perl args.pl Eeyore Piglet "Pooh Bear"
Argument 0 = |Eeyore|
Argument 1 = |Piglet|
Argument 2 = |Pooh Bear|
Length of @ARGV array: 3
Argument 0 = |Eeyore|
Argument 1 = |Piglet|
Argument 2 = |Pooh Bear|


Command-line arguments are passed to main method in a Java program. The arguments are passed as a String array called args (or whatever you may choose to call it).

Java

public class Args
{
public static void main(String[] args)
{
System.out.println("Length of args array: "
+ args.length);

for(int i = 0; i < args.length; i++)
{
System.out.println("Argument " + i + " = |" + args[i] + "|");
}
}
}


Output:


[kl93@localhost ]$ java Args Eeyore Piglet "Pooh Bear"
Length of args array: 3
Argument 0 = |Eeyore|
Argument 1 = |Piglet|
Argument 2 = |Pooh Bear|

Java/Perl - String comparison (eq)

In Perl, comparison operators are used when comparing either numbers or strings. For comparing strings, the comparison operator for equality is 'eq.' Unlike Java, Perl doesn't have a String class as Perl is a "typeless" language. However, Perl scalar variables can contain numbers or strings, so there isn't much difference between 999 (number) or '999' (string). So comparing strings in Perl for equality is fairly straight forward.

Perl

#!/usr/bin/perl
use strict;
use warnings;

my $string1 = 'Eeyore';
my $string2 = 'Eeyore';

if($string1 eq $string2) {
print "True\n";
} else {
print "False\n";
}


The output is:

True


In Java, strings are objects. In most cases when an equality comparison is made between two strings, it is the value of the two strings that we want to compare. To check if two strings have the same value, the 'equals' method is used.

Java

public class Equals
{
public static void main(String[] args)
{
String string1 = "Eeyore";
String string2 = "Eeyore";
if(string1.equals(string2))
System.out.println("True");
else
System.out.println("False");
}
}


The output is:

True


Note that with Java, there are two ways to create a new string.

  1. String string1 = "Eeyore";
  2. // informal method
  3. String string2 = new String("Eeyore");
  4. // formal method


There is a subtle difference. With the the informal method, String objects are created and the JVM places them in the "literal pool," an area in JVM memory that allows shared access to String objects. In this case, before a new String object is created, the literal pool is checked to see if an identical string already exists, and if so it is reused. If not, a new String is created and added to the literal pool. That way, if several strings are created with the same value, they all point to the same String object.

With the formal method, a new String object is created even if the same string already exists. In this case the String object is placed in the JVM general memory.

To check if two Strings point to the same object, the '==' operator is used.


public class Equals2
{
public static void main(String[] args)
{
String string1 = "Eeyore";
String string2 = "Eeyore";
String string3 = new String("Eeyore");

if(string1.equals(string2))
System.out.println("True");
else
System.out.println("False");

if(string1.equals(string3))
System.out.println("True");
else
System.out.println("False");

if(string1 == string2)
System.out.println("True");
else
System.out.println("False");

if(string1 == string3)
System.out.println("True");
else
System.out.println("False");
}
}


The output is:

True
True
True
False


This shows that string1 and string2 have the same value and point to the same String object. This also shows that while string1 and string3 have the same value, they point to different String objects.

Java/Perl - split

split breaks up (or splits) a string into "tokens" or substrings according to a separator. split uses a regular expression as the separator.

Perl


#!/usr/bin/perl
use strict;
use warnings;

my @fields = split /:/, "jim:bob:frank:steve";
foreach my $field (@fields) {
print "$field\n";
}

my $string = "one two three four\tfive";
my @array = split /\s+/, $string;
foreach my $el(@array) {
print "$el\n";
}


Output:

jim
bob
frank
steve
one
two
three
four
five


Java 1.4 added the split() method to the String class.

Java


public class Split
{
public static void main(String[] args)
{
String string1 = "jim:bob:frank:steve";
String[] names = string1.split(":");
for(int i=0; i<names.length; i++)
System.out.println(names[i]);

String string2 = "one two three four\tfive";
String[] words = string2.split("\\s+");
for(int i=0; i<words.length; i++)
System.out.println(words[i]);
}
}


It is also possible to use the java.util.StringTokenizer to break a string into tokens.


import java.util.StringTokenizer;

public class StringToke
{
public static void main(String[] args)
{
String string1 = "jim:bob:frank:steve";
StringTokenizer st1 = new StringTokenizer(string1,":");
while(st1.hasMoreTokens())
System.out.println(st1.nextToken());

String string2 = "one two three four\tfive";
StringTokenizer st2 = new StringTokenizer(string2);
while(st2.hasMoreTokens())
System.out.println(st2.nextToken());
}
}


Output:

jim
bob
frank
steve
one
two
three
four
five


If no regular expression pattern is used, split defaults to using white space as the separator. (both in Perl and Java)

Java/Perl - Directory handles

With Perl, one way to get a list of names in a given directory is to use a directory handle. On my Fedora box, Perl displays "." and ".." while Java does not.

Perl

#!/usr/bin/perl
use strict;
use warnings;

my $dir = '/home/kl93';
opendir DH, $dir or die "Cannot open $dir: $!";
foreach my $file (readdir DH) {
next if $file =~ /^\.\.?$/; # skip . and ..
print "$file\n";
}
close DH;


Java

import java.io.*;

public class ReadDir
{
public static void main(String[] args) throws Exception
{

File file = new File("/home/kl93");

if( !file.exists() || !file.canRead() )
{
System.out.println("Can't read " + file);
return;
}

if( file.isDirectory() ) {
String[] files = file.list();
for(int i=0; i<files.length; i++)
System.out.println(files[i]);
}
else {
System.out.println(file + " is not a directory");
}
}
}


If you want to display filenames based on some sort of criteria - like all files with the .java extension for example - then it's often easiest to use a regular expression.

Perl

#!/usr/bin/perl
use strict;
use warnings;

my $dir = '/home/kl93';
opendir DH, $dir or die "Cannot open $dir: $!";
foreach my $file (readdir DH) {
next unless $file =~ /\.java$/;
print "$file\n";
}
close DH;


Java

import java.io.*;
import java.util.regex.*;

public class ReadDir2
{
public static void main(String[] args) throws Exception
{

File file = new File("/home/kl93");

if( !file.exists() || !file.canRead() )
{
System.out.println("Can't read " + file);
return;
}

if( file.isDirectory() ) {
String[] files = file.list();
for(int i=0; i<files.length; i++)
{
if(files[i].matches("^.*\\.java$"))
System.out.println(files[i]);
}
}
else {
System.out.println(file + " is not a directory");
}
}
}


Or


import java.io.*;
import java.util.regex.*;

public class ReadDir3
{
public static void main(String[] args) throws Exception
{

File file = new File("/home/kl93");

if( !file.exists() || !file.canRead() )
{
System.out.println("Can't read " + file);
return;
}

if( file.isDirectory() ) {
String[] files = file.list();
Pattern pattern = Pattern.compile("\\.java$");
Matcher matcher;
for(int i=0; i<files.length; i++)
{
matcher = pattern.matcher(files[i]);
if(matcher.find())
System.out.println(files[i]);
}
}
else {
System.out.println(file + " is not a directory");
}
}
}


Sort (ascending)

Perl

#!/usr/bin/perl
use strict;
use warnings;

my $dir = '/home/kl93';
opendir DH, $dir or die "Cannot open $dir: $!";
foreach my $file (sort readdir(DH)) {
print "$file\n";
}
close DH;


Sort desending


#!/usr/bin/perl
use strict;
use warnings;

my $dir = '/home/kl93';
opendir DH, $dir or die "Cannot open $dir: $!";
foreach my $file (reverse(sort readdir(DH))) {
print "$file\n";
}
close DH;

About Me

My photo
Developer (Ruby on Rails, iOS), musician/composer, Buddhist, HSP, Vegan, Aspie.

Blog Archive

Labels