0

Ruby on Rails Plugins

Posted by jason on Jul 30, 2010 in Ruby

Not sure if this is worth doing or not since these ruby plugins change so frequently, but here are a list of plugins that I find interesting as a java guy coming up to speed in ruby on rails.

Referential Integrity

script/plugin install http://svn.hasmanythrough.com/public/plugins/validates_existence/

From “Learning Rails” by O’Reilly.  Page 149

Scaffolding

ActiveScaffold.com

Geo Coding

http://geokit.rubyforge.org/

Image Processing

attachment_fu

ImageScience

http://seattlerb.rubyforge.org/ImageScience.html

 
0

HTML Dom in Ruby with Nokogiri

Posted by jason on Feb 24, 2010 in Ruby

I recently needed to do a screen scrape of a website.  The page I’m trying to parse is marked as an “XHTML 1.0 Transitional”.  XHTML?  Should be easy.  Parse the doc, use XPath and I’ll be done.

If you search “xml parser ruby”, the first result you will get is REXML.   I’ve read comparisons that point out that libxml is several of orders of magnitude faster.  My first attempt used REXML.  I failed miserably in this attempt because the web page I was parsing was not actually valid XHTML.  After I learned it was broken, I ran it through W3C’s validation service and discovered the site had over 100 errors.  XML Parsing is out.

That led me to a search for HTML Dom ruby which led me to hpricot.  I actually didn’t even try this parser because Andrew Kavanaugh pointed me to Nokogiri.  Nokogiri is interesting because it provides two different ways to find the elements you are interested in.  It lets you find an element using XPath or CSS selectors.  Lately, I’ve been doing a lot of CSS selectors so I went that route.  The document I was searching through had something like the following HTML:

...
<div class="section">
<h4>Section 1</h4>
<p>
  <sup class="requirement">1</sup>Requirement 1 descriptive sentence.
  <sup class="requirement">2</sup>Requirement 2 descriptive sentence.
  <sup class="footnote"><a href="#footnote1">footnote 1</a></sup>
    &nbsp;&nbsp;&nbsp;
    More descriptive text.
  <sup class="requirement">3</sup>Requirement 3 descriptive sentence.</div>
</p>
</div>

I needed to translate this into a format I could insert into my database.  I don’t care about footnotes, or spacing or anything other than the raw text.  I need it to look something like:

Section 1.1, Requirement 1 descriptive sentence.
Section 1.2, Requirement 2 descriptive sentence. More descriptive text.
Section 1.3, Requirement 3 descriptive sentence.

I used the following code to get the section number I was after:

@section = doc.at('div.section h4').inner_html

I then used the following code to get the subsection number and the text associated with it:

doc.css('div.section sup.requirement').each do |element|
  # Get the requirement subsection number
  @requirement = element.to_s.strip

  # Since we are interested in all the text between each of the subs
  # We need to get every text node until we run into the start of the next
  # sub class='requirement' node
  @node = element.next
  @text = ""
  while @node != nil && (@node['class'] != 'requirement') do
    if (@node.text?) then
      @text = @text + " " + @node.to_s.strip
    end
    @text = @text.strip
    @node = @node.next
  end
  puts  @section + "." + num.inner_html + ", " + @text
end

Man I love these “whatever.each do |element|” style blocks.  Very powerful.  When I ran this for the first time, I encountered an oddity I didn’t quite understand.  Even though I was calling strip to eliminate the white space, I was getting a row that looked like:

Section 1.2, Requirement 2 descriptive sentence.    More descriptive text.

It turns out that when calling to_s on a node, it converts &nbsp; into something that is whitespace but not stripped out by the normal strip function.  I modified the strip function of the String class and all worked well.

class String
  alias_method :strip_old, :strip
  def strip
    self.gsub(/^[\302\240|\s]*|[\302\240|\s]*$/, '')
  end

  def strip!
    before = self.reverse.reverse
    self.gsub!(/^[\302\240|\s]*|[\302\240|\s]*$/, '')
    before == self ? nil : self
  end
end

