Small State_Machine Helper

Some small helpers to

  • access all states
  • access all events
  • define all status? methods

for rails state_machine plugin (for former acts_as_state_machine).

(this will not work if you got multiple status fields).

Usage

movie.online?
movie.banned?
...
f.select(:status,Movie.states)

Install

#lib/status_helper.rb
module StatusHelper
  def self.included(base)
    base.class_eval do
      def self.states; state_machines['status'].states; end
      def self.events; state_machines['status'].events.keys; end
      states.each {|state| define_method("#{state}?"){status == state} }
    end
  end
end

#your model
class Movie
  include StatusHelpers
end

Custom Form Builders Are Evil

Every Formbuilder i see does the same, building some rows/tables/div structure around the fields it is be called on.

f.text_field => tr td label input

In theory this sounds great, but as soon as one tries to make some real life use of it it breaks down:

  • How do i handle I18N ?
  • How can i change the label text from the default
  • How do i put the checkbox in front of the label
  • How can i output only a field/2 fields in one row

Form builders that always output label+input are unsuited for everyday development!

So what else can we do ?

#app/helpers/application_helper.rb
class ActionView::Helpers::FormBuilder
  def build_translated_label(content)
    if content.class == Array
      label(content[0],content[1])#label + text
    elsif content.class == String
      label('',content)#text
    else content.class == Symbol
      #uses gettext translation, feel free to insert your own...
      label(content,_("#{object.class}|#{content.to_s.capitalize.gsub('_',' ')}"))#label + translation        
    end
  end
  
  def row(label,content)
    @template.content_tag('div',build_translated_label(label) + content.to_s,:class=>'row')
  end
end

Usage

f.row(_('Movie|Website')+'http://', f.text_field(:website))
f.row(:tags, f.text_field(:tag_list)+_('comma divided'))

If someone wants the R-specs, drop me a mail 🙂

Small Spec Helpers

So small but so powerful, i just wanted to share my little time savers, and hope you share your most essential helpers in the comments.

(I use @item as an alias for the current object in all my tests, to make generic tests less painful.)

Small example:

it "renders feedback" do
  expects_find
  get :feedback , :id => @item.to_param
  response.should render_template('edit')
end

Code:

#spec/spec_helper.rb
def mock_create(item,success,para=nil)
  if para
    item.class.expects(:new).with(para).returns item
  else
    item.class.expects(:new).returns item
  end
  item.expects(:save).returns success
end

def mock_update(item,success)
  expects_find(item)
  item.expects(:save).returns success
end

def expects_find(item=nil)
  item ||= @item
  item.class.expects(:find).with(item.to_param).returns(item)
end

The Universal email_token

Activation_code, password_reminder_token and more to come.

Strange code:

before_create :make_activation_token

def make_activation_code
  self.activation_code = Digest::SHA1.hexdigest( ...)
end

def forgot_password
  ...
  password_reset_token=Digest::SHA1.hexdigest(...)
end
#and so on...

Let the mailman handle the postbox keys
Stay with a simple email token. It is updated every time we send any activation/reset/verification mail so no user can perform two action with the same token or say ‘find’ an old token and then request a password reset.

#user.rb
def update_email_token
  update_attribute(:email_token,Digest::SHA1.hexdigest(..)
end

#user_mailer.rb
def setup_email(user)
  user.update_email_token
  ...
end

Generic + Smart – link_to_s link_to_edit link_to_destroy

Building on my work in Separate Rights Management from Controllers i killed all linking-view-logic.

Example

before:
(movie.online? or movie.owner_id==current_user.id) ?
  link_to(movie,movie) : ''
(movie.owner_id==current_user.id) ?
  link_to('edit',movie) : ''

after:
link_to_s(movie)
link_to_edit(movie)

Code

#app/helpers/link_helper.rb
module LinkHelper
  def is_record?(something)
    something.kind_of?(ActiveRecord::Base)
  end

  def can_write?(obj)
    user = current_user || User.new
    user.can_write?(obj)
  end

  def can_read?(obj)
    user = current_user || User.new
    user.can_read?(obj)
  end

  #can we build a link ?
  #true if it is an id / path
  #false if it is an record and i cannot reda/write it
  def is_linkable?(object,rw)
    raise ":r or :w" unless [:r,:w].include? rw
    return false unless object
    return true unless is_record?(object)
    return rw == :r ? can_read?(object) : can_write?(object)
  end

  def link_to_s(object,options={})
    return "" unless is_linkable?(object,:r)
    link_to(object.to_s,object,options)
  end

  def link_to_edit(path_or_object,options={})
    return "" unless is_linkable?(path_or_object,:w)
    if is_record?(path_or_object)
      path_or_object = edit_polymorphic_path(path_or_object)
    end
    link_to("edit",path_or_object,options)
  end

  def link_to_destroy(path_or_object,options={})
    return "" unless is_linkable?(path_or_object,:w)
    link_to( 'X', path_or_object, options.merge(:class=>'destroy'))
  end
end

Please drop a comment if you can think of enhancements, if i can find more uses/methods i will build a new link-helper-plugin 🙂