Changeset 1016

Show
Ignore:
Timestamp:
2008-05-26 22:21:09 (8 months ago)
Author:
gaspard
Message:

Implemented site based foxy fixtures to ease creating new fixtures. It is now possible
to create specific fixtures for tests by creating a folder in 'test/sites' and
putting the fixtures files in there. On setup, set the global variable $_test_site
to the folder name.
The new fixtures do not need to have project_id, rwp groups, ... set. These are guessed
from the inheritance flag and the parent. Refs #199(19).

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/app/helpers/application_helper.rb

    r1001 r1016  
    413413  def make_gallery(ids=[], opts={}) 
    414414    if ids == [] 
    415       images = secure!(Image) { Image.find(:all, :conditions => ["parent_id = ?", (opts[:node] || @node)[:id]])} 
     415      images = secure!(Image) { Image.find(:all, :conditions => ["parent_id = ?", (opts[:node] || @node)[:id]], :order => "position ASC, name ASC")} 
    416416    else 
    417417      ids = ids.map{|i| i.to_i} 
  • trunk/app/models/group.rb

    r902 r1016  
    2222  before_destroy          :check_can_destroy 
    2323  belongs_to              :site 
     24   
     25  # FIXME: test translate_pseudo_id for groups 
     26  def self.translate_pseudo_id(id,sym=:id) 
     27    str = id.to_s 
     28    if str =~ /\A\d+\Z/ 
     29      # id 
     30      res = self.connection.execute( "SELECT #{sym} FROM groups WHERE site_id = #{current_site[:id]} AND id = '#{str}'" ).fetch_row 
     31      res ? res[0].to_i : nil 
     32    elsif str =~ /\A([a-zA-Z ]+)(\+*)\Z/ 
     33      res = self.connection.execute( "SELECT groups.#{sym} FROM groups WHERE site_id = #{current_site[:id]} AND name LIKE #{self.connection.quote("#{$1}%")} LIMIT #{$2.size}, 1" ).fetch_row 
     34      res ? res[0].to_i : nil 
     35    else 
     36      nil 
     37    end 
     38  end 
    2439   
    2540  # Return true if the group is the public group of the site. 
  • trunk/app/models/iformat.rb

    r964 r1016  
    9494       
    9595      errors.add('name', "invalid") if name.blank? || name =~ /[^a-zAZ]/ 
    96       if self.size != 'keep' 
     96      if self.size != SIZES.index('keep') 
    9797        errors.add('width', "must be greater then 0") if width.to_i <= 0 
    9898        errors.add('height', "must be greater then 0") if height.to_i <= 0 
     
    102102    def iformat_before_validation 
    103103      self[:site_id] = visitor.site[:id] 
    104       if self.size == 'keep' 
     104      if self.size == SIZES.index('keep') 
    105105        self[:width] = nil  
    106106        self[:height] = nil 
  • trunk/app/models/node.rb

    r1001 r1016  
    590590    # This method translates dates, zazen shortcuts and zips and returns a stringified hash. 
    591591    def transform_attributes(new_attributes) 
    592       parent_id  = new_attributes[:_parent_id] # real id set inside zena. 
     592      res = {} 
     593      res['parent_id'] = new_attributes[:_parent_id] if new_attributes[:_parent_id] # real id set inside zena. 
     594       
    593595      attributes = new_attributes.stringify_keys 
    594       attributes.delete('_parent_id') 
    595  
    596       if parent_id 
    597         attributes.delete('parent_id') 
    598       elsif p = attributes.delete('parent_id') 
    599         parent_id = Node.translate_pseudo_id(p) || p 
    600       end 
    601  
     596 
     597      if !res['parent_id'] && p = attributes['parent_id'] 
     598        res['parent_id'] = Node.translate_pseudo_id(p) || p 
     599      end 
     600       
    602601      attributes.keys.each do |key| 
    603         if ['rgroup_id', 'wgroup_id', 'pgroup_id', 'user_id'].include?(key) 
    604           # ignore 
     602        next if ['_parent_id', 'parent_id'].include?(key) 
     603         
     604        if ['rgroup_id', 'wgroup_id', 'pgroup_id'].include?(key) 
     605          res[key] = Group.translate_pseudo_id(attributes[key]) || attributes[key] 
     606        elsif ['rgroup', 'wgroup', 'pgroup'].include?(key) 
     607          res["#{key}_id"] = Group.translate_pseudo_id(attributes[key]) || attributes[key] 
     608        elsif ['user_id'].include?(key) 
     609          res[key] = User.translate_pseudo_id(attributes[key]) || attributes[key] 
    605610        elsif ['v_publish_from', 'log_at', 'event_at'].include?(key) 
    606           # parse date 
    607           attributes[key] = attributes[key].to_utc(_('datetime'), visitor.tz) 
     611          if attributes[key].kind_of?(Time) 
     612            res[key] = attributes[key] 
     613          else 
     614            # parse date 
     615            res[key] = attributes[key].to_utc(_('datetime'), visitor.tz) 
     616          end 
    608617        elsif key =~ /^(\w+)_id$/ 
    609618          if key[0..1] == 'd_' 
    610             attributes[key] = Node.translate_pseudo_id(attributes[key],:zip) || attributes[key] 
     619            res[key] = Node.translate_pseudo_id(attributes[key],:zip) || attributes[key] 
    611620          else 
    612             attributes[key] = Node.translate_pseudo_id(attributes[key]) || attributes[key] 
     621            res[key] = Node.translate_pseudo_id(attributes[key]) || attributes[key] 
    613622          end 
    614623        elsif key =~ /^(\w+)_ids$/ 
     
    620629            values.map! {|v| Node.translate_pseudo_id(v,:id ) } 
    621630          end 
    622           attributes[key] = values.compact 
     631          res[key] = values.compact 
     632        elsif key == 'file' 
     633          unless attributes[key].blank? 
     634            res[key] = attributes[key] 
     635          end 
    623636        else 
    624637          # translate zazen 
    625638          value = attributes[key] 
    626639          if value.kind_of?(String) 
    627             attributes[key] = ZazenParser.new(value,:helper=>self, :node=>self).render(:parse_shortcuts=>true) 
    628           end 
    629         end 
    630       end 
    631  
    632  
    633       attributes['parent_id'] = parent_id if parent_id 
    634  
    635       attributes.delete('file') if attributes['file'] == '' 
    636  
    637       attributes 
     640            res[key] = ZazenParser.new(value,:helper=>self, :node=>self).render(:parse_shortcuts=>true) 
     641          else 
     642            res[key] = value 
     643          end 
     644        end 
     645      end 
     646 
     647      res 
    638648    end 
    639649 
    640650    def get_attributes_from_yaml(filepath) 
    641       attributes = {} 
    642       YAML::load_documents( File.open( filepath ) ) do |entries| 
    643         entries.each do |key,value| 
    644           attributes[key] = value 
    645         end 
    646       end 
    647       attributes 
     651      attributes = YAML::load( File.read( filepath ) ) 
     652      attributes.delete(:_parent_id) 
     653      transform_attributes(attributes) 
    648654    end 
    649655     
     
    12641270        documents.each do |doc| 
    12651271          unless doc.unpublish 
    1266             doc.errors.each do |err
    1267               errors.add('document', err.to_s
     1272            doc.errors.each do |k, v
     1273              errors.add('document', "#{k} #{v}"
    12681274            end 
    12691275            allOK = false 
  • trunk/app/models/skin.rb

    r528 r1016  
    88   
    99    def set_need_skin_name_update 
    10       return nil if new_record? 
    11       @need_skin_name_update = (self[:name] != old[:name]) 
     10      @need_skin_name_update = !new_record? && (self[:name] != old[:name]) 
    1211      true # save can continue 
    1312    end 
     
    1716      # FIXME: escape correctly against sql injection 
    1817      # FIXME: when moving a template or a page that is a parent of a template: we must sync skin_name after spread_project_and_section. 
    19       Skin.connection.execute "UPDATE nodes,template_contents SET template_contents.skin_name = '#{name.gsub(/^\w_\./,'')}' WHERE nodes.id = template_contents.node_id AND nodes.section_id = '#{self[:id].to_i}' AND template_contents.site_id = '#{self[:site_id].to_i}'" 
     18      Skin.connection.execute "UPDATE nodes,template_contents SET template_contents.skin_name = #{Skin.connection.quote(name)} WHERE nodes.id = template_contents.node_id AND nodes.section_id = #{self[:id]} AND template_contents.site_id = '#{self[:site_id]}'" 
    2019    end 
    2120end 
  • trunk/app/models/template.rb

    r1008 r1016  
    1616class Template < TextDocument 
    1717  validate :valid_section 
     18  after_save :update_content 
    1819   
    1920  class << self 
     
    9091     
    9192    def valid_section 
     93      @need_skin_name_update = !new_record? && old.section_id != section[:id] 
    9294      errors.add('parent_id', 'Invalid parent (section is not a Skin)') unless section.kind_of?(Skin) 
    9395    end 
     
    102104    end 
    103105     
     106    def update_content 
     107      if @need_skin_name_update 
     108        Template.connection.execute "UPDATE template_contents SET skin_name = #{Template.connection.quote(section[:name])} WHERE node_id = #{Template.connection.quote(self[:id])}" 
     109        @need_skin_name_update = nil 
     110      end 
     111    end 
     112     
    104113end 
  • trunk/app/models/user.rb

    r1000 r1016  
    6060      make_visitor :login => login, :password => password, :host => host 
    6161    end 
     62     
     63    # Needs proper testing 
     64    # def translate_pseudo_id(id,sym=:id) 
     65    #   str = id.to_s 
     66    #   if str =~ /\A\d+\Z/ 
     67    #     # id 
     68    #     res = self.connection.execute( "SELECT users.#{sym} FROM users INNER JOIN participations ON users.id = participations.user_id AND participations.site_id = #{current_site[:id]} WHERE id = '#{str}'" ).fetch_row 
     69    #     res ? res[0].to_i : nil 
     70    #   elsif str =~ /\A([a-zA-Z ]+)(\+*)\Z/ 
     71    #     self.connection.execute( "SELECT users.#{sym} FROM users INNER JOIN participations ON users.id = participations.user_id AND participations.site_id = #{current_site[:id]} WHERE login LIKE #{self.connection.quote("#{$1}%")} LIMIT #{$2.size}, 1" ).fetch_row 
     72    #     res ? res[0].to_i : nil 
     73    #   else 
     74    #     nil 
     75    #   end 
     76    # end 
    6277     
    6378    # Return the logged in visitor from the session[:user] or the anonymous user if id is nil or does not match 
  • trunk/config/zena.rb

    r919 r1016  
    5050end 
    5151 
     52# TODO: where should this go ? (it has to be loaded by 'rake' and tests...) 
     53module ZenaTest 
     54  def self.id(site, key) 
     55    return nil if key.blank? 
     56    if key == 0 # special rgroup, wgroup, pgroup values... 
     57      key 
     58    else 
     59      "#{site}_#{key}".hash.abs 
     60    end 
     61  end 
     62 
     63  def self.multi_site_id(key) 
     64    return nil if key.blank? 
     65    key.to_s.hash.abs 
     66  end 
     67 
     68  def self.multi_site_tables 
     69    ['users', 'sites'] 
     70  end 
     71end 
    5272# this list is taken from http://www.duke.edu/websrv/file-extensions.html 
    5373EXT_TYPE = [ 
  • trunk/lib/node_query.rb

    r996 r1016  
    142142    end 
    143143     
    144     def map_field(field, table_name = table, is_null = false
     144    def map_field(field, table_name = table, context = nil
    145145      case field[0..1] 
    146146      when 'd_' 
     
    148148        key = field[2..-1] 
    149149        key, function = parse_sql_function_in_field(key) 
    150         key = function ? "#{function}(#{dyn_value('versions', key, is_null)})" : dyn_value('versions', key, is_null
     150        key = function ? "#{function}(#{dyn_value('versions', key, context)})" : dyn_value('versions', key, context
    151151      when 'c_' 
    152152        # CONTENT TABLE 
     
    225225    end 
    226226     
    227     def dyn_value(table_name, key, is_null
     227    def dyn_value(table_name, key, context
    228228      @dyn_keys[table_name] ||= {} 
    229229      @dyn_keys[table_name][key] ||= begin 
    230230        needs_table('nodes', 'versions', "TABLE1.id = TABLE2.node_id") 
    231         if true || is_null # always use a LEFT join with the key, works better for sort clauses. 
    232           dtable = needs_join_table('versions', 'LEFT', 'dyn_attributes', "TABLE1.id = TABLE2.owner_id AND TABLE2.key = '#{key.gsub(/[^a-z_A-Z]/,'')}'", "versions=dyn_attributes=#{key}") 
    233         else 
    234           dtable = needs_join_table('versions', 'LEFT', 'dyn_attributes', 'TABLE1.id = TABLE2.owner_id') 
    235           @filters << "#{dtable}.key = '#{key.gsub(/[^a-z_A-Z]/,'')}'" 
    236         end 
     231        dtable = needs_join_table('versions', 'LEFT', 'dyn_attributes', "TABLE1.id = TABLE2.owner_id AND TABLE2.key = '#{key.gsub(/[^a-z_A-Z]/,'')}'", "versions=dyn_attributes=#{key}") 
    237232        "#{dtable}.value" 
    238233      end 
  • trunk/lib/parser/lib/rules/zazen.rb

    r999 r1016  
    1515      @text = @text.gsub("\r\n","\n") # this also creates our own 'working' copy of the text 
    1616      @blocks = "" # same reason as why we rewrite 'store' 
     17       
    1718      extract_code(@text) 
    1819       
     
    299300     
    300301     
    301     def extract_code(text) 
     302    def extract_code(fulltext) 
    302303      @escaped_code = [] 
    303304      block_counter = -1 
    304       text.gsub!( /<code([^>]*)>(.*?)<\/code>/m ) do 
     305      fulltext.gsub!( /<code([^>]*)>(.*?)<\/code>/m ) do 
    305306        if @parse_shortcuts 
    306307          @escaped_code << $& 
     
    328329      @escaped_at = [] 
    329330      block_counter = -1 
    330       text.gsub!( /(\A|[^\w])@(.*?)@(\Z|[^\w])/m ) do 
     331      fulltext.gsub!( /(\A|[^\w])@(.*?)@(\Z|[^\w])/m ) do 
    331332        @escaped_at << $2 
    332333        block_counter += 1 
  • trunk/lib/parser/test/parser/zazen.yml

    r1006 r1016  
    1414  src: "some @ruby|class << self@" 
    1515  res: "<p>some <code class='ruby'><span class=\"keyword\">class </span><span class=\"punct\">&lt;&lt;</span> <span class=\"constant\">self</span></code></p>" 
    16    
     16 
     17at_code_and_zazen: 
     18  src: | 
     19    This @do='[v_title]'@ and "":332. 
     20     
     21    <code>blah</code> 
     22  res: "/\[make_link id:\|332\| title:\|\|\]/" 
     23 
    1724code_ruby: 
    1825  src: | 
  • trunk/lib/query_builder/lib/query_builder.rb

    r996 r1016  
    142142              "NULL" 
    143143            else 
    144               if fld = field_or_param(part, table, op[0..2] == 'is') # we need to inform if we are looking for 'null' related field/param 
     144              if fld = field_or_param(part, table, :filter) # we need to inform if we are looking for 'null' related field/param 
    145145                fld 
    146146              elsif fld.nil? 
     
    176176        if clause =~ /^\s*([^\s]+) (ASC|asc|DESC|desc)/ 
    177177          fld_name, direction = $1, $2 
    178           if fld = map_field(fld_name, table
     178          if fld = map_field(fld_name, table, :order
    179179            res << "#{fld} #{direction.upcase}" 
    180180          elsif fld.nil? 
     
    192192    def parse_group_clause(field) 
    193193      return nil unless field 
    194       if fld = map_field(field, table
     194      if fld = map_field(field, table, :group
    195195        " GROUP BY #{fld}" 
    196196      else 
     
    375375     
    376376    # Map a field to be used inside a query 
    377     def field_or_param(fld, table_name = table, is_null = false
     377    def field_or_param(fld, table_name = table, context = nil
    378378      if table_name 
    379         map_field(fld, table_name, is_null
     379        map_field(fld, table_name, context
    380380      else 
    381381        map_parameter(fld) 
     
    384384     
    385385    # Overwrite this and take car to check for valid fields. 
    386     def map_field(fld, table_name, is_null=false
     386    def map_field(fld, table_name, context = nil
    387387      if fld == 'id' 
    388388        "#{table_name}.#{fld}" 
  • trunk/lib/query_builder/test/query_builder/basic.yml

    r1006 r1016  
    1010 
    1111recipients: 
    12   res: "SELECT objects.* FROM objects,links WHERE objects.id = links.target_id AND links.relation_id = 4 AND links.source_id = ID" 
     12  res: "SELECT objects.* FROM objects,links WHERE objects.id = links.target_id AND links.relation_id = _ID(node_has_an_icon) AND links.source_id = ID" 
    1313 
    1414letters_in_project: 
     
    2424order_many_tables: 
    2525  src: "recipients order by name ASC" 
    26   res: "SELECT objects.* FROM objects,links WHERE objects.id = links.target_id AND links.relation_id = 4 AND links.source_id = ID ORDER BY objects.name ASC" 
     26  res: "SELECT objects.* FROM objects,links WHERE objects.id = links.target_id AND links.relation_id = _ID(node_has_an_icon) AND links.source_id = ID ORDER BY objects.name ASC" 
    2727 
    2828limit: 
     
    4242    - "recipients" 
    4343    - "objects" 
    44   res: "SELECT objects.* FROM objects,links WHERE ((objects.id = links.target_id AND links.relation_id = 4 AND links.source_id = ID) OR (objects.parent_id = ID)) GROUP BY objects.id" 
     44  res: "SELECT objects.* FROM objects,links WHERE ((objects.id = links.target_id AND links.relation_id = _ID(node_has_an_icon) AND links.source_id = ID) OR (objects.parent_id = ID)) GROUP BY objects.id" 
  • trunk/lib/query_builder/test/query_builder/filters.yml

    r1006 r1016  
    55recipients_that_are_clients: 
    66  src: "recipients where kpath like 'NRCC%'" 
    7   res: "SELECT objects.* FROM objects,links WHERE objects.id = links.target_id AND links.relation_id = 4 AND links.source_id = ID AND objects.kpath LIKE \"NRCC%\"" 
     7  res: "SELECT objects.* FROM objects,links WHERE objects.id = links.target_id AND links.relation_id = _ID(node_has_an_icon) AND links.source_id = ID AND objects.kpath LIKE \"NRCC%\"" 
    88 
    99pages_in_site_name_like: 
     
    1919 
    2020recipients_group_by_name: 
    21   res: "SELECT objects.* FROM objects,links WHERE objects.id = links.target_id AND links.relation_id = 4 AND links.source_id = ID GROUP BY objects.name" 
     21  res: "SELECT objects.* FROM objects,links WHERE objects.id = links.target_id AND links.relation_id = _ID(node_has_an_icon) AND links.source_id = ID GROUP BY objects.name" 
  • trunk/lib/query_builder/test/query_builder/joins.yml

    r1006 r1016  
    11 
    22icons_from_recipients: 
    3   res: "SELECT ob1.* FROM objects,links,objects AS ob1,links AS li1 WHERE ob1.id = li1.source_id AND li1.relation_id = 5 AND li1.target_id = objects.id AND objects.id = links.target_id AND links.relation_id = 4 AND links.source_id = ID" 
     3  res: "SELECT ob1.* FROM objects,links,objects AS ob1,links AS li1 WHERE ob1.id = li1.source_id AND li1.relation_id = 5 AND li1.target_id = objects.id AND objects.id = links.target_id AND links.relation_id = _ID(node_has_an_icon) AND links.source_id = ID" 
    44 
    55letters_from_recipients: 
    6   res: "SELECT ob1.* FROM objects,links,objects AS ob1 WHERE ob1.kpath LIKE 'NNL%' AND ob1.parent_id = objects.id AND objects.id = links.target_id AND links.relation_id = 4 AND links.source_id = ID" 
     6  res: "SELECT ob1.* FROM objects,links,objects AS ob1 WHERE ob1.kpath LIKE 'NNL%' AND ob1.parent_id = objects.id AND objects.id = links.target_id AND links.relation_id = _ID(node_has_an_icon) AND links.source_id = ID" 
    77 
    88objects_from_recipients: 
    9   res: "SELECT ob1.* FROM objects,links,objects AS ob1 WHERE ob1.parent_id = objects.id AND objects.id = links.target_id AND links.relation_id = 4 AND links.source_id = ID" 
     9  res: "SELECT ob1.* FROM objects,links,objects AS ob1 WHERE ob1.parent_id = objects.id AND objects.id = links.target_id AND links.relation_id = _ID(node_has_an_icon) AND links.source_id = ID" 
    1010 
    1111parent_from_parent: 
  • trunk/lib/secure.rb

    r981 r1016  
    273273            # private 
    274274            if visitor.site[:allow_private] 
    275               self[:rgroup_id] = 0 
    276               self[:wgroup_id] = 0 
    277               self[:pgroup_id] = 0 
     275              self[:rgroup_id] = 0  # FIXME: why not just use nil ? (NULL in db) 
     276              self[:wgroup_id] = 0  # FIXME: why not just use nil ? (NULL in db) 
     277              self[:pgroup_id] = 0  # FIXME: why not just use nil ? (NULL in db) 
    278278            else 
    279279              errors.add('inherit', "you cannot change this") 
     
    325325          end 
    326326          if user_id != old.user_id 
    327             if visitor.group_ids.include?(2) # admin group 
     327            if visitor.is_admin? 
    328328              # only admin can change owners 
    329329              unless User.find(:first, :conditions => ["id = ?",user_id]) 
  • trunk/lib/tasks/zena.rake

    <
    r966 r1016  
    7878    end 
    7979  end 
     80   
     81  class FoxyParser 
     82    attr_reader :column_names, :table, :elements, :site, :name, :defaults 
     83    def initialize(table_name, opts={}) 
     84      @table = table_name 
     85      @column_names = Node.connection.columns(table).map {|c| c.name } 
     86      @elements = {} 
     87      @options  = opts 
     88    end 
     89     
     90    def run 
     91       
     92      Dir.foreach("#{RAILS_ROOT}/test/sites") do |site| 
     93        next if site =~ /^\./ 
     94        @site = site 
     95        parse_fixtures 
     96      end 
     97      @file.close if @file 
     98    end 
     99     
     100    def all_elements 
     101      @elements 
     102    end 
     103     
     104    private 
     105      def parse_fixtures 
     106        fixtures_path = File.join("#{RAILS_ROOT}/test/sites",site,"#{table}.yml") 
     107        return unless File.exist?(fixtures_path) 
     108         
     109        out "\n# ========== #{site} ===========" 
     110        content = File.read(fixtures_path) + "\n" 
     111        # build simple hash to set/get defaults and other special values 
     112        content.gsub!(/<%.*?%>/m,'') 
     113        @elements[site] = elements = YAML::load(content) 
     114         
     115        # set defaults 
     116        set_defaults 
     117         
     118        definitions = [] 
     119        name = nil 
     120        File.foreach(File.join("#{RAILS_ROOT}/test/sites",site,"#{table}.yml")) do |l| 
     121          if l =~ /^([\w\.]+):/ 
     122            last_name = name 
     123            name = $1 
     124            # purge text in between fixtures 
     125            if last_name 
     126              parse_definitions(last_name, definitions) 
     127            else 
     128              # purge text in between fixtures 
     129              definitions.each do |d| 
     130                out d 
     131              end 
     132            end 
     133            definitions = [] 
     134          else 
     135            definitions << l 
     136          end 
     137        end 
     138         
     139        if name 
     140          parse_definitions(name, definitions) 
     141        else 
     142          # purge text in between fixtures 
     143          definitions.each do |d| 
     144            out d 
     145          end 
     146        end 
     147         
     148      end 
     149       
     150      def elements 
     151        @elements[@site] 
     152      end 
     153       
     154      def set_defaults 
     155        defaults = elements.delete('DEFAULTS') 
     156        defaults ||= {} 
     157         
     158        defaults['site_id'] = ZenaTest::multi_site_id(site) if column_names.include?('site_id') 
     159         
     160        elements.each do |n,v| 
     161          unless v 
     162            v = elements[n] = {} 
     163          end 
     164          v[:defaults_keys] = [] 
     165          column_names.each do |k| 
     166            if k =~ /^(\w+)_id/ 
     167              k = $1 
     168            end 
     169            if !v.has_key?(k) && defaults.has_key?(k) 
     170              v[:defaults_keys] << k # so we know what to write out 
     171              v[k] = defaults[k] 
     172            end 
     173          end 
     174          if column_names.include?('name') && !v.has_key?('name') 
     175            v['name'] = n  
     176            v[:defaults_keys] << 'name' 
     177          end 
     178        end 
     179      end 
     180       
     181      def parse_definitions(name, definitions) 
     182        return if name == 'DEFAULTS' 
     183        @name = name 
     184         
     185        insert_headers 
     186         
     187        definitions.each do |l| 
     188          if l =~ /^\s+(\w+):/ 
     189            unless ignore_key?($1) 
     190              out l 
     191            end 
     192          else 
     193            out l 
     194          end 
     195        end 
     196         
     197      end 
     198       
     199      def insert_headers 
     200        out "" 
     201        if ZenaTest::multi_site_tables.include?(table) 
     202          out "#{name}:" 
     203        else 
     204          out "#{site}_#{name}:" 
     205        end 
     206         
     207        if column_names.include?('id') 
     208          if ZenaTest::multi_site_tables.include?(table) 
     209            out_pair('id', ZenaTest::multi_site_id(name)) 
     210          else 
     211            out_pair('id', ZenaTest::id(site,name)) 
     212          end 
     213        end 
     214         
     215        out_pair('site_id', ZenaTest::multi_site_id(site)) if column_names.include?('site_id') 
     216         
     217        id_keys.each do |k| 
     218          insert_id(k) 
     219        end 
     220         
     221        multi_site_id_keys.each do |k| 
     222          insert_multi_site_id(k) 
     223        end 
     224         
     225        element = elements[name] 
     226        element[:defaults_keys].each do |k| 
     227          next if ignore_key?(k) 
     228          out_pair(k, element[k]) 
     229        end 
     230      end 
     231       
     232      def out(res) 
     233        unless @file 
     234          # only open the file if we have things to write in it 
     235          @file = File.open("#{RAILS_ROOT}/test/fixtures/#{table}.yml", 'wb') 
     236          @file.puts "# Fixtures generated from content of 'sites' folder by FoxyParser (rake zena:build_fixtures)" 
     237          @file.puts "" 
     238        end 
     239        @file.puts res 
     240      end 
     241       
     242      def out_pair(k,v) 
     243        return if v.nil? 
     244        out sprintf('  %-16s %s', "#{k}:", v.to_s =~ /^\s*$/ ? v.inspect : v.to_s) 
     245      end 
     246       
     247      def ignore_key?(k) 
     248        (id_keys + multi_site_id_keys).include?(k) 
     249      end 
     250       
     251      def insert_id(key) 
     252        return unless column_names.include?("#{key}_id") 
     253        out_pair("#{key}_id", ZenaTest::id(site, elements[@name][key])) 
     254      end 
     255       
     256      def insert_multi_site_id(key) 
     257        return unless column_names.include?("#{key}_id") 
     258        out_pair("#{key}_id", ZenaTest::multi_site_id(elements[@name][key])) 
     259      end 
     260       
     261      def id_keys 
     262        @id_keys ||= column_names.map {|n| n =~ /^(\w+)_id$/ ? $1 : nil }.compact - multi_site_id_keys - ['site'] 
     263      end 
     264       
     265      def multi_site_id_keys 
     266        ['user'] 
     267      end 
     268  end 
     269  FOXY_PARSER = {} 
     270   
     271  class FoxyNodeParser < FoxyParser 
     272    attr_reader :virtual_classes, :max_status, :publish_from, :zip_counter 
     273     
     274    def initialize(table_name, opts = {}) 
     275      super 
     276      @virtual_classes = opts[:virtual_classes].all_elements 
     277      @max_status      = opts[:versions].max_status 
     278      @publish_from    = opts[:versions].publish_from 
     279      @zip_counter     = {} 
     280    end 
     281     
     282    private 
     283      def set_defaults 
     284        super 
     285        # set project, section, read/write/publish groups 
     286 
     287        [['project',"nil", "Node.get_class(parent['class']).kpath =~ /^\#{Project.kpath}/", "current['parent']"], 
     288         ['section',"nil", "Node.get_class(parent['class']).kpath =~ /^\#{Section.kpath}/", "current['parent']"], 
     289         ['rgroup' ,"!node['inherit']", "!parent['inherit']", "parent['rgroup']"], 
     290         ['wgroup' ,"!node['inherit']", "!parent['inherit']", "parent['wgroup']"], 
     291         ['pgroup' ,"!node['inherit']", "!parent['inherit']", "parent['pgroup']"], 
     292         ['skin' ,"!node['inherit']", "!parent['inherit']", "parent['skin']"], 
     293         ].each do |key, next_if, parent_test, value| 
     294          elements.each do |k,node| 
     295            next if node[key] || eval(next_if) 
     296            current = node 
     297            name    = k 
     298            res     = nil 
     299            path_names = [k] 
     300            while true 
     301              if parent = elements[current['parent']] 
     302                # has a parent 
     303                if eval(parent_test) 
     304                  # found 
     305                  res = eval(value) 
     306                  break 
     307                elsif parent[key] 
     308                  res = parent[key] 
     309                  # found 
     310                  break 
     311                else 
     312                  # move up 
     313                  path_names << name 
     314                  name    = current['parent'] 
     315                  current = parent 
     316                end 
     317              elsif current['parent'] 
     318                raise NameError "[#{site} #{k}] Bad parent name '#{current['parent']}' for node '#{name}'." 
     319              else 
     320                # top node 
     321                if key == 'project' || key == 'section' 
     322                  res = current[key] = name 
     323                else 
     324                  raise NameError.new( "[#{site} #{k}] Reached top without finding '#{key}'.") 
     325                end 
     326                break 
     327              end 
     328            end 
     329            if res 
     330              path_names.each do |n| 
     331                elements[n][key] = res 
     332              end 
     333            end 
     334          end 
     335        end 
     336      end 
     337       
     338      def insert_headers 
     339        super 
     340        klass = elements[name]['class'] 
     341        if virtual_classes[site] && vc = virtual_classes[site][klass] 
     342          out_pair('vclass_id', ZenaTest::id(site,klass)) 
     343          out_pair('type', vc['real_class']) 
     344          out_pair('kpath', vc['kpath'])