In the past, this would have been something I’d have just thrown together in Java.  If I ever need to do something like this again in Java, I’m going to try out this HTML parser called Cobra.  It even handles javascript calls in the page (like document.write).

 
0

Simulating a slow network on a Mac

Posted by jason on Feb 12, 2010 in Mac, Ruby

I ran across these very handy commands to simulate a connection of only 4kbps.  This is helpful when developing mobile applications in particular because some cell connections are horrible.

sudo ipfw pipe 1 config bw 4KByte/s
sudo ipfw add 100 pipe 1 tcp from any to me 3000

There are several interesting parts to these commands.

The 4KByte/s which can be whatever speed you want.  This is the bandwidth, measured in [K|M]{bit/s|Byte/s}.

The other interesting part is 3000.  This is the port that will be slowed.  Port 3000 is the default port on which Ruby on Rails listens.  If you are doing Java, you would probably want to use 8080.

The 100 is the rule number.  Useful when it comes time to delete this rule.

To clear out this pipe, simply type the following when finished:

sudo ipfw delete 100

To clear all custom rules, you could type:

sudo ipfw flush

 
2

Ruby on Rails Nested Resources, Namespaces, and RESTful

Posted by admin on Jan 11, 2010 in Ruby

I am working on a website where I have an admin section to add data and public read only sections.  I like to have a clean hierarchy of URLs.  I first implemented it using the standard generator code.  I had a /restaurants and a /locations url for both.  Locations doesn’t make sense as a top level URL because Restaurants have locations.  I’d rather see things like /restaurants/1/locations.  In fact, that isn’t quite right either.  I only want to show the restaurants for the logged in user.  I really want it to look like /admin/restaurants/1/locations.

Nested Resources

So the first thing I did was to make locations be a resource of restaurants. In my config/routes.rb file, I did the following:

map.resources :restaurants, :has_many => [ :locations ]

In the app/controllers/locations_controller.rb, I created a before_filter to set the @restaurant reference.  Every place where I used Location.find, I changed it to @restaurants.locations.find.  This only gets the locations for the restaurant from the restaurant’s locations attribute.

class LocationsController < ApplicationController
  before_filter :get_restaurant      

  def index
    @locations = @restaurant.locations
    ...
  end

private
  def get_restaurant
    @restaurant = Restaurant.find(params[:restaurant_id])
  end

end

Now my URLs look like /restaurants/1/locations.

You will need to change the form_for on your new.html.erb and edit.html.erb.  If you are using partials for both of these (you should be) then edit that file.  Change the form_for tag to

<% form_for([@restaurant, @location]) do |f| %>

Namespace

Now, I’d like to move these into the /admin url.  I do this by modifying the config.routes.rb file again.

  map.namespace :admin do |admin|
    admin.resources :restaurants, :has_many => [ :locations ]
  end

This creates the admin namespace with URLs that look like /admin/restaurants/1/locations.  When you add this namespace, Rails will then look for your controllers inside the module Admin.  So you will need to create the app/controllers/admin directory and move the restaurants_controller.rb and locations_controller.rb inside it.  Do the same for the app/views/admin directory.  Move the locations and restaurants folder inside it.  You will need to modify the /app/controllers/admin/restaurants_controller.rb and the /app/controllers/admin/locations_controller.rb files.  Add Admin:: in front of the class name like follows:

class Admin::LocationsController < ApplicationController

When I opened up /admin/restaurants/1/locations in my browser, I got errors saying things like restaurant_location_path is invalid.  To correct this, I had to modify the views to prepend “admin_” to all the path functions and add the location parameter.  A safe guess is that Ruby on Rails dynamically generates these functions.

<td><%= link_to 'Show', admin_restaurant_location_path(@restaurant, location) %></td>
<td><%= link_to 'Edit', edit_admin_restaurant_location_path(@restaurant, location) %></td>
<td><%= link_to 'Destroy', admin_restaurant_location_path(@restaurant, location), :confirm => 'Are you sure?', :method => :delete %></td>

