Is this your first visit? You may want to subscribe to the feed.

Articles tagged with plugin

acts_as_audited and authlogic

For those using authlogic that have had issues with auditing your User model, version 1.0.2 of acts_as_audited should cure your woes.

All you need to do is exclude the last_request_at and perisable_token fields from being audited. We also excluded a few other fields that don’t need to be audited:

class User < ActiveRecord::Base
  acts_as_audited :except => [
    :crypted_password, 
    :persistence_token,
    :single_access_token,
    :perishable_token,
    :last_request_at,
  ]
end
Code: plugin Oct 29, 2009 ● updated Oct 29, 2009 0 comments

awesome_nested_set: making nested sets cool

Yes, I’m making the assertion that preordered tree traversal is now cool. And I don’t mean just “pocket protector” cool, because it’s always been that, but now it’s “show your friends” cool.

For those that have no idea what I’m talking about, and don’t really care, but still want to be cool, skip to the next section. For all three of you that want to understand all the gory details, check out this MySQL DevZone article on managing hierarchical data.

What are you talking about?

I’m talking about putting hierarchical data into a relational database, and a plugin to make that easier. There are lots of reasons for trying to do this: organizational structures, genealogies, taxonomies, nested pages of a website, etc. It’s kinda like putting a square peg into a round hole, except that the square peg is made out of Play-Doh, so we can force it through the hole anyway, and we just have a little extra mess to deal with.

There were several Active Record plugins out there that tried to clean up the mess, but they were either buggy or incomplete.

We created awesome_nested_set to try to remedy that.

What makes this so awesome?

There’s a lot of things that makes this awesome, but my personal favorite is that awesome_nested_set makes use of Rails 2.1’s named_scope features1, so most of the nested set methods return a scope that works as a finder. You can call find methods on it or access other named scopes.

