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 | |