Rails plugins and libraries / Model-level
From WhyNotWiki
[edit] [Model-level (category)]
http://wiki.rubyonrails.org/rails/pages/Model+Extensions
[edit] SemanticAttributes
| Rating: | ![]() |
|---|---|
| Categories/Tags: | [Validations (category)][ActiveRecord / Attributes (category)]
|
| Project/Development: | http://code.google.com/p/semanticattributes/
|
| Description: | Provides meaningful model attributes for the Ruby on Rails world.
|
| Authors: | Lance Ivy |
| Readiness: | not even in beta
|
http://code.google.com/p/semanticattributes/.
ActiveRecord's attributes are not very friendly to programs that want to automatically and intelligently adapt behavior based on column types. Even once the dev has added validations to the attributes, other plugins are unable to reflect on the attributes and customize their behavior.The basic problem is that attributes in ActiveRecord have no semantic meaning. Validations only describe what to do with a field, not what the field means. But if you know what the field means, then you (and other plugins) can easily infer what to do! So the built-in validations are really tackling the problem backwards.
It sounds impossible to describe the meaning every attribute may have, but if we accept the 80/20 guideline then we can package up 20% of the descriptions that classify 80% of the attributes, and provide a way for devs to build the rest as needed.
Consider how common the following fields are:
- phone number
- email address
- url
- filename
- zip code
- us_state
Given semantically meaningful descriptions of these (and other) fields, we can not only automatically generate validation logic but also provide methods for standard database/human formatting and provide a reflection API for other plugins (e.g. ActiveScaffold).
Some work has been done in this field already, but the other projects don't seem to realize their entire potential. For comparison, see:
[edit] composed_of_conversion
| Source code: | http://source.collectiveidea.com/public/rails/plugins/composed_of_conversion
|
|---|
Brandon (2006-11-15). Making #composed_of more useful (http://www.opensoul.org/2006/11/16/making-code-composed_of-code-more-useful).
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.
[edit] 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 endIf #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 => #<Money:0x2612770 @cents=10000, @currency="USD">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 endA 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…
[edit] has_many_polymorphs
| Homepage: | http://blog.evanweaver.com/articles/2006/06/02/has_many_polymorphs |
|---|---|
| Source code: | svn://rubyforge.org/var/svn/polymorphs/has_many_polymorphs |
| Project/Development: | http://rubyforge.org/projects/polymorphs/ |
| As listed in other directories: | http://rubyfurnace.com/plugins/has_many_polymorphs
|
| Authors: | Evan Weaver
|
http://blog.evanweaver.com/pages/has_many_polymorphs
This plugin for ActiveRecord gives you the ability to define easy-to-use self-referential polymorphic associations with full array-like behavior (.push, .delete, <<) in your models.
“Polymorphic” means an association can freely point to any of several disparate model classes, instead of being tied to one particular class.
Example [1]:
# this is the (typed) class that accepts the (untyped) polymorphic objects as children class Petfood < ActiveRecord::Base has_many_polymorphs :eaters, :from => [:dogs, :cats, :birds] end # this is the join table class EatersPetfood < ActiveRecord::Base belongs_to :petfood belongs_to :eater, :polymorphic => true end # this is an example of one of the child classes class Dog < ActiveRecord::Base # nothing end petfood.eaters << Cat.create petfood.cats.size # => 4 petfood.eaters.size # => 6
[edit] ↓ Association Scope
| Source code: | http://soen.ca/svn/plugins/rails/association_scope/trunk/
|
|---|---|
| As listed in other directories: | http://rubyfurnace.com/plugins/association_scope
|
http://rubyfurnace.com/plugins/association_scope
Allows scoping of association definitions. - Makes your code more DRY - Works with belongs_to, has_one, has_many, has_and_belongs_to_many
[edit] Example
class Post < ActiveRecord::Base with_association_scope :order => "created_at DESC", :dependent => :delete_all do # Both of these are ordered by created_at has_many :posts has_many :comments # And yes we can override default behaviour has_many :milestones, :order => "priority" end end
[edit] :dependent => :protect option
http://rubyfurnace.com/plugins/%3Adependent_%3D%26gt__%3Aprotect_opti
Adds a new option :protect for the parameter :depends from has_many method. This option forbids destroying records with associated records in a association created with :dependent => :protect option, more or less like ON DELETE RESTRICT SQL statement. If you try to destroy a record with associated records it will raise a ActiveRecord::ReferentialIntegrityProtectionError (defined also in this plugin).
- http://rubyfurnace.com/plugins/%3Adependent_%3D%26gt__%3Aprotect_opti
- Repository Path: http://svn.ruido-blanco.net/dependent_protect/trunk
- Homepage: http://ruido-blanco.net/blog/rails-dependent-protect-plugin-english
[edit] [Data formats (category)][CSV (category)]: convertible_to_csv
http://rubyfurnace.com/plugins/convertible_to_csv
class Customer < ActiveRecord::Base acts_as_convertible_to_csv :header => true, :fields => %w(id firstname lastname email_address) endCustomer.find(:all).to_csv Customer.find(:first).to_csv
- Repository Path: svn://rubygreenblue.com/rubygreenblue_opensource/convertible_to_csv/trunk
- Homepage: http://rubygreenblue.com/project/convertible_to_csv
Comments:
- Doesn't seem good that you have to / can only specify the list of fields to include once per model.
[edit] [Data formats (category)][CSV (category)]: to_csv
http://rubyfurnace.com/plugins/to_csv
This plugin provides support for responding to the CSV format, as well as exporting a collection of ActiveRecord objects to CSV.
You can simply take a collection of ActiveRecord objects and apply the to_csv method to them:
@users = User.find(:all) @users.to_csv
- Installation command: ./script/plugin install -x http://svn.integralserver.com/plugins/to_csv
- http://rubyfurnace.com/plugins/to_csv
- Repository Path: http://svn.integralserver.com/plugins/to_csv
- Homepage: http://blog.integralimpressions.com/articles/2006/09/01/tocsv-plugin
[edit] cached_model
http://seattlerb.rubyforge.org/cached_model/
- CachedModel stores Rails ActiveRecord objects in memcache allowing for very fast retrievals. CachedModel uses the ActiveRecord::Locking to ensure that you don‘t perform multiple updates.
- CachedModel only accelerates simple finds for single rows.
[edit]
ScopedProxy
| Categories/Tags: | [with_scope (category)] |
|---|---|
| Homepage: | http://www.jackchristensen.com/article/4/scopedproxy-plugin-for-ruby-on-rails
|
| As listed in other directories: | http://rubyfurnace.com/plugins/scopedproxy |
| Description: | ScopedProxy uses with_scope and proxy objects to make it easy to find and count different types of records.
|
| Authors: | Jack Christensen
|
Example [2]
class Person < ActiveRecord::Base scoped_proxy :female, :conditions => 'gender = "female"' scoped_proxy :single, :conditions => 'married = 0' # ... end Person.single.find :all Person.single.female.find :all # They can also be chained together.
[edit] [Model-level (category)]: [Pagination (category)]
Some might argue that this is a controller-level feature, but I don't think so. Half the challenge is getting it to limit your database query so that it returns the correct page of rows and only those rows...
[edit] Paginator gem
"This is an excellent gem and is a good display of using ruby correctly for accomplishing the concept of pagination." (found on [3], credited to [4])
"You instantiate a Paginator object, telling it how many records there are and how many you’d like per page. You also pass a block that will be used to return the records for a page, given the record offset (and the number of records per page—which you just passed into the constructor—as a convenience)." ([5])
PER_PAGE = 20
@pager = ::Paginator.new(Foo.count, PER_PAGE) do |offset, per_page|
Foo.find(:all, :limit => per_page, :offset => offset)
end
@page = @pager.page(params[:page])
[edit] paginating_find
| Categories/Tags: | [Pagination (category)] |
|---|---|
| Homepage: | http://cardboardrocket.com/pages/paginating_find |
| Source code: | http://svn.cardboardrocket.com/paginating_find
|
| As listed in other directories: | http://rubyfurnace.com/plugins/paginating_find
|
http://rubyfurnace.com/plugins/paginating_find
Got 15,842 records that you'd like to export to a file? Using the standard Rails ActiveRecord::Base#find method will load all 15,842 into memory all at once and return them all in an array. If your app is running on a shared host, or if you?re keeping your app on a memory budget, this is a big problem for you. So you could load each record one by one, but that'll kill your DB server. Wouldn't it be sweet if #find could return an Enumerable that would load your records in batches of say 1,500 records? Well with my new nifty-jifty paginating_find plugin, it can.
[edit]
acts_as_breadcrumbs
| Homepage: | http://breadcrumbs.rubyforge.org/ http://actsasflinn.com/articles/2007/05/02/acts_as_breadcrumbs |
|---|---|
| Source code: | svn://rubyforge.org/var/svn/breadcrumbs/trunk/acts_as_breadcrumbs
|
| As listed in other directories: | http://agilewebdevelopment.com/plugins/acts_as_breadcrumbs |
| Description: | This plugin uses an acts_as_tree model and creates a breadcrumbs trail attribute using a base attribute.
|
| Authors: | Flinn Mueller - actsasflinn.com
|
class WebPage < ActiveRecord::Base
include ActionView::Helpers::TagHelper
acts_as_tree
acts_as_breadcrumbs(:attr => :url, :basename => :slug, :separator => "/")
acts_as_breadcrumbs(:basename => :link, :separator => " > ")
# from urlnameable
def slug
t = self.title.to_s.downcase.strip.gsub(/[^-\s[:alnum:]]/, ’’).squeeze(’ ‘).tr(’ ‘, ‘‘)
(t.blank?) ? ’_’ : t
end
def link
content_tag ‘a’, self.title, :href => "#{self.url}"
end
end
baz = WebPage.create(:title => "Baz", :parent => bar)
baz.url #=> ‘foo/bar/baz’
baz.breadcrumbs #=> ’<a href="foo">Foo</a> > <a href="foo/bar">Bar</a> > <a href="foo/bar/baz">Baz</a>’
[edit] [Model-level (category)]: [acts_as plugins (category)]
(This categorization has a rather low order of precedence. I'm not even sure if it's a very good category to have, since there are some plugins that certainly work similarly to some of these, but don't have "acts_as" in their name (convertible_to_csv could have just as easily been called acts_as_convertible_to_csv ... except that would be rather verbose; cached_model could have been called acts_as_cached_model; ...). Should they be included here too?)
[edit] Why they're cool
Bruce Tate (2007-03-13). Crossing borders: Extensions in Rails: The anatomy of an acts_as plug-in (http://www-128.ibm.com/developerworks/java/library/j-cb03137/index.html).
If you've spent any time at all with Rails, you've doubtless noticed the
acts_ascommands in ActiveRecord. Though ActiveRecord deals with persistence, you often want to add behavior to your [models] beyond database storage and retrieval. For example, by using acts_as_tree, you can add tree-like behavior to a class with a parent_id attribute. By saying nothing more than acts_as_tree in your ActiveRecord model, you can dynamically add methods to manage the tree, such as methods that retrieve the parent record or child record. Over the past month, I've been able to find Rails plug-ins to handle voting, versioning, Ajax, composite keys, and all manner of features that basic Rails doesn't support.Using the plug-in, I can decorate my class object directly, using a DSL to represent the various states, transitions between them, and the events that fire those transitions. [...]
...
You might think it would be simpler just to build a state machine and use it as a library. The acts_as plugin is much nicer. It [
acts_as_state_machine] lets you effectively add a state-machine column to your database.Other plug-ins let you do versioning, build in audit histories, handle images, and perform hundreds of other simple tasks, just as if those tasks were a seamless integration between the Rails environment and the database.
[edit] ActsAsScheduled
| Project/Development: | http://code.google.com/p/actsasscheduled/
|
|---|---|
| Description: | Plugin for Ruby on Rails that enables scheduling of a model
|
| Authors: | Lance Ivy |
| Readiness: | not even in beta
|
http://code.google.com/p/actsasscheduled/.
Lets you plot some model or record into the future. Lets you define the record as a repeated event. Provides an API to let you navigate the future occurrences.
[edit] acts_as_geocodable
| Have used: | no |
|---|---|
| Rating: | ![]() |
| Categories/Tags: | [Geocoding (category)] |
| Documentation: | Readme |
| Source code: | http://source.collectiveidea.com/public/rails/plugins/acts_as_geocodable
|
brandon (2007-02-13). Geocoding as easy as 1-2... (http://www.opensoul.org/2007/2/13/geocoding-as-easy-as-1-2).
...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')...
[edit] 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.
[edit] 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 (category)].
[edit] Why don’t you work together?
Excellent idea! We would love to.
[edit]
acts_as_cached
| Categories/Tags: | [acts_as plugins (category)] |
|---|---|
| Homepage: | ?
|
DRY association sweeping with acts_as_cached (http://blog.methodmissing.com/2007/1/11/dry-association-sweeping-with-acts_as_cached).
Cherry picking exactly which models and associations to cache with Acts as Cached is dead simple.However, cached object associations quickly result in repetitive sweeping snippets.
...
But there's a better way...
Add to acts_as_cached's InstanceMethods: def expire_cache_with_associations( *associations_to_sweep ) ((self.class.cache_options[:include] || []) + associations_to_sweep).flatten.uniq.compact.each do |assoc| macro = self.class.reflect_on_association(assoc.to_sym).macro macro == :has_many ? self.send(assoc.to_sym).each{|r| r.expire_cache unless r.nil? } : self.send(assoc.to_sym).expire_cache unless self.send(assoc.to_sym).nil? end expire_cache endclass Address < ActiveRecord::Base acts_as_cached :version => 1, :include => [:country] belongs_to :addressable, :polymorphic => true belongs_to :country, :class_name => 'Globalize::Country', :foreign_key => 'country_id' after_save :cache_sweeper before_destroy :cache_sweeper protected #sweep the related addressable entity def cache_sweeper; expire_cache_with_associations(:addressable) end end
[edit]
acts_as_versioned
| Categories/Tags: | [acts_as plugins (category)]
|
|---|---|
| Source code: | http://svn.techno-weenie.net/projects/plugins/acts_as_versioned/ |
| Project/Development: | http://rubyforge.org/projects/ar-versioned |
| As listed in other directories: | http://agilewebdevelopment.com/plugins/acts_as_versioned |
| Description: | This library adds simple versioning to an ActiveRecord module. |
| Depends on: | ActiveRecord
|
[6] kpr 1 Jun 2006
This plugin does not work at all with join tables.
[edit]
acts_as_audited
| Categories/Tags: | [acts_as plugins (category)] |
|---|---|
| Homepage: | http://opensoul.org/2006/07/21/acts_as_audited/ |
| Source code: | http://source.collectiveidea.com/public/rails/plugins/acts_as_audited
|
| As listed in other directories: | http://rubyfurnace.com/plugins/acts_as_audited
|
http://opensoul.org/2006/07/21/acts_as_audited/
acts_as_audited is an Active Record plugin that logs all modifications to your models in an
auditstable. 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 serialized hash of the changes, and optionally the user that performed the action.class ApplicationController < ActionController::Base audit User, List, Item end
brandon (2007-06-18). Revisioning with acts_as_audited (http://www.opensoul.org/2007/6/18/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... endor 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 revisionSee the original post for more details about installing and using the plugin.
...
[edit] 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.
[edit] 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.
[edit] acts_as_audited / acts_as_audited comparison
brandon (2007-06-18). Revisioning with acts_as_audited (http://www.opensoul.org/2007/6/18/revisioning-with-acts_as_audited).
[edit] 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.
...
[edit] acts_as_changed
| Categories/Tags: | [monkey patches (category)]
|
|---|---|
| Source code: | http://svn.viney.net.nz/things/rails/plugins/acts_as_changed/
|
| As listed in other directories: | http://rubyfurnace.com/plugins/acts_as_changed |
| Description: | Specifies only the changed fields when updating the database record.
|
http://svn.viney.net.nz/things/rails/plugins/acts_as_changed/lib/acts_as_changed.rb
ActiveRecord::Base.send(:include, ActiveRecord::Acts::Changed)
[edit]
ActsAsList
acts_as_list
| Part of: | Rails
|
|---|---|
| Documentation: | * http://www.rubyonrails.org/api/classes/ActiveRecord/Acts/List/ClassMethods.html |
| Source code: | /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/acts/list.rb
|
[edit] How it works: insert_at
/usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/acts/list.rb
def insert_at(position = 1)
insert_at_position(position)
end
def insert_at_position(position)
remove_from_list
increment_positions_on_lower_items(position)
self.update_attribute(position_column, position)
end
def remove_from_list
decrement_positions_on_lower_items if in_list?
end
def decrement_positions_on_lower_items
return unless in_list?
acts_as_list_class.update_all(
"#{position_column} = (#{position_column} - 1)", "#{scope_condition} AND #{position_column} > #{send(position_column).to_i}"
)
end
# This has the effect of moving all the lower items down one.
def increment_positions_on_lower_items(position)
acts_as_list_class.update_all(
"#{position_column} = (#{position_column} + 1)", "#{scope_condition} AND #{position_column} >= #{position}"
)
end
Sometimes it's hard to read past all the abstractions (metaprogramming, methods that call many other methods, etc.) and see what's actually going on. This was one of those cases (it was for me, anyway). A couple specific examples that let me see what was going on would just help me tremendously to get a picture in my mind of what's going on and how this code works.
So if we started with this list:
# 1. lilac
#(2. pansy)
# 3. daffodil
# 4. rose
and applied pansy.insert_at(1), then this is sort of how the transformation would take place:
pansy.insert_at_position(position = 1)
(self.position == 2)
remove_from_list
decrement_positions_on_lower_items
update_all(
"position = (position - 1)", "position > 2"
)
# 1. lilac
# 2. daffodil * moved /\
# 3. rose * moved /\
#
#(2. pansy)
# It's as if pansy is no longer in the list. The other items moved up to take its place.
increment_positions_on_lower_items(position = 1)
update_all(
"position = (position + 1)", "position >= 1"
)
# 1. (empty)
# 2. lilac * moved \/
# 3. daffodil * moved \/
# 4. rose * moved \/
#
# Hey cool, that just opened up a slot for pansy!
#(3. pansy) * moved \/
# Oops, we're moving further away from our goal, but it doesn't really matter because
# the next step will always fix it...
self.update_attribute(:position, 1)
# 1. pansy * moved directly to 1
# 2. lilac
# 3. daffodil
# 4. rose
It worked, but I still don't understand why it was implemented that, why we went through all the trouble of decrementing those rows, only to increment them as the very next thing we do! When really, the only record that needed to be updated was the one at position #1 (lilac) (move it to position #2)! Seems kind of inefficient in this case.
I guess it does have the advantage that it makes the logic easier to follow:
- first, we remove the item from the list;
- then we make room for it;
- then we put it in the slot we just created.
Let's try another example and see if that changes our opinion of this algorithm any.
# 1. lilac
#(2. pansy)
# 3. daffodil
# 4. rose
pansy.insert_at_position(position = 3)
(self.position == 2)
remove_from_list
decrement_positions_on_lower_items
update_all(
"position = (position - 1)", "position > 2"
)
# 1. lilac
# 2. daffodil * moved /\
# 3. rose * moved /\
#
#(2. pansy)
# It's as if pansy is no longer in the list. The other items moved up to take its place.
increment_positions_on_lower_items(position = 1)
update_all(
"position = (position + 1)", "position >= 3"
)
# 1. lilac
# 2. daffodil
# 3. (empty)
# 4. rose * moved \/
#
# Hey cool, that just opened up a slot for pansy!
#(2. pansy)
self.update_attribute(:position, 3)
# 1. lilac
# 2. daffodil
# 3. pansy * moved directly to 3
# 4. rose
I think my confidence in this algorithm has gone up since when I first stumbled upon this code -- thoroughly confused. Yep, it makes sense. It may not be the most efficient way, but it's easy to see how the algorithm will always work.
[edit]
ActsAsListOf
| Homepage: | ??? |
|---|---|
| Documentation: | http://pastie.caboo.se/31232.txt |
| Source code: | ???
|
| Authors: | Ezra
|
http://pastie.caboo.se/31232 [7]
[Modified quotation (category)][Paraphrase (category)]
This plugin uses a has_many :through relationship to setup ordered collections of models. By adopting a HABTM-like join table we can keep the code clean and simple, as opposed to a general table for any acts_as_list_of join table.Category has_list_of :imagesCreates the following setup:
Image belongs_to_list_of :categories # (The relationships set up behind the scenes are bi-directional) ImageCategoryListing belongs_to :category ImageCategoryListing belongs_to :image ImageCategoryListing acts_as_list (scoped by Category)And provides the following association methods:
Category.find(1).images.create(*args, &block) Category.find(1).images.push(*items) Category.find(1).images.unshift(*items) Category.find(1).images.replace(*items) Category.find(1).images.clear(*args) Category.find(1).images.remove(*items) Category.find(1).images.remove_at_position(*range_or_array) Category.find(1).images.reorder(*items) Category.find(1).images.insert_at(index_or_obj, position = 1) Category.find(1).images.move_to_top(index_or_obj) Category.find(1).images.move_higher(index_or_obj) Category.find(1).images.move_lower(index_or_obj) Category.find(1).images.move_to_bottom(index_or_obj) Category.find(1).images.entries_at_position(*range_or_array) ... Image.find(1).categories.create(*args, &block) Image.find(1).categories.push(*categories) Image.find(1).categories.unshift(*categories) Image.find(1).categories.replace(*categories) Image.find(1).categories.clear(*args) Image.find(1).categories.remove(*categories)[edit]
has_filtered_list_ofYou can also create a filtered list with some conditions:
class Category < ActiveRecord::Base has_filtered_list_of :images, :active_images, :conditions => 'categories_images.active = 1' endThis implicitly creates the base
has_list_ofassociation as well, so it's equivalent to:class Category < ActiveRecord::Base has_list_of :images has_filtered_list_of :images, :active_images, :conditions => 'categories_images.active = 1' end[edit] Required schema
create_table "categories", :force => true do |t| t.column "name", :string t.column "description", :string t.column "position", :integer end create_table "images", :force => true do |t| t.column "path", :string t.column "description", :string t.column "created_at", :datetime t.column "updated_at", :datetime endThe join table has a 'position' column to add acts_as_list behaviour. Any additional column you add will be available as well; note that although this looks like a regular HABTM relationship we actually have a full has_many :through join model: CategoryImageListing Where 'Category' is the entry_keeper and Image is the entry_item The alphabetical order of both tables is used for the join table as well as for the *Listing join model.
create_table "categories_images", :force => true do |t| t.column "category_id", :integer t.column "image_id", :integer t.column "position", :integer t.column "comment", :string end
[edit] acts_as_reportable
http://stonecode.svnrepository.com/acts_as_reportable/trac.cgi :
acts_as_reportable is a Rails plugin that allows you to convert a query on an ActiveRecord model object into a Ruport::Data::Table for easy integration with Ruport's formatting capabilities.
>> puts Book.report_table(:all).as(:csv) id,title,author_id 1,Whys (Poignant) Guide to Ruby,1 2,"Flow My Tears, The Policeman Said",2 3,Farenheit 451,3>> puts Book.report_table(:all, :columns => ['title','author.name']).as(:text) +----------------------------------------------------+ | title | author.name | +----------------------------------------------------+ | Why's (Poignant) Guide to Ruby | _why | | Flow My Tears, The Policeman Said | Philip K. Dick | | Farenheit 451 | Ray Bradbury | +----------------------------------------------------+
[edit] acts_as_state_machine
Rubyist - Acts As State Machine
I’ve been noticing a pattern in several of my projects where I’m having a model object keep track of its state. Sometimes it’s a simple boolean flag, and this presents little problem. Other times, it gets more complex. In a current project, things started out simply. Then the model evolved and finally I realized that I was using three boolean variables to keep the state. To make matters worse, these three flags were being distilled into one state. Needless to say, this collapsed under its own tremendous weight fairly quickly.
So I stood at my newly hung whiteboard and thought and thought. I realized that this object was not just in a state, but moving from state to state, in a well defined series of steps, throughout its lifetime. I do believe this is what them academicals calls a “finite automaton” or “state machine” for those of us who doodled or slept during class.
Can I relate to that! There is a need for this plugin.
Good, it looks like a model can have more than one state machine (just give each one a different column name). [Update: On second thought, if that's stored as a class variable (state_column), then you can probably only have one per model.]
http://elitists.textdriven.com/svn/plugins/acts_as_state_machine/trunk/lib/acts_as_state_machine.rb :
module ActMacro
# Configuration options are
#
# * +column+ - specifies the column name to use for keeping the state (default: state)
# * +initial+ - specifies an initial state for newly created objects (required)
def acts_as_state_machine(opts)
A pretty good example of how to use it -- pretty detailed example: http://blog.methodmissing.com/2006/11/16/beyond-callbacks-for-complex-model-lifecycles/
[edit] acts_as_flaggable
| Homepage: | http://www.baconbear.com/articles/2006/12/04/acts-as-flaggable-released |
|---|---|
| Source code: | http://svn.baconbear.com/rails_plugins/acts_as_flaggable/trunk
|
| As listed in other directories: | http://rubyfurnace.com/plugins/acts_as_flaggable |
| Description: | Acts As Flaggable is a model modifier which allows flag records to be associated with records. Authors of community driven sites may find this useful for quickly adding the ability to mark records as "spam" or "inappropriate". It also facilities in a way to automatically take a specific action automatically if a record has been flagged a certain way too many times (Think auto-removing posts that have been marked as "spam").
|
[edit] acts_as_clusterable
| Homepage: | http://cuttingtheredtape.blogspot.com/2006/08/actsasclusterable.html |
|---|---|
| Source code: | http://opensvn.csie.org/acts_as_clusterable/
|
| As listed in other directories: | http://rubyfurnace.com/plugins/acts_as_clusterable
|
http://rubyfurnace.com/plugins/acts_as_clusterable
This plugin makes it very easy to cluster active record objects. It can be used for grouping search results (see http://demo.carrot-search.com/carrot2-webapp/main).
This plugin requires the clusterer Ruby gem.
Add the following lines to your model:
acts_as_clusterable :fields => ['title','text']if no fields are given then it will use all text and string fields present in the model. Now, doing clustering is as easy as:
Joke.hierarchical_clustering() Joke.kmeans_clustering()
[edit] acts_as_activated
| Categories/Tags: | [acts_as_plugins (category)] |
|---|---|
| Homepage: | http://www.baconbear.com/articles/2006/12/29/actsaslocateable-released |
| Source code: | http://svn.webwideconsulting.com/svn/acts_as_activated/
|
| As listed in other directories: | http://rubyfurnace.com/plugins/acts_as_activated |
| Description: | Basically the opposite of acts_as_paranoid, this plugin hides records until the is_active method returns true.
|
[edit] acts_as_activity_logged
| Homepage: | http://aaal.fearoffish.co.uk/ |
|---|---|
| Source code: | svn://svn.fearoffish.co.uk/acts_as_activity_logged/trunk
|
| As listed in other directories: | http://rubyfurnace.com/plugins/acts_as_activity_logged
|
http://rubyfurnace.com/plugins/acts_as_activity_logged
The plugin is designed to track the event that occurred on a model (create/update/destroy) and allow you to see what models were affected.
So if you need “Jamie added a comment to ‘My First Post’” kind of functionality, then it’s easy to get with this plugin.
[edit] acts_as_rated
http://rubyfurnace.com/plugins/acts_as_rated
Though similar to other rating plugins, this one has a ton of options to customize, while still making it very easy to use. And most important for my use, can cache the statistics of the ratings (total/count/average) in the model itself or an external statistics table, eliminating the need to call sum/count/avg on the ratings table itself.
[edit] acts_as_list for habtm relationships
There is a acts_as_habtm_list plugin, but I couldn't get it to work.
Perhaps better to convert the habtm's to has_many :through relationships.
http://www.archivesat.com/Ruby_on_Rails_developers_help/thread783283.htm
[edit] acts_as_favorite
http://rubyfurnace.com/plugins/acts_as_favorite
- This plugin provides a simple way to track users favorites within the system using ActiveRecord models.
[edit] ↓ acts_as_view
| Categories/Tags: | [with_scope (category)] |
|---|---|
| Homepage: | http://habtm.com/articles/2006/07/23/acts_as_view |
| Source code: | http://wota.jp/svn/rails/plugins/branches/stable/acts_as_view/
|
| As listed in other directories: | http://rubyfurnace.com/plugins/acts_as_view |
| Description: | Allows an ActiveRecord model to act as a view table. |
| Depends on: | [[:MethodScoping class)]]
|
http://rubyfurnace.com/plugins/acts_as_view
class ActiveMember < Member acts_as_view :deleted => false end ActiveMember.find(:all) # => SELECT * FROM members WHERE (( deleted = 'f' )) ActiveGrade2Member.create(:name=>"chinako") # => #"chinako", "grade"=>2, "id"=>5, "deleted"=>false}>
Comments:
- Implemented using ActiveRecord's “with_scope” feature, so it inherits the benefits and problems of "with_scope".
- Doesn't actually create a view as far as the DB is concerned. You can't actually do
select * from my_view, so it's not helpful when you need to do find_by_sql.
[edit] acts_as_slugable [Permalinks (category)]
| Homepage: | http://multi-up.ca/code/ |
|---|---|
| Source code: | http://multi-up.ca/code/acts_as_slugable/
|
| As listed in other directories: | http://wiki.rubyonrails.org/rails/pages/Model+Extensions |
| Description: | Generates URL slugs (permalinks) based on a specified field
|
http://wiki.rubyonrails.org/rails/pages/Model+Extensions
For example, generate a URL slug based on the title of a
Pagerecord and store it inurl_slug. Slug will be unique within theparent.id.class Page < ActiveRecord::Base acts_as_slugable :source_column => :title, :target_column => :url_slug, :scope => :parent end
[edit] Rails plugins and libraries / Model-level / Validation
Rails plugins and libraries / Model-level / Validation edit
[edit]
Validation Reflection
| Rating: | ![]() |
|---|---|
| Categories/Tags: | [Validations (category)] |
| Documentation: | Readme |
| Source code: | svn://rubyforge.org//var/svn/valirefl/validation_reflection/trunk http://www.schuerig.de/michael/rails/validation_reflection/
|
| As listed in other directories: | http://agilewebdevelopment.com/plugins/validation_reflection http://rubyfurnace.com/plugins/validation_reflection
|
| Authors: | Michael Schuerig
|
http://agilewebdevelopment.com/plugins/validation_reflection
This plugin provides reflective access to validations
- ModelClass.reflect_on_all_validations
- ModelClass.reflect_on_validations_for(:property)
[edit]
ValidationReflection (by Yossef)
| Homepage: | http://www.rubybyraeli.org/blog/articles/2007/04/06/36-reflections-on-validity
|
|---|---|
| Authors: | Yossef
|
Yossef (April 06, 2007). Reflections on Validity (http://www.rubybyraeli.org/blog/articles/2007/04/06/36-reflections-on-validity).
[...] All of these are covered by the Rails built-in validation helpers, but there’s a bit of a catch. That catch is reflection.Ruby is one of those languages where reflection abounds. Rails, built with Ruby, takes advantage of this fact in some areas. It does not, however, when it comes to validation. And that’s yet another problem I have with how Rails handles validation. It’d be so wonderful if there was validation reflection. It’d be so nice to just query a model to see what it considers valid. It’d make a lot of interface problems easier to deal with. It’d make things more DRY. This idea started my journey into the bowels of ActiveRecord validation.
My first step this time was to see if anyone else had looked into this. It turns out Michael Schuerig did. He wrote a validation reflection plugin that seemed nice, but it wasn’t exactly what I wanted. That problem seems to come up from time to time. Is it me or everyone else? So I built on his idea, learning a good bit in the process, and came up with something that did what I wanted.
...
Some of the differences are cosmetic or more on the line of “syntactic sugar”. [...] And one of the differences is just something I wanted to make it a little more explicit. Where he used a plain MacroReflection object, I created a new class specifically for this purpose: ValidationReflection.
module ActiveRecord::Reflection class ValidationReflection < MacroReflection attr_accessor :block alias_method :klass, :active_record alias_method :attr_name, :name alias_method :validation, :macro alias_method :configuration, :options end endAs you can see, it’s pretty much the same thing with a few aliases added for convenience. Even if that was the only difference, I’d want to have this class. It’s explicit. I lets me know exactly what type of reflection this is. I mean, there are AssociationReflection and AggregateReflection, so why use the abstract class for validations? I think they deserve more. And, of course, there’s more to this class. It includes another attribute, the block of code that composes this validation. The actual Proc object is stored within the reflection. But why?
Most of my problems with validation have to do with the messiness under the surface. Nothing (Hardly anything) is done centrally. Each individual validation repeats much of the same work done by all the rest: get the options, explicitly get the default message, create a block of code, do something to install the block of code. The actual validation process (simplified somewhat) involves getting a list of these blocks of code, running them, and checking for errors. There’s really no tie between validation-as-idea and validation-as-code. I mean, look at how validations are stored internally.
...
[edit] Validates Unchangeable
| Have used: | no |
|---|---|
| Rating: | ![]() |
| Homepage: | http://locusfoc.us/2007/2/1/validates_unchangeable-plugin |
| Source code: | svn://svn.locusfoc.us/plugins/trunk/validates_unchangeable
|
| As listed in other directories: | http://rubyfurnace.com/plugins/validates_unchangeable
|
http://rubyfurnace.com/plugins/validates_unchangeable
Sometimes you want to allow users to save data only once to a field. For example, Ebay only allows you to change the price of an item if there are no bids and it is within a certain date range. I created the validates_unchangeable for similar situations.
[edit] validates_as_email
| Have used: | yes |
|---|---|
| Rating: |
|
| Source code: | https://svn.greenpeace.org/repositories/rails_plugins/validates_as_email
|
| Authors: | Ximon Eighteen, Dan Kubb, Thijs van der Vossen
|
[edit] validates_email_format_of
| Have used: | no. How does this compare to validates_as_email??
|
|---|---|
| Homepage: | http://multi-up.ca/code/ |
| Source code: | http://multi-up.ca/code/validates_email_format_of/
|
| As listed in other directories: | http://rubyfurnace.com/plugins/validates_email_format_of
|
| Authors: | Alex Dunae
|
http://rubyfurnace.com/plugins/validates_email_format_of
- Validate email addresses against RFC 2822
[edit] Before Assignment: add a before_assignment callback to a belongs_to association
| Source code: | http://obiefernandez.com/svn/plugins/before_assignment
|
|---|---|
| As listed in other directories: | http://rubyfurnace.com/plugins/before_assignment http://wiki.rubyonrails.org/rails/pages/Model+Extensions |
| Description: | * Adds support for a before_assignment callback to belongs_to relationships. Allows validation of the object being assigned to the relationship at the time of assignment, instead of having to depend on validations or before_save callbacks on the parent object.
|
http://rubyfurnace.com/plugins/before_assignment
Plugin makes it possible to add a before_assignment callback to a belongs_to association, which enables things such as type and validity checking at the time of assignment instead of having to rely on later validation.
The idea came up while researching the :conditions option for belongs_to. I realized that Rails doesn’t prevent you from assigning a bogus value—even worse, you will be confused when you try to retrieve it later and nothing is there.
Some caboosers argued that this is not needed, because you can check the validity of the assignment as part of the validation rules for the model with the belongs_to association. To which I say: “Not always!”
Sure, there might be cases where it makes sense to check the validity of an assignment to belongs_to as part of the validation routines of the model. But in cases where a wrong assignment is NOT a state that is reachable normally by the end user, then I believe that it is better to fail fast!
If there is a programmer error or a hacker trying to breach my security, I want to know about it right away and in an explicit fashion.
Example:
class Approvable < ActiveRecord::Base belongs_to :approver, :class_name => 'User', :foreign_key => 'approver_id', :conditions => ['authorized_approver = ?', true], :extend => CheckApproverExtension endmodule CheckApproverExtension def before_assignment(approver) raise UnauthorizedApproverException unless approver.authorized_approver end endNote: Rails (as of rev 5523) supports extension (
:extend =>) of a belongs_to association, but it isn’t documented. I believe that it’s perfectly valid to extend a belongs_to relationship, and that belongs_to should accept a block parameter for extension methods, exactly how other associations do.
[edit] Name Nanny
| Categories/Tags: | [Censoring (category)] [Filtering offensive content (category)] [Bleeping (category)] (Useful for but not limited to: [Users (category)] [Sign-up forms (category)] [User registration/enrollment (category)]) |
|---|---|
| Homepage: | http://locusfoc.us/2007/2/13/name-nanny-plugin |
| Source code: | svn://svn.locusfoc.us/plugins/trunk/name_nanny/
|
| As listed in other directories: | http://rubyfurnace.com/plugins/name_nanny
|
http://locusfoc.us/2007/2/13/name-nanny-plugin
I needed functionality to encourage user’s to be on their best behavior when they choosing a username or filling out a form. Bad behavior can come in many forms but specifically, I wanted to prevent people from registering usernames like “administrator”, “root”, “abuse”,”support” etc. I also didn’t want people to pick names like “whore” or even “hoar”,”h0ar”,”wh0r3” (you get the picture). It occurred to me that others would like this functionality too, so I created the name_nanny plug-in which can be employed in a couple of ways.
In the model you can use:
validates_wholesomeness_of :loginThis will check the login against values in two text files in the plug-in directory “badwords.txt”, and “restrictedwords.txt”. By default a match returns an non-descript error like “this username is already taken”. I chose a generalized error to dissuade users from trying to hack around the filter and find a word i missed.
If you are filtering the body of text you may want to allow words like “administrator” in the body of the document. Therefore namenanny has a second method called “bleeptext”, which you can use thusly:
def before_save %w(description, title, author, tags).each do |column| if self[column.to_sym] self[column.to_sym] = bleep_text(self[column.to_sym]) end end endA sample output might look like this:
p = Post.new p.description = "Step two: put your dick in a box." p.save puts p.description # => "Step two: put your bleeep in a box."
| Depends on | ActiveRecord + |
| Description | [Oops! No type defined for attribute] |