reworked rendering method

Oleg committed Sep 24, 2010
commit 9a496069c6a59e8616a0aa5288eaf83189e234c2
Showing 9 changed files with 203 additions and 87 deletions
app/models/cms_page.rb +7 -37
@@ @@ -12,7 +12,7 @@ class CmsPage < ActiveRecord::Base
# -- Relationships --------------------------------------------------------
belongs_to :cms_layout
- has_many :cms_assets,
+ has_many :cms_uploads,
:dependent => :destroy
has_many :cms_blocks,
:dependent => :destroy
@@ @@ -47,44 +47,14 @@ class CmsPage < ActiveRecord::Base
end
# -- Instance Methods -----------------------------------------------------
- # Processing content will return rendered content and all tag that were used.
- def process_content
- CmsTag.process_content(self, cms_layout.content.dup)
+ # Processing content will return rendered content and all tags that were used.
+ def content
+ cms_layout ? CmsTag.process_content(self, cms_layout.content.dup) : ''
end
- # Initilize tags the moment layout gets assigned. This way there's no need to
- # call initialize tags manually. Need to do this on both association and
- # foreign id assignments.
- # def cms_layout_id=(value)
- # write_attribute(:cms_layout_id, value)
- # self.cms_layout_with_tag_initialization = CmsLayout.find_by_id(value)
- # end
- #
- # def cms_layout_with_tag_initialization=(value)
- # self.cms_layout_without_tag_initialization = value
- # self.initialize_tags
- # end
- # alias_method_chain :cms_layout=, :tag_initialization
-
- # Returns an array of tag objects, at the same time populates cms_blocks
- # of the current page
- # def initialize_tags
- # CmsTag.initialize_tags(self)
- # end
-
- # Converting object from cms_tags collection into cms_blocks
- # TODO: Explicitely calling it. Can't really put it into a callback
- # def assign_cms_blocks!
- # self.cms_tags.each do |tag|
- # if block = self.cms_blocks.find_by_label(tag.label)
- # block.type = tag.type
- # block.content = tag.content
- # block.save!
- # else
- # self.cms_blocks.create!(:label => tag.label, :type => tag.type, :content => tag.content)
- # end
- # end
- # end
+ def cms_tags
+ @cms_tags ||= []
+ end
protected
comfortable_mexican_sofa/cms_tag.rb b/lib/comfortable_mexican_sofa/cms_tag.rb +31 -8
@@ @@ -6,6 +6,8 @@
# end
module CmsTag
+ attr_accessor :parent
+
module ClassMethods
# Regex that is used to match tags in the content
# Example:
@@ @@ -31,6 +33,23 @@ module CmsTag
module InstanceMethods
+ # String indentifier of the tag
+ def identifier
+ "#{self.class.name.underscore}_#{self.label}"
+ end
+
+ # Equality check. Content doesn't matter. Signature does.
+ def ==(tag)
+ self.identifier == tag.identifier
+ end
+
+ # Ancestors of this tag constructed during rendering process
+ def ancestors
+ node, nodes = self, []
+ nodes << node = node.parent while node.parent
+ nodes
+ end
+
# Regex that is used to identify instance of the tag
# Example:
# /<\s*?cms:page:tag_label\/?>/
@@ @@ -38,18 +57,17 @@ module CmsTag
nil
end
- def content=(value)
- nil
- end
-
+ # Content that is accociated with Tag instance.
def content
nil
end
- # Content that is used during page rendering
+ # Content that is used during page rendering. Outputting existing content
+ # as a default.
def render
content
end
+
end
private
@@ @@ -62,13 +80,18 @@ private
end
# Scanning provided content and splitting it into [tag, text] tuples.
- # Tags are processed further and their content is expanded in the same way
- def self.process_content(cms_page, content = '')
+ # Tags are processed further and their content is expanded in the same way.
+ # Tags are defined in the parent tags are ignored and not rendered.
+ def self.process_content(cms_page, content = '', parent_tag = nil)
tokens = content.to_s.scan(/(<\s*cms:\w+:\w+(?::\w+)?\s*\/?>)|((?:[^<]|\<(?!\s*cms:\w+:\w+(?::\w+)?\s*\/?>))+)/)
tokens.collect do |tag_signature, text|
if tag_signature
if tag = self.initialize_tag(cms_page, tag_signature)
- self.process_content(cms_page, tag.render)
+ tag.parent = parent_tag if parent_tag
+ unless tag.ancestors.member?(tag)
+ cms_page.cms_tags << tag
+ self.process_content(cms_page, tag.render, tag)
+ end
end
else
text
comfortable_mexican_sofa/cms_tag/page_string.rb.todo b/lib/comfortable_mexican_sofa/cms_tag/page_string.rb.todo +0 -4
@@ @@ -11,10 +11,6 @@ class CmsTag::PageString < CmsBlock
self.class.regex_tag_signature(label)
end
- def content=(value)
- write_attribute(:content_string, value)
- end
-
def content
read_attribute(:content_string)
end
test/fixtures/README.md +3 -3
@@ @@ -3,11 +3,11 @@ Default Test Application Data Structure
Layout
------
<cms:field:default_field:text>
- content_a
+ layout_content_a
<cms:page:default_page:text>
- content_b
+ layout_content_b
<cms:snippet:default_snippet>
- content_c
+ layout_content_c
Page
----
### CmsBlock::FieldText
test/fixtures/cms_blocks.yml +1 -1
@@ @@ -10,5 +10,5 @@ default_page_text:
label: default_page_text
content_text: |-
default_page_text_content_a
- <cms:snippet:default_snippet>
+ <cms:snippet:default>
default_page_text_content_b
test/fixtures/cms_layouts.yml +4 -4
@@ @@ -3,11 +3,11 @@ default:
parent:
content: |-
<cms:field:default_field_text:text>
- content_a
+ layout_content_a
<cms:page:default_page_text:text>
- content_b
- <cms:snippet:default_snippet>
- content_c
+ layout_content_b
+ <cms:snippet:default>
+ layout_content_c
nested:
label: Nested Layout
test/fixtures/cms_snippets.yml +1 -1
@@ @@ -1,3 +1,3 @@
default:
- label: default_snippet
+ label: default
content: default_snippet_content
test/test_helper.rb +6 -0
@@ @@ -20,6 +20,12 @@ class ActiveSupport::TestCase
end
end
end
+
+ # Small method that allows for better formatting in tests
+ def rendered_content_formatter(string)
+ string.gsub(/^[ ]+/, '')
+ end
+
end
class ActionController::TestCase
test/unit/cms_tag_test.rb +150 -29
@@ @@ -2,44 +2,165 @@ require File.dirname(__FILE__) + '/../test_helper'
class CmsTagTest < ActiveSupport::TestCase
- def test_initialization_and_rendering
+ def test_content_for_existing_page
page = cms_pages(:default)
- raise page.process_content.to_yaml
+ assert page.cms_tags.blank?
+ assert_equal rendered_content_formatter(
+ '
+ layout_content_a
+ default_page_text_content_a
+ default_snippet_content
+ default_page_text_content_b
+ layout_content_b
+ default_snippet_content
+ layout_content_c'
+ ), page.content
+
+ assert_equal 4, page.cms_tags.size
+ assert_equal 'cms_tag/field_text_default_field_text', page.cms_tags[0].identifier
+ assert_equal 'cms_tag/page_text_default_page_text', page.cms_tags[1].identifier
+ assert_equal 'cms_tag/snippet_default', page.cms_tags[2].identifier
+ assert_equal page.cms_tags[1], page.cms_tags[2].parent
+ assert_equal 'cms_tag/snippet_default', page.cms_tags[3].identifier
end
- def test_method_find_cms_tags
- content = cms_layouts(:default).content
- assert_equal [
- '<cms:page:content/>',
- '<cms:page:title:string/>',
- '<cms:page:number:integer/>'
- ], CmsTag.find_cms_tags(content)
+ def test_content_for_new_page
+ page = CmsPage.new
+ assert page.cms_blocks.blank?
+ assert page.cms_tags.blank?
+ assert_equal '', page.content
+ assert page.cms_tags.blank?
end
- def test_initialize_tags
- content = cms_layouts(:default).content
- tags = CmsTag.initialize_tags(nil, content)
- assert_equal 3, tags.size
- assert_equal 3, (cms_blocks = tags.select{|t| t.class.superclass == CmsBlock}).count
- cms_blocks.each do |block|
- assert block.content.blank?
- end
+ def test_content_for_new_page_with_layout
+ page = CmsPage.new(:cms_layout => cms_layouts(:default))
+ assert page.cms_blocks.blank?
+ assert page.cms_tags.blank?
+ assert_equal rendered_content_formatter(
+ '
+ layout_content_a
+
+ layout_content_b
+ default_snippet_content
+ layout_content_c'
+ ), page.content
+
+ assert_equal 3, page.cms_tags.size
+ assert_equal 'cms_tag/field_text_default_field_text', page.cms_tags[0].identifier
+ assert_equal 'cms_tag/page_text_default_page_text', page.cms_tags[1].identifier
+ assert_equal 'cms_tag/snippet_default', page.cms_tags[2].identifier
end
- def test_initialize_tags_for_a_saved_page
- tags = CmsTag.initialize_tags(cms_pages(:default))
- assert_equal 3, tags.size
- assert_equal 3, (cms_blocks = tags.select{|t| t.class.superclass == CmsBlock}).count
- cms_blocks.each do |block|
- assert !block.content.blank?
- end
+ def test_content_for_new_page_with_initilized_cms_blocks
+ page = CmsPage.new(:cms_layout => cms_layouts(:default))
+ assert page.cms_blocks.blank?
+ assert page.cms_tags.blank?
+ page.cms_blocks_attributes = [
+ {
+ :label => 'default_field_text',
+ :content => 'new_default_field_content',
+ :type => 'CmsTag::FieldText'
+ },
+ {
+ :label => 'default_page_text',
+ :content => "new_default_page_text_content\n<cms:snippet:default>",
+ :type => 'CmsTag::PageText'
+ },
+ {
+ :label => 'bogus_field_that_never_will_get_rendered',
+ :content => 'some_content_that_doesnot_matter',
+ :type => 'CmsTag::PageText'
+ }
+ ]
+ assert_equal 3, page.cms_blocks.size
+
+ assert_equal rendered_content_formatter(
+ '
+ layout_content_a
+ new_default_page_text_content
+ default_snippet_content
+ layout_content_b
+ default_snippet_content
+ layout_content_c'
+ ), page.content
+
+ assert_equal 4, page.cms_tags.size
+ assert_equal 'cms_tag/field_text_default_field_text', page.cms_tags[0].identifier
+ assert_equal 'cms_tag/page_text_default_page_text', page.cms_tags[1].identifier
+ assert_equal 'cms_tag/snippet_default', page.cms_tags[2].identifier
+ assert_equal page.cms_tags[1], page.cms_tags[2].parent
+ assert_equal 'cms_tag/snippet_default', page.cms_tags[3].identifier
end
- def test_initialize_tags_for_initialized_page
- page = CmsPage.new
- assert_equal 0, CmsTag.initialize_tags(page).size
- page.cms_layout = cms_layouts(:default)
- assert_equal 3, CmsTag.initialize_tags(page).size
+ def test_content_with_repeated_tags
+ page = cms_pages(:default)
+ page.cms_layout.content << "\n<cms:page:default_page_text:text>"
+ page.cms_layout.save!
+
+ assert_equal rendered_content_formatter(
+ '
+ layout_content_a
+ default_page_text_content_a
+ default_snippet_content
+ default_page_text_content_b
+ layout_content_b
+ default_snippet_content
+ layout_content_c
+ default_page_text_content_a
+ default_snippet_content
+ default_page_text_content_b'
+ ), page.content
+
+ assert_equal 6, page.cms_tags.size
+ assert_equal 'cms_tag/field_text_default_field_text', page.cms_tags[0].identifier
+ assert_equal 'cms_tag/page_text_default_page_text', page.cms_tags[1].identifier
+ assert_equal 'cms_tag/snippet_default', page.cms_tags[2].identifier
+ assert_equal page.cms_tags[1], page.cms_tags[2].parent
+ assert_equal 'cms_tag/snippet_default', page.cms_tags[3].identifier
+ assert_equal 'cms_tag/page_text_default_page_text', page.cms_tags[4].identifier
+ assert_equal 'cms_tag/snippet_default', page.cms_tags[5].identifier
+ assert_equal page.cms_tags[4], page.cms_tags[5].parent
+ end
+
+ def test_content_with_shallow_cyclical_tags
+ page = cms_pages(:default)
+ snippet = cms_snippets(:default)
+ snippet.update_attribute(:content, "infinite <cms:snippet:default> loop")
+ assert_equal rendered_content_formatter(
+ '
+ layout_content_a
+ default_page_text_content_a
+ infinite loop
+ default_page_text_content_b
+ layout_content_b
+ infinite loop
+ layout_content_c'
+ ), page.content
+
end
+ def test_content_with_deep_cyclical_tags
+ page = cms_pages(:default)
+ snippet = cms_snippets(:default)
+ snippet.update_attribute(:content, "infinite <cms:page:default> loop")
+ assert_equal rendered_content_formatter(
+ '
+ layout_content_a
+ default_page_text_content_a
+ infinite loop
+ default_page_text_content_b
+ layout_content_b
+ infinite loop
+ layout_content_c'
+ ), page.content
+ end
+
+ def test_tag_equality
+ tag_1 = CmsTag::PageText.new(:label => 'new_text', :content => 'content')
+ tag_2 = CmsTag::FieldText.new(:label => 'new_text', :content => 'content')
+ tag_3 = CmsTag::PageText.new(:label => 'new_text', :content => 'other content')
+
+ assert_not_equal tag_1, tag_2
+ assert_equal tag_1, tag_3
+ end
end