validates_presence_of

A handy little method to ensure that one field, another or both are present.

  def self.validates_presence_of_either(*attrs)
    options = { :message => "One of #{attrs.to_sentence} must be set", :on => :save,
      :error_key => attrs.collect { |a| a.to_s }.join("_") }
    options.update(attrs.pop) if attrs.last.is_a?(Hash)
        
    send(validation_method(options[:on])) do |record|
      has_error = true
      attrs.each do |attr|
        value = record.send(attr)
        unless value.blank?
          has_error = false
        end
      end
      
      if has_error
        record.errors.add(options[:error_key], options[:message])
      end
    end
  end


Then use as follows:

validates_presence_of_either :foo, :bar, :message => “Please set foo and/or bar!”

Sunday, August 1, 2010

Updated breadcrumbs [+ better titles!]

Just a quick post, I’ve been tweaking my breadcrumbs mixin, and I think I’ve got it as good as it’s gonna get (hah!)..

I’ll just give you guys the code:

module BreadcrumbHelper

  def self.included receiver
    receiver.extend ClassMethods
  end

  module ClassMethods    
    def breadcrumb name, options = {}
      before_filter options do |controller|
        controller.send(:breadcrumb, name, options)
      end
    end
  end

  def breadcrumb name, options = {}
    @breadcrumbs ||= []    
    @breadcrumbs << { :name => name, :options => options }
  end
  
  def breadcrumbs delimeter = " / "
     links = []
     @breadcrumbs.each do |breadcrumb|
       name = format_name(breadcrumb)
       unless breadcrumb == @breadcrumbs.last
         if breadcrumb[:options][:url]
           url = breadcrumb[:options][:url]
         else
           url = "#{name.downcase}_path" # for the lazy
         end
         url = eval(url) if url =~ /^@|([a-z0-9]+(_path|_url)(\([^ ]+\))*)$/i
       end
       links << @template.link_to_if(url, @template.html_escape(name), url)
     end
     links.join("#{delimeter}")
   end
   
   def format_name breadcrumb
     if breadcrumb[:options][:eval] and  breadcrumb[:options][:eval] == true
       eval("\"#{breadcrumb[:name]}\"")
     elsif breadcrumb[:name].class == Symbol
       breadcrumb[:name].to_s.capitalize 
     else
       breadcrumb[:name]
     end
   end
   
   def breadcrumbs_title delimiter = " / ", max_length = 2
     titles = []
     @breadcrumbs[@breadcrumbs.length-max_length..-1].each do |breadcrumb|
       titles << @template.html_escape(format_name(breadcrumb))
     end
     titles.join(delimiter)
   end
   
end

Usage is similar to before, but without the need for :link => [boolean].

eval(not-evil)-d urls work better than before, and accept params now!

You can also eval the title, by using :eval => true when you define the breadcrumb.

breadcrumb 'Order: #{@purchase.id_token}', :eval => true, :except => [:new, :create]

The titles helper now accepts two params: delimiter & max_crumbs. This means you can pick and delimit breadcrumbs from the end of the trail to form your title.

Enjoy :)

Friday, March 19, 2010

I recently had to include a breadcrumb style navigation system on a site I’m working on.

You know the score… Like on a forum:

Home > Random Crap > My new pet narwhal

Anyway, I found a few gems and plugins out there which help out with this, but nothing really nailed it for what I needed.

My requirements were:

  • Simple to implement
  • Smartness. I don’t want to write config files for this.
  • ..but not too smart. I don’t want it to guess, guess wrong and for me to have no control over fixing this.
  • Flexibility. Different breadcrumb trails within the same controller is a must.

I found this solution which I liked, but it didn’t handle dynamic linking so well within my slightly complex setup. That’s to say, I didn’t want the page we’re currently on to be an active link, because this is a bit of a usability faux pas, according to Jacob Nielsen. I don’t remember the exact reasoning, but something a little more elaborate was required.

