Skip to content

Instantly share code, notes, and snippets.

@rsl
Created October 24, 2008 17:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rsl/19493 to your computer and use it in GitHub Desktop.
Save rsl/19493 to your computer and use it in GitHub Desktop.
module LuckySneaks
module ActsAsUrl # :nodoc:
def self.included(base)
base.extend ClassMethods
end
module ClassMethods # :doc:
# Creates a callback to automatically create an url-friendly representation
# of the <tt>attribute</tt> argument. Example:
#
# act_as_url :title
#
# will use the string contents of the <tt>title</tt> attribute
# to create the permalink. You may also pass a Proc object instead and this
# object will be evaluated and its return value used instead of a database
# attribute
#
# The default attribute <tt>acts_as_url</tt> uses
# to store the permalink on the database is <tt>url</tt> but can be changed in the options hash.
# Available options are:
#
# <tt>:url_attribute</tt>:: The name of the attribute to use for storing the generated url string.
# Default is <tt>:url</tt>
# <tt>:scope</tt>:: The name of model attribute to scope unique urls to. There is no default here.
# <tt>:sync_url</tt>:: If set to true, the url field will be updated when changes are made to the
# attribute it is based on. Default is false.
def acts_as_url(attribute, options = {})
cattr_accessor :attribute_to_urlify
cattr_accessor :scope_for_url
cattr_accessor :url_attribute # The attribute on the DB
if options[:sync_url]
before_validation :ensure_unique_url
else
before_validation_on_create :ensure_unique_url
end
self.attribute_to_urlify = attribute
self.scope_for_url = options[:scope]
self.url_attribute = options[:url_attribute] || "url"
end
# Initialize the url fields for the records that need it. Designed for people who add
# <tt>acts_as_url</tt> support once there's already development/production data they'd
# like to keep around.
#
# Note: This method can get very expensive, very fast. If you're planning on using this
# on a large selection, you will get much better results writing your own version with
# using pagination.
def initialize_urls
find(:all, :conditions => {self.url_attribute => nil}).each do |instance|
instance.send :ensure_unique_url
instance.save
end
end
end
private
def ensure_unique_url
url_attribute = self.class.url_attribute
base_url = get_base_url
conditions = ["#{url_attribute} = ?", base_url]
unless new_record?
conditions.first << " and id != ?"
conditions << id
end
if self.class.scope_for_url
conditions.first << " and #{self.class.scope_for_url} = ?"
conditions << send(self.class.scope_for_url)
end
url_owners = self.class.find(:all, :conditions => conditions)
if url_owners.size > 0
n = 1
while url_owners.detect{|u| u.send(url_attribute) == "#{base_url}-#{n}"}
n = n.succ
end
write_attribute url_attribute, "#{base_url}-#{n}"
else
write_attribute url_attribute, base_url
end
end
def get_base_url
if self.class.attribute_to_urlify.is_a?(Proc)
instance_exec self, &self.class.attribute_to_urlify
else
send self.class.attribute_to_urlify
end.to_s.to_url
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment