Wednesday, January 9, 2008

AJAX, Django, RoR

Michael has a couple of posts on ajax and django.  Since we occasionally discuss the merits and faults of rails and django, I thought I would provide a similar example in rails (version 2.02). At the end of this post I've included the entire source code of a rails application that creates a search form, submits the search string via XMLHttpRequest, generates random search data, updates the search results independently of the rest of the page, and for kicks - makes the search results flash.

It is simple but instructive.  To my eye, it is more elegant than the django code. for a couple of reasons:
  • No JavaScript.  I love JavaScript and have written plenty of it, but for simple and moderately complex things rails gets the abstraction correct.  Rails' ajax helpers (form_remote_tag and page in this case), do all the work for you.
  • Easy reuse of partials.   Michael had to jump through a few hoops to reuse his 'searchresults' partial (partial in rails == tag in djago). This is because django is not aware of the ajax library and thus cannot provide facilities to automate the reuse.
Edit:  My apologies for the code formatting.  The formatter I used does not recognize the html.erb syntax, and made a mess of the entire code block.

# 1) create a rails application by running "rails ajaxform" at a command prompt
# 2) create the files listed below
# 3) start the server by running "script/server" (mac,*nix) or "ruby script/server" (windows)
# 4) point your webrowser at http://localhost:3000

# ----- app/views/foos/search.html.erb -----
<% form_remote_tag(:url=>{:action=>'search'}) do %>
<%= text_field_tag('search_str') %
>
<%= submit_tag 'Search' %>
<% end %>
<%
= render :partial=>'search_results'%>

# ----- app/views/foos/_search_results.html.erb -----
<div id='search_results'
>
<%= @search_results.size > 0 ? @search_results.collect do |result| "<p>#{result}</p>" end.join : "No Search Results" %>
</div>

# ----- app/views/foos/search.js.rjs--------
page[:search_results].replace(:partial
=>'search_results')
page[:search_results].visual_effect(:pulsate) # just for fun!`

# ----- app/controllers/foos_controller.rb -----
class FoosController < ApplicationController
def search
@search_results=[]
if search_str=params[:search_str]
@search_results = (1..rand(10)).collect do |ii| "#{search_str} + #{ii}" end
end
respond_to do |format|
format.html
format.js
end
end
end

# ----- app/views/layouts/application.html.erb -----
<html>
<head>
<%=javascript_include_tag :defaults%>
</head>
<body>
<%
= yield %>
</body
>
</html>

Friday, December 7, 2007

How to Format Ruby Code in a Blog Post

My previous post was the first time I had ever presented ruby code in a post. This article saved me the hassle of figuring it out how to make it look pretty (I stuck the css directly into my blogger template). Thanks Wolfmann.

Sending GMail from a Standalone Ruby Script

As part of my rails application, I need to send emails from a stand alone ruby script. For example, the back end of my rails application is a (currently) small postgres database. The easiest way to back it up is to send a database dump to myself via gmail.

There are several tutorials on how to send gmail directly from a rails app. Presented here is a method for doing it from a standalone script. This method assumes rails is installed, but the script itself runs outside of any rails application. In my case in the wee hours of the morning as a cron job.

Usage of this script would be something like:
require 'send_gmail'
hsh={:to=>'name@domain.com', :subject=>'subject', :body=>'body'}
SendGMail.send_gmail(hsh)

The script can also handle attachments, albeit in an unusual way. Instead of giving it a file, you give it the data directly, along with a filename, mime_type, and transfer encoding:
raw_attachment={:mime_type=>'application/x-gzip', :body=>make_body, :transfer_encoding=>'base64', :filename=>'backup.tar.gz' }
hsh[:raw_attachment]=raw_attachment

It should be easy enough to modify the script to handle files as attachments directly, but I didn't have such a need.

Here is the complete listing. My thanks to Stephun Chu, as most of this is cribbed from his aforementioned post.

Edit: Fixed syntax error - Thanks Nolan!  

send_gmail.rb


#!/usr/bin/env ruby
require 'rubygems'
gem 'actionmailer'
require 'action_mailer'
require 'openssl'
require 'net/smtp'

module SendGMail

@user_name='username@domain.com'
@domain='domain.com'
@password='password'


def SendGMail.send_gmail(hsh)

raw_attachments=hsh.fetch(:raw_attachements, [])
if hsh.has_key?(:raw_attachment)
raw_attachments.push(hsh[:raw_attachment])
end

mail=TMail::Mail.new
mail.to=hsh[:to]
mail.date=Time.now
mail.from=@user_name
mail.subject=hsh[:subject]

main=mail
main=TMail::Mail.new
main.body = hsh[:body]
puts main.body
main.set_content_type('text/plain', nil, 'charset'=>'utf-8')
mail.parts.push(main)

for raw_attachment in raw_attachments
part = TMail::Mail.new
transfer_encoding=raw_attachment[:transfer_encoding]
body=raw_attachment[:body]
case (transfer_encoding || "").downcase
when "base64" then
part.body = TMail::Base64.folding_encode(body)
when "quoted-printable"
part.body = [body].pack("M*")
else
part.body = body
end

part.transfer_encoding = transfer_encoding
part.set_content_type(raw_attachment[:mime_type], nil, 'name' => raw_attachment[:filename])
part.set_content_disposition("attachment", "filename"=>raw_attachment[:filename])
mail.parts.push(part)
end

mail.set_content_type('multipart', 'mixed')
ActionMailer::Base.deliver(mail)

end

ActionMailer::Base.smtp_settings = {
:address => 'smtp.gmail.com',
:domain => @domain,
:authentication => :plain,
:port => 587,
:user_name => @user_name,
:password => @password
}

Net::SMTP.class_eval do
private
def do_start(helodomain, user, secret, authtype)
raise IOError, 'SMTP session already started' if @started
check_auth_args user, secret, authtype if user or secret

sock = timeout(@open_timeout) { TCPSocket.open(@address, @port) }
@socket = Net::InternetMessageIO.new(sock)
@socket.read_timeout = 60 #@read_timeout
@socket.debug_output = STDERR #@debug_output

check_response(critical { recv_response() })
do_helo(helodomain)

raise 'openssl library not installed' unless defined?(OpenSSL)
starttls
ssl = OpenSSL::SSL::SSLSocket.new(sock)
ssl.sync_close = true
ssl.connect
@socket = Net::InternetMessageIO.new(ssl)
@socket.read_timeout = 60 #@read_timeout
@socket.debug_output = STDERR #@debug_output
do_helo(helodomain)

authenticate user, secret, authtype if user
@started = true
ensure
unless @started
# authentication failed, cancel connection.
@socket.close if not @started and @socket and not @socket.closed?
@socket = nil
end
end

def do_helo(helodomain)
begin
if @esmtp
ehlo helodomain
else
helo helodomain
end
rescue Net::ProtocolError
if @esmtp
@esmtp = false
@error_occured = false
retry
end
raise
end
end

def starttls
getok('STARTTLS')
end

def quit
begin
getok('QUIT')
rescue EOFError, OpenSSL::SSL::SSLError
end
end
end
end

Thursday, November 15, 2007

Syntax Coloring for Dynamic Stylesheets

I often need to dynamically generate stylesheets. Thankfully this path is well worn and solutions are readily available for Ruby on Rails. I chose the ERb/CSS based solution presented here:

Dirt Simple .rcss templates
Simpler than dirt: RESTful Dynamic CSS

If you're in the market, I'd also recommend looking at Sass (Syntactically Awesome StyleSheets). I'll explain my choice in favor of ERb/CSS in a future post.

One of the issues with the ERb/CSS solution is that editors are not aware of the syntax. To remedy this, I have created a plugin for ActiveState's Komodo IDE .

The plugins are here (with the usual disclaimer):

css-erb-highlight-only-1.0.0-ko.xpi
css-erb-highlight-and-check-1.0.0-ko.xpi
rcss-highlight-only-1.0.0-ko.xpi
rcss-highlight-and_check-1.0.0-ko.xpi

The "highlight-and-check" versions provide syntax highlighting, syntax checking, and auto indentation. However due to a shortcoming in ActiveState's tools, the syntax checking is imperfect and you will see syntax errors where there are none. I will remedy this as soon at the tools allow.

It should go without saying that the "highlight-only" versions only provide syntax highlighting, and the "css-erb" and "rcss" versions provide the functionality for the ".css.erb" and ".rcss" extensions respectively.

In a future post, I hope detail how these extensions were created.

I'm using Komodo IDE 4.2 on Mac OS X, but I believe the extensions will work in all Komodo 4 products. Please let me know if they don't.