page acts as tree / list + improve validations

This commit is contained in:
dinedine 2010-04-30 16:05:53 +02:00
parent e81b4a353a
commit ae20b51636
8 changed files with 142 additions and 13 deletions

View File

@ -5,13 +5,14 @@ source "http://gems.github.com"
gem "rails", "3.0.0.beta3"
gem "liquid"
gem "bson_ext"
gem "bson_ext", '0.20.1'
gem "mongo_ext"
gem "mongoid", ">= 2.0.0.beta2"
gem "warden"
gem "devise", ">= 1.1.rc0"
gem "haml", '>= 3.0.0.beta.2', :git => 'git://github.com/nex3/haml.git'
gem "formtastic", :git => 'git://github.com/justinfrench/formtastic.git', :branch => 'rails3'
gem "mongoid_acts_as_tree", :git => 'git://github.com/evansagge/mongoid_acts_as_tree.git'
# Development environment

View File

@ -1,37 +1,67 @@
class Page
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Acts::Tree
## fields ##
field :title
field :path
field :slug
field :published, :type => Boolean, :default => false
field :keywords
field :description
field :position, :type => Integer
## associations ##
belongs_to_related :site
embeds_many :parts, :class_name => 'PagePart'
## validations ##
validates_presence_of :site, :title, :path
validates_uniqueness_of :path, :scope => :site_id
validate :path_must_not_begin_with_reserverd_keywords
validates_presence_of :site, :title, :slug
validates_uniqueness_of :slug, :scope => :site_id
validates_exclusion_of :slug, :in => Locomotive.config.reserved_slugs, :if => Proc.new { |p| p.depth == 0 }
## callbacks ##
before_create :add_to_list_bottom
before_create :add_body_part
before_destroy :remove_from_list
before_validate :normalize_slug
## named scopes ##
## behaviours ##
acts_as_tree
## methods ##
def add_body_part
self.parts.build :name => 'body', :value => '---body here---'
end
def parent=(owner) # missing in acts_as_tree
self[self.parent_id_field] = owner._id
self[self.path_field] = owner[owner.path_field] + [owner._id]
self[self.depth_field] = owner[owner.depth_field] + 1
self.instance_variable_set :@_will_move, true
end
def path_must_not_begin_with_reserverd_keywords
if (self.path =~ /^(#{Locomotive.config.forbidden_paths.join('|')})\//) == 0
errors.add(:path, :reserved_keywords)
def route
File.join self.self_and_ancestors.map(&:slug)
end
protected
def add_to_list_bottom
self.position = (Page.where(:_id.ne => self._id).and(:parent_id => self.parent_id).max(:position) || 0) + 1
end
def remove_from_list
Page.where(:parent_id => self.parent_id).and(:position.gt => self.position).each do |p|
p.position -= 1
p.save
end
end
def normalize_slug
self.slug.slugify!(:without_extension => true) if self.slug.present?
end
end

View File

@ -3,6 +3,10 @@
- localize devise emails
x admin layout
x logout button
x slugify page
x validation page slug
- sitemap
- pages section (CRUD)
- my account section (part of settings)
- snippets section
- layouts section

View File

@ -21,4 +21,8 @@ class String
s
end
def slugify!(options = {})
replace(self.slugify(options))
end
end

View File

@ -6,7 +6,8 @@ module Locomotive
:name => 'LocomotiveApp',
:default_domain => 'rails.local.fr',
:reserved_subdomains => %w{www admin email blog webmail mail support help site sites},
:forbidden_paths => %w{layouts snippets stylesheets javascripts assets admin system api}
# :forbidden_paths => %w{layouts snippets stylesheets javascripts assets admin system api},
:reserved_slugs => %w{stylesheets javascripts assets admin images api pages}
}
cattr_accessor :settings

29
lib/locomotive/sitemap.rb Normal file
View File

@ -0,0 +1,29 @@
module Locomotive
module SiteMap
def self.build(pages)
return Folder.new if pages.empty?
pages = pages.to_a.clone # make a secure copy
root = Folder.new :root => pages.delete_if { |p| p.path == 'index' }
pages.each do |page|
end
end
class Folder
attr_accessor :root, :name, :children
def initialize(attributes = {})
self.root = attributes[:root]
self.name = attributes[:name]
end
end
end
end

View File

@ -18,5 +18,5 @@ end
Factory.define :page do |p|
p.association :site, :factory => :site
p.title 'Home page'
p.path 'index'
p.slug 'index'
end

View File

@ -8,7 +8,7 @@ describe Page do
## Validations ##
%w{site title path}.each do |field|
%w{site title slug}.each do |field|
it "should validate presence of #{field}" do
page = Factory.build(:page, field.to_sym => nil)
page.should_not be_valid
@ -16,10 +16,18 @@ describe Page do
end
end
it 'should validate uniqueness of path' do
it 'should validate uniqueness of slug' do
page = Factory(:page)
(page = Factory.build(:page, :site => page.site)).should_not be_valid
page.errors[:path].should == ["is already taken"]
page.errors[:slug].should == ["is already taken"]
end
%w{admin stylesheets images javascripts}.each do |slug|
it "should consider '#{slug}' as invalid" do
page = Factory.build(:page, :slug => slug)
page.should_not be_valid
page.errors[:slug].should == ["is reserved"]
end
end
## Named scopes ##
@ -36,7 +44,59 @@ describe Page do
page.parts.first.name.should == 'body'
end
it 'should have normalized slug' do
page = Factory.build(:page, :slug => ' Valid ité.html ')
page.valid?
page.slug.should == 'Valid_ite'
end
end
describe 'acts as tree' do
before(:each) do
@home = Factory(:page)
@child_1 = Factory(:page, :title => 'Subpage 1', :slug => 'foo', :parent => @home, :site => @home.site)
end
it 'should add root elements' do
page_404 = Factory(:page, :title => 'Page not found', :slug => '404', :site => @home.site)
Page.roots.count.should == 2
Page.roots.should == [@home, page_404]
end
it 'should add sub pages' do
child_2 = Factory(:page, :title => 'Subpage 2', :slug => 'bar', :parent => @home, :site => @home.site)
Page.first.children.count.should == 2
Page.first.children.should == [@child_1, child_2]
end
it 'should generate a route from parents' do
nested_page = Factory(:page, :title => 'Sub sub page 1', :slug => 'bar', :parent => @child_1, :site => @home.site)
nested_page.route.should == 'index/foo/bar'
end
end
describe 'acts as list' do
before(:each) do
@home = Factory(:page)
@child_1 = Factory(:page, :title => 'Subpage 1', :slug => 'foo', :parent => @home, :site => @home.site)
@child_2 = Factory(:page, :title => 'Subpage 2', :slug => 'bar', :parent => @home, :site => @home.site)
@child_3 = Factory(:page, :title => 'Subpage 3', :slug => 'acme', :parent => @home, :site => @home.site)
end
it 'should be at the bottom of the folder once created' do
[@child_1, @child_2, @child_3].each_with_index { |c, i| c.position.should == i + 1 }
end
it 'should have its position updated if a sibling is removed' do
@child_2.destroy
[@child_1, @child_3.reload].each_with_index { |c, i| c.position.should == i + 1 }
end
end
end