class Department < ActiveRecord::Base
  acts_as_nested_set
  named_scope :in_need_of_review, :lambda => {{
    :conditions => {:reviewed_at > 1.year.ago
  }}
end

chancellor = Department.create(:name => 'Chancellor')
aa = Department.create(:name => 'Academic Affairs').move_to_child_of(chancellor)
Department.create(:name => 'Admissions').move_to_child_of aa
Department.create(:name => 'Student Services',
  :reviewed_at => 3.months.ago).move_to_child_of aa

chancellor.descendants.in_need_of_review
rogue = chancellor.descendants.all(:conditions => 'manager_id IS NULL')

There’s lots more info in the README on GitHub, so check it out. Let us know if you have any suggestions or feedback.

  1. It also backports named_scope for those still on Rails 2.0
Code: plugin Nov 17, 2008 ● updated Nov 17, 2008 3 comments

acts_as_audited in development

Thanks to metatribe, a huge annoyance in acts_as_audited is now fixed: it works in development mode. Due to it’s dependance on Rails’ cache sweepers, which are only enabled when caching is, acts_as_audited didn’t work in development mode.

metatribe has a crafty solution, which took me a few minutes to believe it would even work. So pull the latest version and happy auditing.

Code: plugin Oct 12, 2008 ● updated Oct 14, 2008 1 comment

Merging Active Record models

We’ve been working on a project that involves importing a massive amount of data from multiple sources. The data is somewhat complicated, so we occasionally end up with duplicate records that need merged together. The data is highly normalized, so there are a bunch of associations that also need merged. If you’ve ever done this by hand, you know how painful it can be.

To alleviate that pain, I introduce you to merger, a Rails plugin for merging Active Record models.

@person.merge!(Person.find_all_by_email(@person.email))

The plugin is pretty simple right now. All it does is:

  1. Given a set of records, picks the oldest record (the one with the lowest id) as the one to keep
  2. Moves any associated has_many and habtm records from the duplicates to the record that is being kept
  3. Deletes the duplicate records

We intend to add a lot to it, including:

  • Strategies for choosing which record to keep
  • Strategies for merging the individual attributes of the records
  • Recursively merge associations based on certain attributes
  • Options for what to do with the duplicate records

Check it out on Github and let us know what you think.

  1. Photo adapted from http://flickr.com/photos/xrrr/2478140383/
Code: plugin Aug 21, 2008 ● updated Oct 14, 2008 0 comments

acts_as_audited and ActiveScaffold

Several people have reported that acts_as_audited does not work with ActiveScaffold. I don’t use ActiveScaffold, so I had no motivation to fix it. But thanks to a tip from Aaron, this has now been fixed.

To make acts_as_audited work with ActiveScaffold, only enable auditing for only the :create, :update, and :destroy actions.

class ApplicationController < ActionController::Base
  audit User, Thing, :only => [:create, :update, :destroy]
end

I’m not sure exactly why it breaks or why this fixes it, but from this thread it sounds like ActiveScaffold just doesn’t support polymorphic associations, which is what acts_as_audited uses to associated audits with models.

Code: plugin Dec 13, 2007 ● updated Dec 13, 2007 0 comments

acts_as_ferret will_paginate

Update: This is not needed with recent versions of acts_as_ferret.

Here’s a little nugget to add to acts_as_ferret to make your searches paginate with will_paginate.

module ActsAsFerret
  module ClassMethods
    def paginate_search(query, options = {})
      page, per_page, total = wp_parse_options(options)
      pager = WillPaginate::Collection.new(page, per_page, total)
      options.merge!(:offset => pager.offset, :limit => per_page)
      result = find_by_contents(query, options)
      returning WillPaginate::Collection.new(page, per_page, result.total_hits) do |pager|
        pager.replace result
      end
    end
  end
end

Updated from Behrang’s comment based on changes to will_paginate.

There was a slight challenge in that will_paginate expects that you do one query to get the count, create a new collection object based on that count, and then perform the actual search. But acts_as_ferret does it all in one method call, so I have to create a temporary collection object to get the offset, then do the search and create the collection object. It’s a little messier than it needs to be, but it works.

Product.paginate_search params[:q], :page => params[:page]
Code: plugin Aug 17, 2007 ● updated May 07, 2009 47 comments

Handling forms with multiple buttons

With an app that I’m working on, the client wants to have several buttons for doing different actions on every form: “Save”, “Save & Continue Editing”, “Save & Add Another”, and “Cancel”. HTML only allows you to have one action defined per form (instead of per button), and Rails pretty much assumes that if you submit a request to a specific action, you expect to execute it.

So, instead of littering my code with all kinds of if/else statements, I decided to wrap up the functionality into a little plugin that makes it a little cleaner.

with_action is a respond_to style helper for executing different blocks based on presence of certain request parameters.

def create
  with_action do |a|
    a.cancel { redirect_to articles_path }
    a.any do
      @article = Article.new(params[:article])
      if @article.save
        a.save { redirect_to article_path(@article) }
        a.edit { redirect_to article_path(@article) }
        a.approve do
          @article.approve!
          redirect_to article_path(@article)
        end
      else
        render :action => 'new'
      end
    end
  end
end

A block is invoked if a parameter with the same name exists and is not blank. Here is an example of the form that submits to this action:

<%= submit_tag 'Save', :name => 'save' %>
<%= submit_tag 'Save & Continue Editing', :name => 'edit' %>
<%= submit_tag 'Save & Approve', :name => 'approve' %>
<%= submit_tag 'Cancel', :name => 'cancel' %>

If an any block is present and no parameter that matches one of the other blocks, it is called by default, otherwise the first block will be called. The any block is the only one that can have nesting and be called multiple times.

I realize this could be considered trivial, but this looks a lot cleaner than the alternative, and more importantly, gave me a way to standardize on how I handle these actions. Let me know what you think.

http://github.com/collectiveidea/with_action
Code: plugin Jul 16, 2007 ● updated Jun 19, 2008 17 comments

Firebug lite for all those other browsers

If for some reason you use one of those other browsers, then you don’t have the pleasure of using Firebug for debugging (Safari nuts, I know you secretly debug your javascript and stylesheets with Firebug in Firefox, despite it “not looking like a Mac app”).

Fortunately for you, there’s Firebug Lite. From the website:

Firebug is an extension for Firefox, but what happens when you need to test your pages in Internet Explorer, Opera, and Safari? If you are using console.log() to write to Firebug’s console, you’ll wind up with JavaScript errors in these other browsers, and that’s no fun.

The solution is Firebug Lite, a JavaScript file you can insert into your pages to simulate the Firebug console in browsers that are not named “Firefox”.

To ease the pain of debugging Javascript in those other browsers, I’ve thrown together a little plugin that automagically includes Firebug Lite into your application in development mode. All you need to do is install the plugin and include the default javascripts in your layout (which you’re probably already doing):

<%= javascript_include_tag :defaults %>

Just hit F12 (Ctrl+F12 on Mac) to open Firebug Lite on any page, or jump the focus directly to the command line with Ctrl+Shift+L (or ⌘+Shift+L on Mac).

http://source.collectiveidea.com/public/rails/plugins/firebug
Code: plugin Jul 02, 2007 ● updated Jul 02, 2007 0 comments

Revisioning with acts_as_audited

When I first created acts_as_audited over a year ago, I intended to add versioning/revisioning capabilities, but found I never really had a need. Well, it just so happened that I finally had a need on an app that we are working on. So, acts_as_audited now allows you to revert back to previous revisions.

You can get all the revisions of a model:

article.revisions.each |revision|
  puts revision.class   #=> Article
  puts revision.version #=> 7, 6, 5, 4...
end

or a specific revision:

revision = article.revision(5)
puts revision.title     #=> "Old Title"

or the previous revision. Reverting is as simple as saving a revision:

previous = article.revision(:previous)
previous.save           # revert to previous revision

See the original post for more details about installing and using the plugin.

How is this different from acts_as_versioned?

I’ve never used acts_as_versioned1 (because I’ve never had a need to do versioning), but I do know that it has a few annoying limitations: 1) it requires a separate table (and model) for each model that you want to version, and 2) it saves every attribute, even if it’s not changed.

