Archive for the 'Rails' Category

wee_lightbox rails plugin

August 6th, 2008 by pyrat

Overview

This is a packaged version of lightbox for viewing images on the screen in the popular lightbox design pattern. The plugin includes some view helpers and an automatic install script to make life easier.

Please refer to the lightbox docs for more info. Included are some rails helpers for easy integration into your site. The lightbox javascript is slightly tweaked to configure the location of images and increase the speed of resize.

You can edit lightbox.js to further tweak these if you wish.

Install

If you install from github with rails 2.1+ it will install the files into your public directory. If you install with another method, you may need to issue a recursive copy command manually.

Also this install script is untested on windows.

Usage Example

In your layout add the following in the head section.

<%= javascript_include_tag :defaults %> 
<%= yield :page_includes %>

In your template:

<%= load_lightbox %>

For each image that you would like to make a lightbox to

<%= lightbox_to('/images/small.gif', '/images/large.gif', "Tasty image caption")

If you want to have more than one lightbox on a page and have the lightbox operate with a slideshow between images..

<%= lightbox_to('/images/small.gif', '/images/large.gif', "Tasty image caption", "group_name")

Where group name needs to be the same for all images in the slideshow.

Github project page

Wee Date Picker Rails Plugin

July 18th, 2008 by pyrat

After a bit of a sabbatical here is another plugin for you to feast on.

This is a super simple datepicker for your rails apps. It is essentially just a packaged version of DatePicker v2.9 by frequency-decoder.com with rails helpers for easy integration.

Install

If you install the plugin using git the installer should run and the files will be copied across recursively to your public directory.

Example

In the head section of the layout.

<%= yield :date_includes %>

In the template.

<%= load_date_picker %>

Then for each date field you need to do the following. Remember: field_names have to be unique on your page.

<%= date_picker :start_date, Date.today %>

Where the first argument is the field_name and the second is the initial value. This must be a date object.

This is a tiny plugin which needs a bit of work but hopefully it helps. It is available on the github project page

Update: I wrote the installer script myself.

Rails Gotcha ActionController::MethodNotAllowed

July 14th, 2008 by pyrat

Turns out that sometimes with Mongrel less than 1.1.5 routes are not being resolved properly with Rails 2.1. (Experienced on OSX)

To fix the problem I upgraded mongrel.

sudo gem update mongrel

Would be nice to know how widespread this problem is. This link talks about this same issue in the comments. The main post is a little confused however.

Ryan talks some more

June 4th, 2008 by pyrat

Andy heres another interview for with Ryan Davis Singer for you. (Its audio)

Interview

Tips for Designers and Rails

June 3rd, 2008 by pyrat

Andy you might like this one; Ryan Davis Singer from 37 Signals on Design and Rails.

Multiple Submit Buttons on Rails Form

June 3rd, 2008 by pyrat

Pretty basic stuff but I often forget as I dont use this pattern much.
Multiple Submit Buttons

9th Place in the Railscasts Contest

May 13th, 2008 by pyrat

I was 9th in the railscasts contest

5 Rails Tips

April 25th, 2008 by pyrat

Top Tips Rails

I have put together 5 rails tips which I think you might find useful.

1. Throw your own exceptions

Throwing and catching exceptions can be a good design pattern in your rails app. Especially when you want to be able to deal with the unexpected in a clean way. Overuse is a no no as with most techniques but it is nice here and there.

I find that a good idea is to make your own exceptions when writing a rails app. This means that standard low level exceptions which often should not be caught are not.

eg.

(in config/initializers/custom_config.rb)

  class ApplicationError < RuntimeError
 
  end

This way in your application code you can do something like the following:

  class User < ActiveRecord:Base
    has_many :articles
    before_destroy :check_articles_dont_exist
 
    private
    def check_articles_dont_exist
      unless articles.empty?
        raise ApplicationError, 'Cannot delete user, they have written articles.'
      end
      true
    end
 
  end

  begin
    @user = User.find_by_password_reset_code(params[:id])
    @user.destroy
    flash[:message] = 'Deleted user successfully'
  rescue ApplicationError => msg
    flash[:warning] = msg.to_s
  end
  redirect_to users_url

2. Minimise your page load overhead in production

Modern website often have lots of tiny javascript/css files, especially rails sites. It is a good technique to only include what you have to on a page. Eg. only load prototype if you need to use some of the functionality it provides.

