lib/releaf/slug.rb in releaf-0.1.1 vs lib/releaf/slug.rb in releaf-0.1.2
- old
+ new
@@ -1,79 +1,118 @@
module Releaf
+ # Provides common methods for finding object by slug. Also overrides to_html
+ # method.
+ #
+ # Simply add this line to your models
+ #
+ # include Releaf::Slug
+ #
+ # or
+ #
+ # ActiveRecord::Base.send(:include, Releaf::Slug)
+ #
+ # to some initializer (<tt>config/initializers/releaf.rb</tt> for example).
+ # Then use find_object or find_object! instead of find
module Slug
- def self.included(base)
- base.class_eval {
+ module ClassMethods
- def self.find_object id_or_slug, scope=nil
- raise ArgumentError unless id_or_slug.is_a?(String) or id_or_slug.is_a?(Fixnum)
+ # same as find_object, except that it will raise
+ # ActiveRecord::RecordNotFound error if no resource was found.
+ def find_object! id_or_slug, scope_name=nil, scope_args=nil
+ obj = find_object(id_or_slug, scope_name, scope_args)
+ raise ActiveRecord::RecordNotFound unless obj
+ return obj
+ end
- unless self.column_names.include?('slug')
- if scope.blank?
- return self.find(id_or_slug)
- else
- return self.send(scope).find(id_or_slug)
- end
- end
+ # Find object by slug or id.
+ #
+ # If id_or_slug looks like id, then tries to find by id first
+ # Otherwise it will search by slug field.
+ #
+ # If instance responds to children method, then it's possible to search
+ # for hierarchic resources
+ #
+ # @param id_or_slug either id of object to find (can be String or Fixnum)
+ # or slug (string) slug, may contain slash ('/')
+ #
+ # @param scope_name optional scope_name to be used for searching. This is
+ # especially useful when you are searching hierarchic resources. For
+ # example you want to find 2nd level active resource.
+ #
+ # @param scope_args any arguments that are required for scope
+ #
+ # @return resource
+ def find_object id_or_slug, scope_name=nil, scope_args=nil
+ raise ArgumentError, "id_or_slug must be String or Fixnum" unless id_or_slug.is_a?(String) or id_or_slug.is_a?(Fixnum)
- obj = nil
+ unless column_names.include?('slug')
+ return scoped_for_find_by_slug(self, scope_name, scope_args).find(id_or_slug)
+ end
- # if it looks like id, search by id
- if id_or_slug.to_s =~ /\A\d+\z/
- if scope.blank?
- obj = self.find_by_id(id_or_slug)
- else
- obj = self.send(scope).find_by_id(id_or_slug)
- end
- end
+ # if it looks like id, search by id first
+ if id_or_slug.to_s =~ /\A\d+\z/
+ obj = scoped_for_find_by_slug(self, scope_name, scope_args).find_by_id(id_or_slug)
+ return obj if obj
+ end
- unless obj
- unless self.column_names.include?('ancestry')
- if scope.blank?
- obj = self.find_by_slug(id_or_slug)
- else
- obj = self.send(scope).find_by_slug(id_or_slug)
- end
- else
- slugs = id_or_slug.split('/')
- if scope.blank?
- obj = self.find_by_slug( slugs.shift )
- else
- obj = self.send(scope).find_by_slug( slugs.shift )
- end
- raise ActiveRecord::RecordNotFound unless obj
- slugs.each do |slug_part|
- if scope.blank?
- obj = obj.children.find_by_slug(slug_part)
- else
- obj = obj.children.send(scope).find_by_slug(slug_part)
- end
- raise ActiveRecord::RecordNotFound unless obj
- end
- end
- end
+ unless column_names.include?('ancestry') || self.new.respond_to?(:children)
+ return scoped_for_find_by_slug(self, scope_name, scope_args).find_by_slug(id_or_slug)
+ else
+ slugs = id_or_slug.split('/')
- raise ActiveRecord::RecordNotFound unless obj
+ obj = scoped_for_find_by_slug(self, scope_name, scope_args).find_by_slug( slugs.shift )
+ return nil unless obj
+ slugs.each do |slug_part|
+ obj = scoped_for_find_by_slug(obj.children, scope_name, scope_args).find_by_slug( slug_part )
+ return nil unless obj
+ end
return obj
end
+ end
- def to_param
- return id unless self.class.column_names.include?('slug')
- return id if self.slug.blank?
- col_names = self.class.column_names
+ private
- if col_names.include?('ancestry')
- return path.pluck(:slug).join('/')
- elsif col_names.include?('lft') && col_names.include?('rgt') && col_names.include?('parent_id') && self.class.respond_to?(:in_list?)
- unless parent_id.blank?
- return parent.to_param + '/' + slug
- else
- return slug
- end
+ def scoped_for_find_by_slug obj, scope_name=nil, scope_args=nil
+ unless scope_name.blank?
+ if scope_args.nil?
+ obj.send(scope_name.to_sym)
else
- return slug
+ obj.send(scope_name.to_sym, *scope_args)
end
+ else
+ obj
end
- }
+ end
end
+
+
+ module InstanceMethods
+ # Ovverrides to_param method to prefer slug field over id (when
+ # possible). It will also generate hearachical uri part if instance
+ # supports parrent method, or has ancestry column (ancestry gem
+ # {http://rubygems.org/gems/ancestry})
+ #
+ # @return String or Fixnum
+ def to_param
+ return id unless self.class.column_names.include?('slug')
+ return id if self.slug.blank?
+ col_names = self.class.column_names
+
+ if col_names.include?('ancestry')
+ return path.pluck(:slug).join('/')
+ elsif self.respond_to?(:parent) && parent
+ return parent.to_param + '/' + slug
+ else
+ return slug
+ end
+ end
+ end
+
+
+ def self.included base
+ base.extend ClassMethods
+ include InstanceMethods
+ end
+
end
end