acts_as_audited already stories all the changes (and only the changes) in one table, so adding versioning was rather trivial. Revisions are created by walking backward through the audits, collecting the changes, and returning an instance of the model.

Upgrading

To use the versioning support, you must add a version field in the audits table:

add_column :audits, :version, :integer, :default => 0

Note: if you already have audit records, the version field will have to be initialized.

Feedback

This code is fairly specific to what I needed, so if you’re using it, I would love to hear how it is working for you. Suggestions and patches welcome.

  1. acts_as_audited was actually one of the first plugins that I wrote, so I looked at the best code I could find as an example for writing a plugin, which happened to be acts_as_versioned. Kudos to technoweenie.
Code: plugin Jun 18, 2007 ● updated Jun 18, 2007 25 comments

Geocoding as easy as 1-2...

3?, nope there is no 3. Geocoding as easy as 1-2!

  1. Create your models
  2. Find them!
event = Event.create :street => "777 NE Martin Luther King, Jr. Blvd.",
  :city => "Portland", :region => "Oregon", :postal_code => 97232

# how far am I from RailsConf 2007?
event.distance_to "49423" #=> 1807.66560483205

# Find our new event, and any other ones in the area
Event.find(:all, :within => 50, :origin => "97232")

# Find the nearest restaurant with beer
Restaurant.find(:nearest, :origin => event, :conditions => 'beer = true')

I know you’re excited, so instead of blabbing on-and-on, FAQ-style:

How are you performing this voodoo?

A slick new plugin called acts_as_geocodable.

How do I get it?

script/plugin install -x git://github.com/collectiveidea/acts_as_geocodable.git

How do I set it up?

Its all in the README.

Why did you write this when there’s already several geocoding plugins?

The Ruby geocoding space is pretty fragmented right now. There’s all kinds of geocoders available, and none of them provide everything. We’re determined to fix that with Graticule and acts_as_geocodable.

We believe that all the heavy lifting of geocoding, distance calculations, etc., should be left to a gem, and only code that is directly related to Rails should be a Rails plugin. Even more, all the different geocoders should be available in the same package and have the same interface. A plugin should then be built on top of the gem that makes your apps geo-capabilities seem like voodoo.

We started our own projects because we didn’t think anyone else got it right. Recently, Bill Eisenhaur and Andre Lewis did a pretty good job, and we borrowed some of their ideas, but I still have issues with their approach and code, mainly that it’s all tied up into a Rails plugin.

Did you get it right?

I think we have a great foundation. It’s not perfect, nor does it have all the features of the other packages, but I think it is well architected.

Why don’t you work together?

Excellent idea! We would love to.

How does this fit in with JWZ’s use case?

(Why are you asking this?) A third of the problem is knowing where, a third is when, and a third is who. You’re on your own for the who and when.

Happy geocoding!

Code: plugin Feb 13, 2007 ● updated Jun 19, 2008 15 comments

Validations on empty (not nil) attributes

One of the first problems that I ran into when I started using Rails was trying to validate the format of attributes that aren’t required. Most of the validations have an :allow_nil option, but the problem is that when a form is submitted with empty form fields, those fields are empty strings instead of nil values. So the validation fails because the attribute is not nil.

For example, here’s a Person model with a validation on :social_security_number, an optional attribute:

class Person < ActiveRecord::Base
  validates_format_of :social_security_number,
    :with => /\d{3}[-]?\d{2}[-]?\d{4}/, :allow_nil => true
end

When this model is used in a form, validation will fail if the social security number field is left blank, even though :allow_nil is set to true.

The solution

It turns out that the solution is really simple: a before_validation callback that just goes through and sets all the empty attributes to nil.

class ActiveRecord::Base
  before_validation :clear_empty_attrs
protected
  def clear_empty_attrs
    @attributes.each do |key,value|
      self[key] = nil if value.blank?
    end
  end