You can often achieve as much as a 50% improvement in page load time by decreasing the number of HTTP requests that the client browser has to make.

Rails 2 has built in caching and joining of javascript and css files. API Documentation for Javascript Include Tag
and API Documentation for Stylesheet Link Tag

ActionController::Base.performcaching__ must be set to true (default in production) for this to work. Also, you may want to setup some ignore rules in your version control to stop the combined files being stored.

Bundle fu

If you are not using Rails 2 or want a slightly more advanced solution there is a plugin called bundle_fu. It is very easy to setup and works well. I have had issues with using this plugin when serving over HTTPS so test it works with your setup first.

3. Write your own capistrano recipes plugin

If you work on a number of projects and use capistrano for a lot of them, it is likely that you are duplicating a lot of deploy functionality in your recipes. There are a number of recipe gems out there, the most famous being the railmachine gem. However the disadvantage of these is that often they dont quite fit the environment you are working in.

The capistrano 2 peepcode screencast gives some good information on this technique. An example deployment recipies plugin with basecamp update support is available at http://iformis.svnrepository.com/svn/deployment_recipiez

This means you can seriously cut down the size of your deploy.rb and setup deployments for new applications in much less time.

  # MAIN Config
  set :application, "test_app"
  default_run_options[:pty] = true
  set :repository,  "git@github.com:pyrat/test-app.git"
  set :scm, "git"
  set :server, 'tester.ath.cx'
 
 
  # Deployment specifics
  set :deploy_to, "/var/www/apps/#{application}" 
  set :user, "deploy"            
  set :rails_env, 'testing'
  set :mongrel_port, 3399
 
  # SSH Keys
  ssh_options[:keys] = %w(/home/pyrat/.ssh/ssh_key)
 
  # Server setup
  role :app, server
  role :web, server
  role :db,  server, :primary => true
 
  # Hooks into tasks
 
  namespace :deploy do
    task :restart do
      recipiez::single_mongrel_restart
      recipiez::nginx_restart
    end
  end
  after "deploy:update_code", "recipiez:rename_db_file" 
  after "deploy:restart", "deploy:cleanup"

Another tip with this is to set it up as an svn:external:

script/plugin install -x [plugin_location]

This allows your recipies to stay up to date across multiple projects. There are complications if you are using git for your source control system. A possible technique is explained here but I look forward to a simpler solution being suggested!

4. Bundle gem dependencies with your application

A technique which has been described by the err the blog guys as Vendor Everything

Basically it involves:

1. Setting up a vendor/gems directory 2. Running
gem unpack
commands in this directory to unpack gems. 3. Adding a hook in your environment.rb to load these:

  config.load_paths += Dir["#{RAILS_ROOT}/vendor/gems/**"].map do |dir| 
    File.directory?(lib = "#{dir}/lib") ? lib : dir
  end

The main advantage of this is that you can deploy on different servers which minimal hassle as all the libraries that are required for your application are included with it.

This technique has been taken on board by the rails core and they are releasing gem dependencies functionality as part of the next big rails release. Info on gem dependencies and how they might work can be found here

5. Use of to_param for cheap seo

Often when developing restful controllers you will have a show url that looks like this:

  article_url(@article)

This will likely translate into
http://myaceapp.com/articles/5
where the article has an id of 5.

  def show
    @article = Article.find(params[:id])
  end

Now this id is not very friendly to the search engines. A very cheap way to generate more descriptive urls is to take advantage of the way that ruby parses numbers. It will strip everything which is not a number if the string starts with a number.

eg.

“5-sfsdfsdf-sdfsdf-fsdfs” => 5
“5dfjdfkljfdkj4444445lsjskldj” => 5
“5” => 5

This cheeky little snippet will help you out (in this case slap it in the Article model):

  def to_param
    "#{id}-#{headline.gsub(/[^a-z0-9]+/i, '-')}".downcase
  end

You will need to change headline to whatever field you want to use. to_param is called on the model when the url is generated. So the example above will change to
http://myaceapp.com/articles/5-railscasts-rocks
There is a slight downside that
http://myaceapp.com/articles/5-ANYTHINGHEREATALL
will take you to the same page, but I think this is a minor issue not worth losing sleep over.

The End

Well I hope you have found my 5 rails tips interesting. Take it easy.

Google Sitemap Generator

February 26th, 2008 by pyrat

maps are good

Google sitemaps are nice for telling google what is where. Often clients want it for SEO or you have a site which has new content all the time and you want to keep google up to date.