I really don’t like embedding the “admin” into the code because I could change my mind and want it all to live inside /administration.  I don’t want to have to change all my code just because of where it lives.  Too tightly coupled.  so, I am not going to use the function created for me.  Instead, I’ll build the URL myself.  If you do not specify the controller or namespace, it uses the current one. Change

<td><%= link_to 'Show', url_for(:action => 'show', :id => location) %></td>
<td><%= link_to 'Edit', url_for(:action => 'edit', :id => location) %></td>
<td><%= link_to 'Destroy', url_for(:action => 'destroy',:id => location), :confirm => 'Are you sure?', :method => :delete %></td>

This will build the same URLs as using the “admin_” functions but without hardcoding the admin path.  You need to do the same thing inside the controllers.  For the link back to the parent, you will need to add the controller as a parameter.

<%= link_to 'Back', admin_restaurant_path(@restaurant) %>

Change to

<%= link_to 'Back', url_for(:controller => 'restaurants', :action => 'show', :id => @restaurant) %>

The next thing you will need to change is the form edit.

<% form_for( :location, @location, :url => { :action => "create"} ) do |f| %>

 
1

Ruby on Rails shell commands

Posted by jason on Jan 6, 2010 in Ruby

This is just a reminder to me about commands that I use.

rails appname
rake --tasks
rake db:create
rake db:drop
rake db:migrate
script/generate controller myobject
script/generate model Person name:string age:integer
script/generate scaffold Person name:string age:integer

 
0

Ruby on Rails ORM through ActiveRecord

Posted by jason on Jan 6, 2010 in Ruby

Rails offers an ORM layer.  This layer is not a very strict layer.  To filter or sort objects, you are actually writing SQL code.  There doesn’t seem to be much complaining in the community about this mis-feature.  In fact in the Agile Web Development with Rails book, the author basically makes snide comments about people who think that the object hierarchy and the database model should not be tied tightly together.  This may just be a cultural thing.  Ruby on Rails seems to be used mostly on brand new projects.  Given this situation, it is understandable why Rails developers don’t understand the importance of uncoupling the database from the object graph.

One of my first consulting jobs was with AMC Theaters.  I was on a tiny project that needed to read from a database that we had absolutely no control over.  Unfortunately for us, the tables and fields often had entirely different purposes than what they were labeled as.  These corporate databases can last decades and changing them is not possible.  Many different groups have written code around these names and changing a field in a table has ramifications way beyond our little group.  It would cause applications to break and there is no funding to fix all the applications just because we don’t like the field name.  Consequently, you are left with the task of mapping a field in a table that has a non-sense name to a field in the object that actually makes sense when you read the code.  This is where Ruby fails to deliver with it’s implementation of ORM through ActiveRecord.

Since I’m learning Rails so I can create new ideas quickly, I will happily use the current ORM.  But this is one area where Rails needs to improve.

 
0

Ruby on Rails Layouts

Posted by jason on Jan 6, 2010 in Ruby

Site wide layout

One common method of creating common look and feels in web development is to split up all the common code into a header file and a footer file and include those files in every single interface page.  This is better than embedding all that html code inside each page, but it is still clunky.  You can not easily see where your tags open and close because they can be opened in one file and closed in another file.  More advanced mechanisms put the overall code in one file but have middle sections where code specific html goes.  So, your html template file would look something like:

<html>
  <body>
    <%= yield %>
  </body>
</html>

It is easy to see the structure of an html page this way.  It also cleans up the code for your view.  Ruby on Rails implements this style of templating and calls it layouts.  Because of Rails’ convention over configuration, to implement a site wide template, you merely need to create /app/views/layouts/application.html.erb and put your site wide html code there.

Override default layout

You can override the layout that will be used by adding a layout declaration inside your controller class like this:

class HelloController < ApplicationController

  layout "mylayout"

  def index
  end
end

Create /app/views/layouts/mylayout.html.erb

Two different layouts, same view

You may have different layouts depending on whether the user is logged in or not.  This is easy to accomplish by using a symbol for the layout.

class HelloController < ApplicationController

  layout :isLoggedInLayout

  def index
  end

private
  def isLoggedInLayout
    "loggedIn"
  else
    "notLoggedIn"
  end