end

I’ve packaged this little nugget into a plugin. Install it and go on your merry validating way.

script/plugin install -x git://github.com/collectiveidea/clear_empty_attributes.git
Code: plugin Feb 06, 2007 ● updated Jun 19, 2008 16 comments

User auditing with acts_as_audited

Thanks to Chaz for pointing out the section in the Rails Recipes book about using Rails’ cache_sweeper to implement auditing. I’ve updated acts_as_audited so that it can quickly and easily audit modifications to your models along with the user that made the change. Check out the original post for more information and examples of how to use it.

Code: plugin Nov 19, 2006 4 comments

Making #composed_of more useful

Update: my patch finally got added to edge rails.

Active Record allows you to abstract fields into an aggregate object by using the composed_of declaration. This is handy, but the current implementation can be a real pain.

The first and somewhat trivial issue is the composed_of declaration builds a string and evals it to define the attribute accessors. It’s not a big deal, but it’s dirty. The second issue is that aggregate objects are not easy to manipulate in Rails, especially in forms.

Fixing it

So, I’ve written a plugin that overrides the Active Record implementation of composed_of, which allows you to specify a block to convert incoming parameters to the correct type.

Personally, this has been most helpful when using the Money gem:

class Account < ActiveRecord::Base
  composed_of :balance, :class_name => "Money", :mapping => %w(cents cents) do |amount|
      amount.to_money
  end
end

If #balance= receives anything besides a Money object, it will call the block to try to convert the parameter to a Money object.

>> account = Account.new :balance => 100
>> account.balance
=> #&lt;Money:0x2612770 @cents=10000, @currency="USD"&gt;

And now it can transparently be used in forms:

  <%= text_field :account, :balance %>

This can even be used for more advanced aggregations:

class User < ActiveRecord::Base
  composed_of :address, :class_name => "Address"
        :mapping => [%w(street street), %w(city city), %w(state state), %w(zip zip)] do |addr|
    Address.new(addr[:street], addr[:city], addr[:state], addr[:zip])
  end
end

A user can now be created from a hash:

  User.new(:address => {:street => "123 A Street", :city => "Somewhere", :state => "NO", :zip => 12345})

I didn’t quite realize all the implications of this extension until I was writing up the docs for this plugin. Active Record magically does type casting for a limited set of types, namely dates and numbers. But this essentially allows you to have a form of type casting for any attribute. Interesting…

Installation

This has also been submitted as a patch to the Rails trac but hasn’t been accepted yet.

  script/plugin install http://source.collectiveidea.com/public/rails/plugins/composed_of_conversion
Code: plugin Nov 15, 2006 ● updated Oct 24, 2007 9 comments

acts_as_money

At Collective Idea, we often try to take pieces of code that we use often and extract them into plugins. We’re trying to get better about making them available, so, here we go:

acts_as_money is a fairly trivial plugin that has proved to be very useful. It just makes it a little easier to work with the money gem.
class Product < ActiveRecord::Base
  money :price
end

This assumes that there are 2 columns in the database, cents (integer) and currency (string). These fields can be changed by setting the :cents and :currency options. To use the default currency, you can simple set :currency option to false and not have a currency field.

class Room < ActiveRecord::Base
  money :rate, :cents => :rate_in_cents, :currency => :rate_currency
  money :discount, :cents => :discount_in_cents, :currency => false
end
acts_as_money also incorporates a patch to composed_of that I submitted to core that allows you to pass a String, Fixnum, Float or Money object as a parameter to the setter, and it will call #to_money to convert it to a Money object. This makes it convenient for using money fields in forms.
r = Room.new :rate => "100.00"
r.rate                            # returns &lt;Money:0x249ef9c @currency="USD", @cents=10000&gt;

Installation

script/plugin install git://github.com/collectiveidea/acts_as_money.git

Hope you find this useful. I know I’ve been using it in any app that handles money, and it’s been a time saver. Allowing me to use a composed_of field in forms has been extremely helpful.

Code: plugin Nov 13, 2006 ● updated Jun 19, 2008 22 comments

acts_as_audited security update

Thanks to Michael Schuerig for pointing out that malicious users could unassociate your audit records due to the use of has_many in acts_as_audited. has_many :audits creates an attribute accessor called audit_ids on the model objects that you declare acts_as_audited, which could allow users to pass an array of ids that would overwrite the actual audit records.

This has been fixed by adding attr_protected :audit_ids, which protects it from mass assignment. If you’re not using SVN externals, make sure you get the latest version.

Code: plugin Sep 07, 2006 ● updated Dec 01, 2006 2 comments

Subscribe

Browse by Tag