Whatever the reason is thats you are interested in these little xml files, the following code allows you to generate a sitemap for a dynamic site in ruby.

Firstly the class:

  require 'net/http'
  require 'uri'
 
  # A class specific to the application which generates a google sitemap from
  # the contents of the database.
  # Author: Alastair Brunton
  class GoogleSitemapGenerator
 
    def initialize(base_url, sources)
      @base_url = base_url
      @sources = sources 
    end
 
    # The main generator method which in turn adds to the path_array from the different
    # sources.
    # Sources are: pages, events, properties
    def generate
      path_ar = Array.new
      @sources.each do |source|
        # initialize the class and call the get_paths method on it.
        path_ar = path_ar + eval("#{source}.get_paths")
      end
      xml = generate_xml(path_ar)
      save_file(xml)
      update_google
    end
 
    # This creates the xml document.
  	def generate_xml(path_ar)
  		xml_str = ""
  		xml = Builder::XmlMarkup.new(:target => xml_str)
 
  		xml.instruct!
  			xml.urlset(:xmlns=>'http://www.google.com/schemas/sitemap/0.84') {
    			path_ar.each do |path|
      	    xml.url {
        	    	xml.loc(@base_url + path[:url])
        			xml.lastmod(path[:last_mod])
        			xml.changefreq('weekly')
     			 }
    			end
  			}	
  		xml_str
  	end
 
  	# Saves the xml file to disc. This could also be used to ping the webmaster tools
  	def save_file(xml)
  		File.open(RAILS_ROOT + '/public/sitemap.xml', "w+") do |f|
  			f.write(xml)	
  		end		
  	end
 
  	# Notify google of the new sitemap
  	def update_google
  	    sitemap_uri = @base_url + '/sitemap.xml'
  	    escaped_sitemap_uri = URI.escape(sitemap_uri)
  	    Net::HTTP.get('www.google.com',
  	                  '/webmasters/sitemaps/ping?sitemap=' +
  	                  escaped_sitemap_uri)
  	end
 
 
  end

You will notice that an array of strings are passed when calling the generator. These are names of object which implement the get_paths method. An example get_paths class method is as follows:

  # for the google sitemap
   def self.get_paths
     path_ar = Array.new
     Property.live_properties.each do |property|
       path_ar << {:url => "/property/#{property.to_param}", :last_mod => property.updated_at.strftime('%Y-%m-%d')}
     end
     path_ar
   end

Basically, you need an array of hashes which each contain the url and the last_mod.

To call this little beastie it is best done from a cron on the production server. An example rake task to do this is as follows:

  namespace :google_sitemap do
    desc "Generate a google sitemap from the site."
    task(:generate => :environment) do
      sources = ['Page', 'Event', 'Property']
      sitemap = GoogleSitemapGenerator.new('http://www.your_url.com', sources)
      sitemap.generate
    end
  end

Remember when you are calling it from a cron to pass the RAILS_ENV. This generator does rely on rails but you could convert it to only rely on ruby by modifying the rake task and changing the RAILS_ROOT reference in the save_file method. Probably can be made to work with Merb but I am unsure of how merb and rake work together. Will hopefully get my hands dirty with Merb sometime soon.

   cd /var/www/apps/site/current /usr/bin/rake RAILS_ENV=production google_sitemap:generate

Throw your own exceptions

February 24th, 2008 by pyrat

exceptions

Throwing and catching exceptions can be a good design pattern in your rails app. Especially when you want to be able to deal with the unexpected in a clean way. Overuse is a no no as with most techniques but it is nice here and there.

I find that a good idea is to make your own exceptions when writing a rails app. This means that standard low level exceptions which often should not be caught are not.

eg.

(in config/initializers/custom_config.rb)

  class ApplicationError < RuntimeError
 
  end

This way in your application code you can do something like the following

  begin
    @user = User.find_by_password_reset_code(params[:id])
    raise ApplicationError if @user.nil?
 
  rescue ApplicationError => msg
    flash[:message] = "Sorry - That is an invalid password reset code. Please check your code and try again. (Perhaps your email client inserted a carriage return?"
    redirect_to logins_url
  end

In other news I have started testing deploying with git and capistrano. It is blazingly fast even for a full checkout. I cant get :remote_cache deploy via working due to an ancient version of git on LTS 6.06. But event with full checkout this is faster than deploy_via :remote_cache with subversion! (This is with 2 slices on a local network.)