Rails plugins and libraries / Model-level

From WhyNotWiki

Jump to: navigation, search

Contents

[edit] [Model-level (category)]

http://wiki.rubyonrails.org/rails/pages/Model+Extensions


[edit] SemanticAttributes

SemanticAttributes edit


Rating: star_full.gif star_full.gif star_empty.gif
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/. Retrieved on 2007-05-11 11:18.


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). Retrieved on 2007-05-11 11:18.


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
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
=> #<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
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…


[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).


[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)
end
Customer.find(:all).to_csv
Customer.find(:first).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

[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] star_full.gif star_empty.gif star_empty.gif 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

Paginator gem edit

"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] star_full.gif star_full.gif star_full.gif 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). Retrieved on 2007-03-14 10:15.

If you've spent any time at all with Rails, you've doubtless noticed the acts_as commands 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/. Retrieved on 2007-05-11 11:18.


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: star_full.gif star_full.gif star_full.gif
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). Retrieved on 2007-05-11 11:18.


...

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] star_full.gif star_full.gif star_empty.gif 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). Retrieved on 2007-03-29 11:48.

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
        end
class 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] star_full.gif star_full.gif star_empty.gif 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] star_full.gif star_full.gif star_empty.gif 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 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 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). Retrieved on 2007-05-11 11:18.


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.

...

[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). Retrieved on 2007-05-11 11:18.


[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] star_full.gif star_full.gif star_empty.gif 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

[Code dissections (category)]

/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:

  1. first, we remove the item from the list;
  2. then we make room for it;
  3. 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] star_full.gif star_full.gif star_empty.gif 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 :images

Creates 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_of

You 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'
end

This implicitly creates the base has_list_of association 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
end

The 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 Page record and store it in url_slug. Slug will be unique within the parent.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] star_full.gif star_full.gif star_empty.gif Validation Reflection

Rating: star_full.gif star_full.gif star_empty.gif
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] star_full.gif star_full.gif star_full.gif 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). Retrieved on 2007-05-11 11:18.


[...] 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
end

As 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: star_full.gif star_full.gif star_empty.gif
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: star_full.gif star_full.gif star_full.gif


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.
  • Complements the :conditions option of belongs_to





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  
end
module CheckApproverExtension
  def before_assignment(approver) 
    raise UnauthorizedApproverException unless approver.authorized_approver 
  end
end

Note: 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 :login

This 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
  end

A 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."
 
Facts about Rails plugins and libraries / Model-levelRDF feed
Depends on ActiveRecord  +
Description [Oops! No type defined for attribute]