I based my implementation on the above solution, so it has a few similarities.

Without further ado:

lib/breadcrumb_helper.rb

module BreadcrumbHelper

  def self.included receiver
    receiver.extend ClassMethods
  end

  module ClassMethods    
    def breadcrumb name, options = {}
      before_filter options do |controller|
        controller.send(:breadcrumb, name, options)
      end
    end
  end

  def breadcrumb name, options
    @breadcrumbs ||= []
    
    unless options[:url]
      url = "#{name}_path"
    else
      url = options[:url]
    end 
        
    name = name.to_s.capitalize if name.class == Symbol
      
    if options[:link] != false
      url = eval(url) if url =~ /_path|_url|@/ #ouch, ugly hack.
      params_pos = url =~ /\?/
      if params_pos
        useful_url = url[0..params_pos-1]
      else
        useful_url = url
      end
    
      path_parts = ActionController::Routing::Routes.recognize_path(useful_url, :method => :get)

      if path_parts[:controller] == params[:controller] and path_parts[:action] == params[:action]
        @breadcrumbs << { :name => name }
      else
        @breadcrumbs << { :name => name, :url => url }
      end
    else
      @breadcrumbs << { :name => name }
    end
    
  end
  
  def breadcrumbs delimeter = " / "
     links = []
     @breadcrumbs.each do |breadcrumb|
       links << @template.link_to_if(breadcrumb[:url], breadcrumb[:name], breadcrumb[:url])
     end
     links.join("<span class='delimeter'>#{delimeter}</span>")
   end
   
   def breadcrumbs_title
     @template.html_escape(@breadcrumbs.last[:name])
   end
   
end

in app/controllers/application_controller.rb :

include BreadcrumbHelper
helper_method :breadcrumbs
helper_method :breadcrumbs_title
breadcrumb :home

This allows you to use and in your views, as well as setting up an initial breadcrumb of “Home”, which maps to home_path. If you don’t have a home_path in your routes, you might want to change it to something else.

Usage

breadcrumb :items

Items [links to items_path, /items]

breadcrumb "My pet narwhal", :url => item_path(narwhal)

My pet narwhal [links to item_path(narwhal), /item/42 for example] You could only do this in a place where the object narwhal exists in scope, so you might have to put it into a method in your controller.

breadcrumb "People", :url => "users_path"

People [links to users_path, /users]. If you quote the path, it will be eval’d within the controller, this helps avoid putting your breadcrumb defs within methods.

breadcrumb "Something Nice", :link => false

Self explanatory, I hope. In your view

<%= breadcrumbs %>

becomes Home / Items / My pet narwhal

<%= breadcrumbs ":" %>

becomes Home : Items : My pet narwhal

You can also generate the page title using the last breadcrumb, using

<%= breadcrumbs_title %>

I’m sure this can be simplified and optimised further, but.. Enjoy?

Sunday, January 31, 2010

Adding markdown to your project

A quick one this time. I’ve just gone about implementing the following setup, which is a super easy way to add Markdown support in your views (and, well, anywhere).

We’ll be using the RDiscount gem for this. There are other gems like Bluecloth, but RDiscount has been consistently good for a long time now.

config/environment.rb add:

config.gem "rdiscount"

then install the new gem using “sudo rake:gems install” and restart rails…

…and that’s it. Now you can parse markdown into html like this:

RDiscount.new("My Lovely Markdown").to_html

But, that’s a mouthful and doesn’t look very clean in a view. So, what I did instead was to open up the String class and add a method called markdown which does that for us.

The easiest way to do that and make it available everywhere is to make an initializer.

Make the file “markdown.rb” in config/initializers/ and fill it with:

class String
  def markdown
    RDiscount.new(self).to_html
  end
end

After restarting rails, you will be able to convert markdown like this:

"My String".markdown

Now isn’t that special?

Sunday, September 27, 2009

Validating images with Paperclip