end

You would have two templates, one /app/views/layouts/loggedIn.html.erb, the other /app/views/layouts/notLoggedIn.html.erb.

 
0

Fixtures and has_one in Ruby on Rails

Posted by jason on Jan 1, 2010 in Ruby

In Rails 2.0, fixtures has been improved.  You no longer need to specify ID.  You can check out the screencast.  unfortunately, it does not support the has_one (as far as I can tell).

I have a location object that specifies a phone number, a name, and an address object.  It is specified as follows:

class Location < ActiveRecord::Base
  has_one :address
end

My address class is simply:

class Address < ActiveRecord::Base
end

My address fixture file addresses.yml looks like this:

address1:
  address1: 1234 E Main St
  address2:
  city: Denver
  state: CO
  postal_code: 80014

My locations.yml file looks like:

location1:
  phone: 555-555-5555
  address: address1

If I try to do rake test:units, I get errors saying the ‘address’ column is unknown. Interestingly, if I change the has_one: to belongs_to:, it works just fine.

My work around is to revert to the old style and go ahead and specify ids.

addresses.yml:

address1:
  id: 1
  address1: 1234 E Main St
  address2:
  city: Denver
  state: CO
  postal_code: 80014

locations.yml

location1:
  phone: 555-555-5555
  address_id: 1

 
0

Creating and deploying a Rails app

Posted by jason on Dec 30, 2009 in Ruby

This documents how I create Ruby on Rails applications and deploy them to my server.  I have created a bare bones apache web server that hosts ruby applications using MySQL.  It also assumes you have Ruby on Rails set up on a Macbook Pro.

Server Side

User

mkdir ~/myrumbleapp
mkdir ~/myrumbleapp/www
mkdir ~/myrumbleapp/www/current
mkdir ~/myrumbleapp/logs

GIT

sudo groupadd myrumbleapp
sudo usermod -a -G myrumbleapp {USER}
cd ~/myrumbleapp
mkdir git
sudo chown {USER}.myrumbleapp git
cd git
git init --bare --shared=group

MySQL

mysql -u root -p
mysql> create database myrumbleapp;
mysql> use myrumbleapp;
mysql> grant all on myrumbleapp.* to myrumbleapp@localhost identified by 'password';

Apache2

/etc/apache2/sites-available/myrumbleapp
<VirtualHost *:80>
ServerName www.myrumbleapp.com
ServerAlias myrumbleapp.com
DocumentRoot /home/{USER}/www.myrumbleapp.com/www/current/public
ErrorLog /home/{USER}/www.myrumbleapp.com/logs/error_log
TransferLog /home/{USER}/www.myrumbleapp.com/logs/access_log
</VirtualHost>
sudo ln -s /etc/apache2/sites-available/myrumbleapp /etc/apache2/sites-enabled/myrumbleapp
sudo /etc/init.d/apache2 restart

Client Side

On the client

First, create a skeleton rails application.  For this example, I will be using “myrumbleapp”.  We will also freeze the rails version into our application. That way we never have to worry about rails versions on serverrs.

cd ~/Sites/
rails myrumbleapp
cd ~/Sites/myrumbleapp
rake rails:freeze:gems

/.gitignore

tmp
log
mkmf.log
config/database.yml

Capistrano
cd ~/Sites/myrumbleapp
capify .

I use the following deploy.rb file.  The important part is that it creates a link from my shared/system/config/database.yml file into the release folder.  I keep the database.yml file out of GIT because it seems to be a best practice that other folks follow.  Not exactly sure it is necessary with my code since I host my own GIT repos accessible only via SSH.

/config/deploy.rb

default_run_options[:pty] = true
set :application, "myrumbleapp"
set :repository,  "{USER}@{SERVER.COM}:myrumbleapp/git"
set :user, "USER"
set :deploy_to, "/home/{USER}/myrumbleapp/www"
set :scm, :git
set :branch, "master"

role :web, "{SERVER.COM}"
role :app, "{SERVER.COM}"
role :db,  "{SERVER.COM}", :primary => true

