From f83ac4db9062a997faf339fefb59e0bf046c271b Mon Sep 17 00:00:00 2001 From: AntonioTerceiro Date: Sat, 24 Nov 2007 16:31:44 +0000 Subject: [PATCH] ActionItem21: moving filesystem-like behaviour from Category class into lib/acts_as_filesystem.rb. All tests for Category still passing. --- app/models/category.rb | 104 +------------------------------------------------------------------------------------------------------- lib/acts_as_filesystem.rb | 131 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 103 deletions(-) create mode 100644 lib/acts_as_filesystem.rb diff --git a/app/models/category.rb b/app/models/category.rb index 02362a0..fff903c 100644 --- a/app/models/category.rb +++ b/app/models/category.rb @@ -14,113 +14,11 @@ class Category < ActiveRecord::Base end end - acts_as_tree :order => 'name' - - # calculates the full name of a category by accessing the name of all its - # ancestors. - # - # If you have this category hierarchy: - # Category "A" - # Category "B" - # Category "C" - # - # Then Category "C" will have "A/B/C" as its full name. - def full_name(sep = '/') - my_name = self.name ? self.name : '?' - self.parent ? (self.parent.full_name(sep) + sep + my_name) : (my_name) - end - - # calculates the level of the category in the category hierarchy. Top-level - # categories have level 0; the children of the top-level categories have - # level 1; the children of categories with level 1 have level 2, and so on. - # - # A level 0 - # / \ - # B C level 1 - # / \ / \ - # E F G H level 2 - # ... - def level - self.parent ? (self.parent.level + 1) : 0 - end - - # Is this category a top-level category? - def top_level? - self.parent.nil? - end - - # Is this category a leaf in the hierarchy tree of categories? - # - # Being a leaf means that this category has no subcategories. - def leaf? - self.children.empty? - end - # Finds all top level categories for a given environment. def self.top_level_for(environment) self.find(:all, :conditions => ['parent_id is null and environment_id = ?', environment.id ]) end - # used to know when to trigger batch renaming - attr_accessor :recalculate_path - - # sets the name of the category. Also sets #slug accordingly. - def name=(value) - if self.name != value - self.recalculate_path = true - end - - self[:name] = value - unless self.name.blank? - # FIXME encapsulate this patter (transliterate -> downcase -> gsub ...) - # in a String method, say, to_slug - self.slug = self.name.transliterate.downcase.gsub( /[^-a-z0-9~\s\.:;+=_]/, '').gsub(/[\s\.:;=_+]+/, '-').gsub(/[\-]{2,}/, '-').to_s - end - end - - # sets the slug of the category. Also sets the path with the new slug value. - def slug=(value) - self[:slug] = value - unless self.slug.blank? - self.path = self.calculate_path - end - end - - # calculates the full path to this category using parent's path. - def calculate_path - if self.top_level? - self.slug - else - self.parent.calculate_path + "/" + self.slug - end - end - - # calculate the right path - before_create do |cat| - if cat.path == cat.slug && (! cat.top_level?) - cat.path = cat.calculate_path - end - end - - # when renaming a category, all children categories must have their paths - # recalculated - after_update do |cat| - if cat.recalculate_path - cat.children.each do |item| - item.path = item.calculate_path - item.recalculate_path = true - item.save! - end - end - cat.recalculate_path = false - end - - def top_ancestor - self.top_level? ? self : self.parent.top_ancestor - end - - def explode_path - path.split(/\//) - end + acts_as_filesystem end diff --git a/lib/acts_as_filesystem.rb b/lib/acts_as_filesystem.rb new file mode 100644 index 0000000..894f301 --- /dev/null +++ b/lib/acts_as_filesystem.rb @@ -0,0 +1,131 @@ +module ActsAsFileSystem + + module ClassMethods + + # Declares the ActiveRecord model to acts like a filesystem: objects are + # arranged in a tree (liks acts_as_tree), and . The underlying table must + # have the following fields: + # + # * name (+:string+) - the title of the object + # * slug (+:string+)- the title turned in a URL-friendly string (downcased, + # non-ascii chars transliterated into ascii, all sequences of + # non-alphanumericd characters changed into dashed) + # * path (+:text+)- stores the full path of the object (the full path of + # the parent, a "/" and the slug of the object) + def acts_as_filesystem + + include ActsAsFileSystem::InstanceMethods + + # a filesystem is a tree + acts_as_tree :order => 'name' + + # calculate the right path + before_create do |record| + if record.path == record.slug && (! record.top_level?) + record.path = record.calculate_path + end + end + + # when renaming a category, all children categories must have their paths + # recalculated + after_update do |record| + if record.recalculate_path + record.children.each do |item| + item.path = item.calculate_path + item.recalculate_path = true + item.save! + end + end + record.recalculate_path = false + end + + end + end + + module InstanceMethods + # used to know when to trigger batch renaming + attr_accessor :recalculate_path + + # calculates the full name of a category by accessing the name of all its + # ancestors. + # + # If you have this category hierarchy: + # Category "A" + # Category "B" + # Category "C" + # + # Then Category "C" will have "A/B/C" as its full name. + def full_name(sep = '/') + my_name = self.name ? self.name : '?' + self.parent ? (self.parent.full_name(sep) + sep + my_name) : (my_name) + end + + # calculates the level of the category in the category hierarchy. Top-level + # categories have level 0; the children of the top-level categories have + # level 1; the children of categories with level 1 have level 2, and so on. + # + # A level 0 + # / \ + # B C level 1 + # / \ / \ + # E F G H level 2 + # ... + def level + self.parent ? (self.parent.level + 1) : 0 + end + + # Is this category a top-level category? + def top_level? + self.parent.nil? + end + + # Is this category a leaf in the hierarchy tree of categories? + # + # Being a leaf means that this category has no subcategories. + def leaf? + self.children.empty? + end + + # sets the name of the category. Also sets #slug accordingly. + def name=(value) + if self.name != value + self.recalculate_path = true + end + + self[:name] = value + unless self.name.blank? + # FIXME encapsulate this pattern (transliterate -> downcase -> gsub + # ...) in a String method, say, to_slug + self.slug = self.name.transliterate.downcase.gsub( /[^-a-z0-9~\s\.:;+=_]/, '').gsub(/[\s\.:;=_+]+/, '-').gsub(/[\-]{2,}/, '-').to_s + end + end + + # sets the slug of the category. Also sets the path with the new slug value. + def slug=(value) + self[:slug] = value + unless self.slug.blank? + self.path = self.calculate_path + end + end + + # calculates the full path to this category using parent's path. + def calculate_path + if self.top_level? + self.slug + else + self.parent.calculate_path + "/" + self.slug + end + end + + def top_ancestor + self.top_level? ? self : self.parent.top_ancestor + end + + def explode_path + path.split(/\//) + end + end +end + +ActiveRecord::Base.extend ActsAsFileSystem::ClassMethods + -- libgit2 0.21.2