If you don’t already use it, check out Thoughtbot’s Paperclip first.

Doing this is always a huge pain, so I wrote a mixin which makes it super easy. It’s based upon some model code I referenced online before, but with a crucial bug fix relating to a situation where an exception would be raised if you posted a form without selecting an image.

So, without further ado:

validates_as_image

module ValidatesAsImage

  def self.included receiver
    receiver.extend ClassMethods
  end

  module ClassMethods
    def validates_as_image fields

      validates_each fields do |record, attr, value|
        if !value.queued_for_write.empty? and value.to_file
          `identify "#{value.to_file.path}"`
          record.errors.add attr, 'is not a valid image' unless $? == 0
        end
      end
         
    end
  end
  
end

Requires Imagemagick to be installed on your server, and the ‘identify’ command must be in your path. Paste this code into a file in your lib folder and restart the server.

Here’s an example of how to use it:

class Widget < ActiveRecord::Base
  
  include ValidatesAsImage
  
  has_attached_file :photo,
                    :whiny => false,
                    :styles => {  :medium  =>  { :geometry => '300x300#', :format => "png" },
                                      :thumb => { :geometry => '100x100#', :format => "png" } }  
  
  validates_as_image :photo

  def etc
     #.........
  end
end

Note that I have turned the :whiny option on in Paperclip. It’s not strictly necessary, but if it’s on you will get extra, redundant form validation errors, as well as just “Photo is not a valid image.” See the documentation for the whiny option here: has_attached_file.

Happy camping.

Sunday, September 27, 2009

Damn

The itegration between Rails and (I hate to say it) AJAX is really awesome.

I’ve created something that would take an afternoon to make in PHP in about 2 minutes.

Wow.

Wednesday, March 11, 2009

MacRuby

This looks interesting [Apple.com]

Develop OS X-native apps with Ruby + Cocoa bindings? Sweet.

Friday, February 20, 2009

Another Challenge

Okay, so I found another challenge I could do, this time on RosettaCode which deals with file I/O in Ruby.

Basically, there’s a 10,000 line log file to parse and gather some data from it.

This one was really, really simple and took about 5 minutes to do.

license_out = 0
record_out = 0
record_times = []
File.open("licenselog.txt").each do |line|
	if line.include?("OUT")
		license_out += 1
	else
		license_out -= 1
	end
	
	if license_out > record_out
		record_out = license_out
		record_times << line.split(" ")[3]
	end
end

puts "Maximum licenses out: #{record_out} at the following times: "
puts record_times.join("\n")
Sunday, February 15, 2009

Yield!

Wow, this is cool:

def test_block
	puts "Beginning...."
	yield if block_given?
	yield if block_given?
	puts "Ended!"
end

test_block do
	puts "I am being called from within test_block!"
end

Sunday, February 15, 2009

More Euler..

Just finished Project 4.

This isn’t the most concice code, because i functionised it, but it works quite well.

class Numeric
	def is_palindrome?
		s_num = self.to_s
		half = s_num[0..(s_num.length/2)-1].to_s
		s_num == half + half.reverse or
			(s_num.length%2 == 1 and s_num == half + s_num[s_num.length/2,1].to_s + half.reverse)
	end
end


def find_largest_palindrome number_digits

	highest_number = (9.to_s * number_digits).to_i
	lowest_number = (1.to_s + 0.to_s* (number_digits - 1)).to_i
	
	largest = 0
	largest_sqrt = 0

	highest_number.downto(lowest_number).each do |num1|
		highest_number.downto(num1).each do |num2|
			if largest_sqrt * 2 > num1 + num2
				break
			end
		
			if (num1 * num2).is_palindrome? and num1 * num2 > largest
				largest = num1 * num2
				largest_sqrt = Math.sqrt(largest)
			end
		end		
	end
	largest
end

puts "Largest palendrome is #{find_largest_palindrome 3}"
Sunday, February 15, 2009