== Flagger
* Version: 0.9.5
* Release date: 31st May 2007
Do you often find yourself writing repetitive collections of methods on your
models that look like:
class Order < ActiveRecord::Base
def self.paid
find_all_by_paid(true)
end
def self.unpaid
find_all_by_paid(false)
end
def mark_as_paid
update_attribute(:paid, true)
# related tasks
end
def mark_as_unpaid
update_attribute(:paid, false)
# related tasks
end
end
This is good practise, as it keeps your column names wrapped up in the model
in case they need changing, rather than littering your controllers (or other
models) with them. And it increases code legibility, which is also a Good Thing.
The Flagger plugin allows ActiveRecord::Base to give you these methods
without you having to write any code. It uses method_missing hooks to
respond to methods not caught by ActiveRecord::Base's built-in
method_missing code. It is designed to be used with boolean attributes,
that is, attributes that contain +true+/+false+, 1/0 or
"1"/"0" depending on column type. (Although, it is generally
not a good idea to use string/text columns for this type of data. You may encounter
problems if you attempt to do so, depending on which Rails version you are using.)
=== Boolean finders
Flagger allows you to use dynamic finders on boolean attributes without using
+find_all_by_+. So, using the above example, you can happily call Order.paid
to get all your paid orders, without defining the method yourself. In addition,
you can call Order.unpaid, and you will get back all the orders for which
paid == false. The full list of allowed prefixes is:
* +in+, +im+, +un+, +non+, +dis+, +de+, +ir+, and +not_+
If your model has an attribute whose name already begins with one of these,
Flagger will take this into account. For example, TallStory.implausible
returns all the TallStory records for which implausible == true.
You can chop off the prefix as well: TallStory.plausible does what
you'd expect.
Only one prefix will be added to or removed from the attribute name you use to
try and find matching attribute names. Fact.undeniable works if the +Fact+
model has an attribute called +undeniable+, +deniable+, +inundeniable+,
+imundeniable+ ... or +not_undeniable+. Attribute names are always checked in this
order, and Flagger will use the first one that exists. If no attribute match can be
found, the exception raised by ActiveRecord::Base's built-in
method_missing code is reraised.
Attribute names can be chained together in a similar fashion to that allowed by
+find_by_+ and friends. You can use +and+ and +but+ to separate names, so you could write:
orders = Order.paid_and_delivered
orders.first.paid?
#=> true
orders.first.delivered?
#=> true
martinis = Cocktail.shaken_but_not_stirred
martinis.first.shaken?
#=> true
martinis.first.stirred?
#=> false
=== Dynamic markers
Attribute-based markers perform the converse task to boolean finders - they set
boolean attributes rather than retrieving based on them. They mark (flag) records
as having certain properties. Their syntax is very similar to that for finders:
order = Order.find(:all).first
order.mark_as_paid
order.paid?
#=> true
order.mark_as_delivered_but_not_signed_for
order.delivered?
#=> true
order.signed_for?
#=> false
These methods do not save the attributes to the database. If you want to save
the attributes, end the method call with a !.
+mark_as_+ methods detect the column type for each attribute and set values
accordingly. +true+/+false+ are used for :boolean fields, 1/0
for numeric fields, and "1"/"0" for :string/:text fields.
To perform tasks related to flagging, you can define +after_mark_as_+ methods on
your models. Each callback can include one attribute name (with or without prefixes
as appropriate) and can end with a ! to denote whether it should be called
on database saves or not. For example, Order#after_mark_as_unpaid! will be
called after mark_as_unpaid!, mark_as_not_paid! and
mark_as_accepted_but_not_paid!, but not after mark_as_unpaid (no
exclamation mark). All the existing callbacks that apply to a +mark_as_+ call
will be called, but only if a change has been made to the relevant attribute.
=== Compatibility
Flagger has been tested successfully using MySQL 5 with Rails 1.1.6 and 1.2.0
through 1.2.3. Bug reports and test reports for other databases are welcome - my
email address is at the end of this file.
=== Notes
Flagger is an adjusted version of a patch[http://dev.rubyonrails.org/ticket/7735]
I submitted to Rails that didn't make it into the core. The source has been modified
from that version in a few places. Thanks to +tomafro+ for pointing me in the right
direction and plugging holes in my Ruby/Rails knowledge.
Clearly this plugin will not cover all possible use cases for flaggable models.
It is meant to try and provide some generally useful functionality out of the box,
and to eliminate the need to write code for the simpler cases. If it doesn't
quite do what you need, you can always roll your own methods to avoid hitting
+method_missing+.
One potentially useful addition would be support for OR-based finder conditions,
as in Lorry.red_or_yellow, although I suspect that including that would
be excessively complicating things - how would one handle
Lorry.red_and_yellow_or_green_but_not_blue? Does that mean (red && yellow)
|| (green && !blue), or red && (yellow || green) && !blue?
Flagger is (c) 2007 James Coglan and is released under the terms of the MIT license.
Contact: james at jcoglan dot com