From 75783ec881df60a8c8217ac18922bc753079f4af Mon Sep 17 00:00:00 2001 From: dinedine Date: Mon, 23 Aug 2010 01:48:11 +0200 Subject: [PATCH] when a template is modified, all pages inheriting from it are updated too (functional tests are coming) --- app/models/extensions/page/parse.rb | 70 ++++++++++++++++++++++----- lib/locomotive/liquid/tags/extends.rb | 14 +++++- perf/benchmark.rb | 3 +- 3 files changed, 72 insertions(+), 15 deletions(-) diff --git a/app/models/extensions/page/parse.rb b/app/models/extensions/page/parse.rb index 35eff7e3..846280af 100644 --- a/app/models/extensions/page/parse.rb +++ b/app/models/extensions/page/parse.rb @@ -7,10 +7,14 @@ module Models included do field :serialized_template, :type => Binary + field :template_dependencies, :type => Array, :default => [] before_validation :serialize_template + after_save :update_template_descendants validate :template_must_be_valid + + scope :pages, lambda { |domain| { :any_in => { :domains => [*domain] } } } end module InstanceMethods @@ -21,26 +25,22 @@ module Models protected - def parse - @template = ::Liquid::Template.parse(self.raw_template, { :site => self.site, :page => self }) + def parse(context = {}) + @template = ::Liquid::Template.parse(self.raw_template, { :site => self.site, :page => self }.merge(context)) @template.root.context.clear - # TODO: walk thru the document tree to get parents as well as used snippets + self.template_dependencies = parent_templates(@template.root) + + # TODO: snippets dependencies end def serialize_template if self.new_record? || self.raw_template_changed? + @template_changed = true + @parsing_errors = [] - begin - self.parse - - self.serialized_template = BSON::Binary.new(Marshal.dump(@template)) - - # TODO: let other pages inheriting from that one and modify them in consequences - - # TODO: build array of parent pages - + self._serialize_template rescue ::Liquid::SyntaxError => error @parsing_errors << :liquid_syntax rescue ::Locomotive::Liquid::PageNotFound => error @@ -49,10 +49,56 @@ module Models end end + def _serialize_template(context = {}) + self.parse(context) + self.serialized_template = BSON::Binary.new(Marshal.dump(@template)) + end + def template_must_be_valid @parsing_errors.try(:each) { |msg| self.errors.add :template, msg } end + def parent_templates(node, templates = []) + templates << node.page_id if node.is_a?(Locomotive::Liquid::Tags::Extends) + + if node.respond_to?(:nodelist) + node.nodelist.each do |child| + self.parent_templates(child, templates) + end + end + + templates + end + + def update_template_descendants + return unless @template_changed == true + + # we admit at this point that the current template is up-to-date + descendants = self.site.pages.any_in(:template_dependencies => [self.id]).to_a + + # group them by fullpath for better performance + cached = descendants.inject({}) { |memo, page| memo[page.fullpath] = page; memo } + + self._update_direct_template_descendants(descendants, cached) + + # finally save them all + descendants.map(&:save) + + # puts "** first descendant = #{descendants.first.object_id} / #{descendants.first.template.inspect}" + end + + def _update_direct_template_descendants(descendants, cached) + direct_descendants = descendants.select do |page| + (page.template_dependencies - self.template_dependencies).size == 1 + end + + direct_descendants.each do |page| + page.send(:_serialize_template, { :cached_parent => self, :cached_pages => cached }) + + page.send(:_update_direct_template_descendants, descendants, cached) + end + end + end end diff --git a/lib/locomotive/liquid/tags/extends.rb b/lib/locomotive/liquid/tags/extends.rb index 23fff723..8543f03a 100644 --- a/lib/locomotive/liquid/tags/extends.rb +++ b/lib/locomotive/liquid/tags/extends.rb @@ -3,17 +3,27 @@ module Locomotive module Tags class Extends < ::Liquid::Extends + attr_accessor :page_id + def parse_parent_template(context) page = nil if @template_name == 'parent' - page = context[:page].parent + if context[:cached_parent] + page = context[:cached_parent] + context[:cached_parent] = nil + else + page = context[:page].parent + end else - page = context[:site].pages.where(:fullpath => @template_name.gsub("'", '')).first + path = @template_name.gsub("'", '') + page = context[:cached_pages].try(:[], path) || context[:site].pages.where(:fullpath => path).first end raise PageNotFound.new("Page with fullpath '#{@template_name}' was not found") if page.nil? + @page_id = page.id + template = page.template # merge blocks ? diff --git a/perf/benchmark.rb b/perf/benchmark.rb index 3d8834fd..8bfa3e95 100755 --- a/perf/benchmark.rb +++ b/perf/benchmark.rb @@ -1,7 +1,8 @@ # require "rubygems" # require "ruby-prof" ENV["RAILS_ENV"] ||= 'test' -require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT) + +require "./" + File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT) %w{sites pages layouts}.each do |collection| Mongoid.master.collection(collection).drop