namespace :deploy do
  desc "restart passenger"
  task :restart, :roles => :app, :except => { :no_release => true } do
    run "#{try_sudo} touch #{File.join(current_path,'tmp','restart.txt')}"
  end

  [:start, :stop].each do |t|
    desc "#{t} task is a no-op with passenger"
    task t, :roles => :app do ; end
  end

  desc "Symlink shared configs and folders on each release."
  task :symlink_shared do
    run "ln -nfs #{shared_path}/system/config/database.yml #{release_path}/config/database.yml"
  end
end

after 'deploy:update_code', 'deploy:symlink_shared'


GIT

Next we will check our application into GIT version control.

git init
cp config/database config/database.tmp.yml
git add .
git commit -m "initial checkin"

Configure the remote repository for GIT

git remote add origin {USER}@snowcaptech.com:myrumbleapp/git

Push the skeleton application to the server

git push origin master

Deploy the application

cap deploy:setup

cap deploy

cap deploy:migrations

 
0

Bare Ubuntu configuration for Ruby on Rails

Posted by jason on Dec 30, 2009 in Ruby, Ubuntu

General

Install Ubuntu 9.1 server using the Bare Server option

The first thing I like to do is add my account to the /etc/sudoers file. This just guarantees that I can always use sudo, even if I mistakenly mess up my group assignment.

sudo visudo
#  /etc/sudoers
root   ALL=(ALL) ALL
user ALL=(ALL) ALL

user should be replaced with the account you use to administer the box.  I like to do this because one time I messed up my group assignment on the only account that was able to admin the box.

alias sudo='sudo -E'

The reason we have that alias nonsense in there is because Ubuntu compiles the sudo command with the –with-secure-path option.  We want to pass our current path into the command.  The alias command is explained here.  I’m sure there are some security vulnerabilities with muti-user systems doing it this way, but I’m the only guy so no need to figure it out right now.

Next, update the apt-get configuration and then upgrade to make sure the latest packages are installed.

sudo apt-get update
sudo apt-get upgrade -y

Configure the server for static IP address.

SSH

Configure SSH.

Ruby

As I write this, the latest version of Ruby for apt-get is 1.8.7 patchlevel 174.

sudo apt-get install build-essential libssl-dev libreadline-dev rake -y
sudo apt-get install ruby rdoc ri rubygems libopenssl-ruby ruby-dev -y
I like to create a current symbolic link that links to the current gem so I can easily upgrade and not have to redo all my paths.
ln -s /var/lib/gems/1.8 /var/lib/gems/current

Now, source the file you just created.  Sourcing a file uses the contents of the file as if you typed them at the command prompt.  You just type a period followed by a space and then the filename.

. /etc/profile.d/gems.sh

You will notice that we did not install Rails.  This is intentional.  We will use

rake rails:freeze:gems

to freeze a known version of rails into our applications.

MySQL

sudo apt-get install mysql-server libmysqlclient-dev
sudo gem install mysql

Apache

We will install Apache2 as well as Passenger (mod_rails or mod_rack).

sudo apt-get install apache2 libapr1-dev apache2-prefork-dev -y
sudo gem install passenger
sudo /var/lib/gems/current/bin/passenger-install-apache2-module
After you run this, you will see instructions to add some lines to your Apache configuration file.  Mine looked like this:
LoadModule passenger_module /var/lib/gems/1.8/gems/passenger-2.2.8/ext/apache2/mod_passenger.so
PassengerRoot /var/lib/gems/1.8/gems/passenger-2.2.8
PassengerRuby /usr/bin/ruby1.8
LoadModule passenger_module /var/lib/gems/1.8/gems/passenger-2.2.8/ext/apache2/mod_passenger.so   PassengerRoot /var/lib/gems/1.8/gems/passenger-2.2.8   PassengerRuby /usr/bin/ruby1.8

Just copy this to it’s own file in the conf.d directory of apache2.

sudo vi /etc/apache2/conf.d/passenger.conf

GIT

Install and configure GIT on the server and the client.

Copyright © 2010 programming with passion All rights reserved. Theme by Laptop Geek.