Articles tagged with plugin

acts_as_audited and ActiveScaffold

plugin December 13 2007

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.

posted by brandon | updated December 13th 06:53 PM | 0 comments

acts_as_ferret will_paginate

plugin August 17 2007

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]
posted by brandon | updated March 14th 10:55 AM | 44 comments

Handling forms with multiple buttons

plugin July 16 2007

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
posted by brandon | updated June 19th 07:19 PM

Firebug lite for all those other browsers

plugin July 02 2007

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
posted by brandon | updated July 2nd 05:26 PM | 0 comments

Revisioning with acts_as_audited

plugin June 18 2007

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.
posted by brandon | updated June 18th 02:23 PM | 19 comments

Geocoding as easy as 1-2...

plugin February 13 2007

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!

posted by brandon | updated June 19th 07:24 PM | 14 comments

Validations on empty (not nil) attributes

plugin February 06 2007

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
posted by brandon | updated June 19th 07:24 PM | 16 comments

User auditing with acts_as_audited

plugin November 19 2006

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.

posted by brandon | 4 comments

Making #composed_of more useful

plugin November 15 2006

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
posted by brandon | updated October 24th 12:02 AM | 9 comments

acts_as_money

plugin November 13 2006

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.

posted by brandon | updated June 19th 07:29 PM | 11 comments

acts_as_audited security update

plugin September 07 2006

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.

posted by brandon | updated December 1st 01:03 AM | 1 comment

acts_as_audited

plugin July 21 2006
acts_as_audited is an Active Record plugin that logs all modifications to your models in an audits table. It uses a polymorphic association to store an audit record for any of the model objects that you wish to have audited. The audit log stores the model that the change was on, the “action” (create, update, destroy), a serialzied hash of the changes, and optionally the user that performed the action.

Auditing in Rails

NOTE: read the caveats section if the following isn’t working.

If you’re using acts_as_audited within Rails, you can simply declare which models should be audited. acts_as_audited can also automatically record the user that made the change if your controller has a current_user method.

class ApplicationController < ActionController::Base
  audit User, List, Item
protected
  def current_user
    @user ||= User.find(session[:user])
  end
end

Caveats

Auditing with user support depends on Rails’ caching mechanisms, therefore auditing isn’t enabled during development mode. To test that auditing is working, start up your app in production mode, or change the following options in config/environments/development.rb:

config.cache_classes = true
config.action_controller.perform_caching = true

Customizing

To get auditing outside of Rails, or to customize which fields are audited within Rails, you can explicitly declare acts_as_audited on your models. The :except option allows you to specify one or more attributes that you don’t want to be saved in the audit log.

class User < ActiveRecord::Base
  acts_as_audited :except => [:password, :credit_card_number]
end

Installation

You can grab the plugin by running:

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

Run the migration generator and migrate to add the audits table.

script/generate audited_migration add_audits_table
rake db:migrate

Upgrading

Those upgrading from version 0.2 need to add 2 fields the audits table:

add_column :audits, :user_type, :string
add_column :audits, :username, :string
posted by brandon | updated June 19th 07:30 PM

acts_as_billable :plugin, :through => :rails

plugin July 13 2006

At Collective Idea, we’re developing several apps that take credit card payments. (We’ve previously blogged about sorting through this whole mine-field of credit card processing.) One of the ideas we came up with early on was using the magic of Ruby and Rails plugins to extract the billing and credit card processing logic into something that is reusable.

We started with ActiveMerchant, a handy little library developed for Shopify by JadedPixel. ActiveMerchant is great for abstracting the credit card processing API, but you still need to figure out what to charge for, how much, how often, and be able to pull up a history of payments.

Welcome to acts_as_billable, a plugin we’re working on that we think will remove some of the complexity from online billing and payments. To illustrate how it will work, here is an example.

  class User &lt; ActiveRecord::Base
    belongs_to :plan
    acts_as_billable :plan, :frequency =&gt; :monthly
  end

  class Plan &lt; ActiveRecord::Base
    has_many :users
  end

When a user signs up for your super-duper-new-app, they can choose a plan. That plan determines what features they get, and how much they pay to use your app. So, the user should be billed (well charged, but chargable just doesn’t sound as nice) each month for the plan.

Another example:

  class User &lt; ActiveRecord::Base
    has_many :registrations
    acts_as_billable :event, :through =&gt; :registrations
  end

  class Registration &lt; ActiveRecord::Base
    belongs_to :user
    belongs_to :event
  end

  class Event &lt; ActiveRecord::Base
    has_many :registrations
  end

A user can register for events, which requires a payment. Users should then be billed for each event that they register for.

So what does acts_as_billable really do? Several things:

  1. It adds temporary fields for the credit card information and kicks off the processing through ActiveMerchant when a record is saved. So when an event is created, if the event costs money, it makes the API calls to process the credit card, returning errors if it fails.
  2. It adds validations for the credit card information, and validates that the credit card processed successfully.
  3. It adds polymorphic associations from the billable class (User in this case) to Exchange (transactions) and Invoice. This gives you access to a record of all the user’s payments.

We’re pretty excited about what this plugin can do so far, and how easy it will make it for us to do more apps that require online payments. We intend to release this plugin as open source when we get it to a usable state. I’m sure there’s a lot we could do with it that we haven’t thought of, so we’d love some feedback.

posted by brandon | updated December 1st 01:07 AM | 17 comments

About

I'm Brandon Keepers, a web application developer that likes beautiful code, valid markup and adherence to standards. As a part of Collective Idea in Holland, Michigan, I practice Agile software development primarily using Ruby on Rails.

-86.103171 42.785037

Contact:

more »

Syndicate