page acts as tree / list + improve validations
This commit is contained in:
parent
e81b4a353a
commit
ae20b51636
3
Gemfile
3
Gemfile
@ -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
|
||||
|
@ -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 path_must_not_begin_with_reserverd_keywords
|
||||
if (self.path =~ /^(#{Locomotive.config.forbidden_paths.join('|')})\//) == 0
|
||||
errors.add(:path, :reserved_keywords)
|
||||
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 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
|
4
doc/TODO
4
doc/TODO
@ -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
|
||||
|
@ -21,4 +21,8 @@ class String
|
||||
s
|
||||
end
|
||||
|
||||
def slugify!(options = {})
|
||||
replace(self.slugify(options))
|
||||
end
|
||||
|
||||
end
|
@ -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
29
lib/locomotive/sitemap.rb
Normal 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
|
@ -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
|
@ -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,6 +44,58 @@ 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
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user