Skip to content

Commit

Permalink
Fixed all tests after VirtualClass introspection and code changes.
Browse files Browse the repository at this point in the history
  • Loading branch information
gaspard committed Jan 14, 2011
1 parent d52e625 commit 14e5990
Show file tree
Hide file tree
Showing 18 changed files with 223 additions and 100 deletions.
5 changes: 5 additions & 0 deletions History.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
* Added 'integer' property with index.
* Added query_parse (parse form content such as '>34' or '10..34').
* Enable setting relations with SQLiss.
* Changed class filter from '<r:Contact>' to '<r:Contact?>'.
* Added VirtualClass introspection from zafu with [grid] and Node [send] methods.
* Added relation groups.
* Added 'map' and 'join' support to Array in RubyLess.
* Added dynamic values support for [toggle].

== 1.0.0.beta4

Expand Down
12 changes: 6 additions & 6 deletions app/models/node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -814,7 +814,7 @@ def plural_relation?(rel)

# Translate attributes from the visitor's reference to the application.
# This method translates dates, zazen shortcuts and zips and returns a stringified hash.
def transform_attributes(new_attributes, base_node = nil, change_timezone = true)
def transform_attributes(new_attributes, base_node = nil, change_timezone = true, is_link = false)
res = {}
res['parent_id'] = new_attributes[:_parent_id] if new_attributes[:_parent_id] # real id set inside zena.

Expand All @@ -841,15 +841,15 @@ def transform_attributes(new_attributes, base_node = nil, change_timezone = true
res[key] = User.translate_pseudo_id(value, :id) || value
elsif %w{id create_at updated_at}.include?(key)
# ignore (can be present in xml)
elsif %w{log_at event_at v_publish_from}.include?(key)
elsif %w{log_at event_at v_publish_from}.include?(key) || (is_link && %w{date}.include?(key))
# FIXME: !!! We need to fix timezone parsing in dates depending on the Schema used. This means
# that we probably need to do this at the property level (during write).
if value.kind_of?(Time)
res[key] = value
elsif value
# parse date
if key == 'date' # never true, 'date' is not in list anymore
if key == 'date'
# TODO: this is a temporary hack because date in links do not support timezones/formats properly
# Do not reanable this hack: it caused lots of other problems with properties called 'date' (use another
# name for link dates...)
res[key] = value.to_utc("%Y-%m-%d %H:%M:%S")
else
res[key] = value.to_utc(_('datetime'), change_timezone ? visitor.tz : nil)
Expand All @@ -864,7 +864,7 @@ def transform_attributes(new_attributes, base_node = nil, change_timezone = true
res[key] = value
end
elsif value.kind_of?(Hash)
res[key] = transform_attributes(value, base_node, change_timezone)
res[key] = transform_attributes(value, base_node, change_timezone, %w{link rel rel_attributes}.include?(key) || is_link)
else
# translate zazen
if value.kind_of?(String)
Expand Down
19 changes: 10 additions & 9 deletions app/models/virtual_class.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ class VirtualClass < Role
include Zena::Use::PropEval::VirtualClassMethods
include Zena::Use::ScopeIndex::VirtualClassMethods

safe_method :roles => {:class => ['Role'], :method => 'sorted_roles'}
safe_method :roles => {:class => ['Role'], :method => 'sorted_roles'}
safe_method :relations => {:class => ['RelationProxy'], :method => 'all_relations'}
safe_method [:relations, String] => {:class => ['RelationProxy'], :method => 'zafu_all_relations'}
safe_method [:relations, String] => {:class => ['RelationProxy'], :method => 'filtered_relations'}
# All columns defined for a VirtualClass (kpath based).
safe_method :all_columns => {:class => ['Column'], :method => 'safe_columns'}

Expand Down Expand Up @@ -340,7 +340,7 @@ def defined_safe_columns
# sorted by kpath, origin (VirtualClass first, Role next) and name.
def safe_columns
@safe_columns ||= begin
(superclass ? superclass.safe_columns : []) +
(superclass.kind_of?(VirtualClass) ? superclass.safe_columns : []) +
defined_safe_columns +
attached_roles.map(&:defined_safe_columns).flatten.sort {|a,b| a.name <=> b.name}
end
Expand All @@ -359,8 +359,8 @@ def safe_column_types
def sorted_roles
@sorted_roles ||= begin
res = []
if up = superclass
res << up.sorted_roles
if superclass.kind_of?(VirtualClass)
res << superclass.sorted_roles
end
res << self unless defined_safe_columns.empty?
attached_roles.sort{|a,b| a.name <=> b.name}.each do |role|
Expand All @@ -371,12 +371,13 @@ def sorted_roles
end
end

# Return virtual class' super class.
# Return virtual class' super class or Node for the virtual class of
# Node.
def superclass
if kpath.size > 1
if kpath && kpath.size > 1
VirtualClass.find_by_kpath(kpath[0..-2])
else
nil
Node
end
end

Expand Down Expand Up @@ -463,7 +464,7 @@ def import_result

# List all relations that can be set for this class, filtering by
# relation group.
def zafu_all_relations(group_filter)
def filtered_relations(group_filter)
all_relations(nil, group_filter)
end

Expand Down
5 changes: 2 additions & 3 deletions lib/zena/use/ajax.rb
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@ def add_toggle_id(dom_id, group_name, role)
@toggle_ids ||= {}
unless list = @toggle_ids[group_name]
list = @toggle_ids[group_name] = []
other = yield

if other = yield
found = other.rel[role].other_zips
Expand Down Expand Up @@ -321,7 +320,7 @@ def r_toggle
dom_id = node.dom_id(:erb => false)
markup.set_id(node.dom_id)
markup.append_param(:class, 'toggle')
out "<% add_toggle_id(\"#{dom_id}\", #{var.inspect}, #{role.inspect}) { #{finder} } -%>#{expand_with}"
out "<% add_toggle_id(\"#{dom_id}\", #{var.inspect}, #{RubyLess.translate_string(self, role)}) { #{finder} } -%>#{expand_with}"
end

def process_toggle
Expand Down Expand Up @@ -351,7 +350,7 @@ def process_toggle
markup.tag ||= 'div'

markup.append_param(:class, 'toggle')
markup.pre_wrap[:toggle] = "<% add_toggle_id(\"#{dom_id}\", #{"#{var}_tog".inspect}, #{role.inspect}) { #{finder} } -%>"
markup.pre_wrap[:toggle] = "<% add_toggle_id(\"#{dom_id}\", #{"#{var}_tog".inspect}, #{RubyLess.translate_string(self, role)}) { #{finder} } -%>"
end

def r_unlink
Expand Down
2 changes: 1 addition & 1 deletion lib/zena/use/display.rb
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,7 @@ def r_zena
def r_grid
return parser_error("not in a list context") unless node.list_context?
return parser_error("not a Node list") unless node.single_class <= Node
klass = "#{node.single_class.name}Class"
klass = "#{node.single_class.name}"
@blocks = [make(:void, :method => 'void', :text => %Q{<table class='grid'>
<tr do='#{klass}' do='roles'><th class='role' colspan='\#{columns.size}' do='each' do='name'/></tr>
<tr do='#{klass}' do='roles' do='each' do='columns'><th do='each' do='name'/></tr>
Expand Down
50 changes: 35 additions & 15 deletions lib/zena/use/forms.rb
Original file line number Diff line number Diff line change
Expand Up @@ -382,25 +382,33 @@ def r_textarea
end

# <r:select name='klass' root_class='...'/>
# <r:select name='parent_id' values='projects in site'/>
# <r:select name='parent_id' nodes='projects in site'/>
# <r:select name='parent_id' values='a,b,c'/>
# TODO: optimization (avoid loading full AR to only use [id, name])
def r_select
html_attributes, attribute = get_input_params()
return parser_error("missing name") unless attribute

if value = @params[:selected]
selected = ::RubyLess.translate_string(self, value)
elsif @context[:in_filter]
selected = "params[#{attribute.to_sym.inspect}].to_s"
elsif %w{parent_id}.include?(attribute)
selected = "#{node}.parent_zip.to_s"
elsif attribute == 'copy_id'
selected = 'nil'
elsif attribute =~ /^(.*)_id$/
# relation
selected = "#{node}.rel[#{$1.inspect}].other_zip.to_s"
# TEMPORARY HACK UNTIL WE FIX get_input_params to return a single hash with
# {:html => { prepared html attributes }, :raw => {:value => '..', :name => '..', :param => '..'}}
if param = @params[:param]
selected = "params[#{param.to_sym.inspect}].to_s"
attribute = param
else
selected = "#{node}.prop[#{attribute.inspect}].to_s"
return parser_error("missing name") unless attribute

if value = @params[:selected]
selected = ::RubyLess.translate_string(self, value)
elsif @context[:in_filter]
selected = "params[#{attribute.to_sym.inspect}].to_s"
elsif %w{parent_id}.include?(attribute)
selected = "#{node}.parent_zip.to_s"
elsif attribute == 'copy_id'
selected = 'nil'
elsif attribute =~ /^(.*)_id$/
# relation
selected = "#{node}.rel[#{$1.inspect}].other_zip.to_s"
else
selected = "#{node}.prop[#{attribute.inspect}].to_s"
end
end

html_id = html_attributes[:id] ? " id='#{html_attributes[:id]}'" : ''
Expand Down Expand Up @@ -703,6 +711,18 @@ def get_options_for_select
end
end
options_list.inspect
elsif code = @params[:eval]
ruby = ::RubyLess.translate(self, code)
if !ruby.klass.kind_of?(Array)
return parser_error("invalid eval: should return an Array (found #{ruby.klass})")
end

if ruby.klass.first <= String
# ok
ruby
else
return parser_error("cannot extract values from eval (not a String list: [#{ruby.klass.first}])")
end
end
end

Expand Down
2 changes: 1 addition & 1 deletion lib/zena/use/query_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ def build_finder(count, rel, params = {})
else_clause = RubyLess.translate(self, else_clause)

if else_clause.klass == Array
else_klass = else_clause.opts[:array_content_class]
else_klass = else_clause.opts[:elem]
if count == :all
# Get first common ancestor
common_klass = (klass.ancestors & else_klass.ancestors).detect {|x| x.kind_of?(Class)}
Expand Down
3 changes: 2 additions & 1 deletion lib/zena/use/relations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ class ProxyLoader
DUMMY = Class.new do
def other_id; nil; end
def other_ids; []; end
def other_zids; []; end
def other_zips; []; end
def to_s; 'nil'; end
end.new.freeze

def initialize(node)
Expand Down
58 changes: 55 additions & 3 deletions lib/zena/use/zafu_safe_definitions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module ViewMethods

# Dynamic resolution of kind_of
def self.kind_of_proc
Proc.new do |receiver, role_or_vclass|
@@kind_of_proc ||= Proc.new do |receiver, role_or_vclass|
if role_or_vclass.kind_of?(VirtualClass)
res = "#{receiver}.kpath_match?('#{role_or_vclass.kpath}')"
else
Expand All @@ -23,6 +23,53 @@ def self.kind_of_proc
end
end

# Dynamic resolution of map
def self.map_proc
@@map_proc ||= Proc.new do |receiver, method|
if elem = receiver.opts[:elem] || receiver.klass.first
if type = RubyLess::safe_method_type_for(elem, [method.to_s])
if type[:method] =~ /\A\w+\Z/
res = "#{receiver.raw}.map(&#{type[:method].to_sym.inspect}).compact"
else
res = "#{receiver.raw}.map{|_map_obj| _map_obj.#{type[:method]}}.compact"
end
res = RubyLess::TypedString.new(res, :class => [type[:class]])
else
raise RubyLess::NoMethodError.new(elem, elem, [method])
end
else
# internal bug: we should have :elem set whenever we use Array
raise RubyLess::NoMethodError.new(receiver.raw, receiver.klass, ['map', method])
end
end
end

# Dynamic resolution of join
def self.join_proc
Proc.new do |receiver, join_arg|
# opts[:elem] = Resolution on Array or static %w{x y z}
# TODO remove with code in RubyLessProcessing
if elem = receiver.opts[:elem] || receiver.klass.first
if type = RubyLess::safe_method_type_for(elem, ['to_s'])
if type[:method] == 'to_s'
# ok
res = receiver.raw
elsif type[:method] =~ /\A\w+\Z/
res = "#{receiver.raw}.map(&#{type[:method].inspect}).compact"
else
res = "#{receiver.raw}.map{|_map_obj| _map_obj.#{type[:method]}}.compact"
end
RubyLess::TypedString.new("#{res}.join(#{join_arg.inspect})", :class => String)
else
raise RubyLess::NoMethodError.new(receiver.raw, receiver.klass, ['to_s'])
end
else
# internal bug: we should have :elem set whenever we use Array
raise RubyLess::NoMethodError.new(receiver.raw, receiver.klass, ['join', join_arg])
end
end
end

safe_method :params => ParamsDictionary
safe_method :now => {:method => 'Time.now', :class => Time}
safe_method [:h, String] => {:class => String, :nil => true}
Expand All @@ -31,16 +78,21 @@ def self.kind_of_proc
safe_method_for String, :strip => {:class => String, :pre_processor => true}
safe_method_for String, :urlencode => {:class => String, :pre_processor => true, :method => :url_name}
safe_method_for String, :to_i => {:class => Number, :pre_processor => true}
safe_method_for String, :to_s => {:class => String, :pre_processor => true}
safe_method_for Number, :to_s => {:class => String, :pre_processor => true}
safe_method_for Object, :blank? => Boolean

safe_method_for Node, [:kind_of?, VirtualClass] =>
{:method => 'nil', :nil => true, :pre_processor => kind_of_proc}
{:method => 'nil', :nil => true, :pre_processor => kind_of_proc}
safe_method_for Node, [:kind_of?, Role] =>
{:method => 'nil', :nil => true, :pre_processor => kind_of_proc}
{:method => 'nil', :nil => true, :pre_processor => kind_of_proc}
safe_method_for Node, [:kind_of?, String] => {:method => 'kpath_match?', :class => Boolean}
safe_method_for Node, [:kind_of?, Number] => {:method => 'has_role?', :class => Boolean}
safe_method_for Array, [:index, String] => {:class => Number, :nil => true}
safe_method_for Array, [:join, String] => # supports map(:name)
{:method => 'nil', :nil => true, :pre_processor => join_proc}
safe_method_for Array, [:map, Symbol] => # supports map(:name)
{:method => 'nil', :nil => true, :pre_processor => map_proc}

end # ViewMethods

Expand Down
17 changes: 17 additions & 0 deletions test/integration/zafu_compiler/ajax.yml
Original file line number Diff line number Diff line change
Expand Up @@ -298,3 +298,20 @@ toggle_attribute_in_each:
src: "<ul do='pages'><li do='each' toggle='favorite' for='visitor.node' do='title'/></ul>"
res: "/<ul><li class='toggle' id='list1_26'>crocodiles</li>/"
js: "/list1_26.*each/"

toggle_dyn_attribute_in_each:
context:
node: cleanWater
rel: favorite
src: "<ul do='pages'><li do='each' toggle='#{params[:rel]}' for='visitor.node' do='title'/></ul>"
tem: "/add_toggle_id\(\"list1_#\{var2.zip\}\", \"var2_tog\", \"#\{params\[:rel\]\}\"\)/"
res: "/<ul><li class='toggle' id='list1_26'>crocodiles</li>/"
js: "/list1_26.*each/"

toggle_bad_rel:
context:
node: cleanWater
rel: bad
src: "<ul do='pages'><li do='each' toggle='#{params[:rel]}' for='visitor.node' do='title'/></ul>"
res: "/<ul><li class='toggle' id='list1_26'>crocodiles</li>/"
js: "/list1_26.*each/"
8 changes: 4 additions & 4 deletions test/integration/zafu_compiler/conditional.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class_conditional:
tem: "/@node.kpath_match\?\('NDI'\)/"

class_conditional_in_if:
src: "<r:parent do='Image'>image<r:else>not an image</r:else></r:parent>"
src: "<r:parent do='Image?'>image<r:else>not an image</r:else></r:parent>"
tem: "<% if var1 = @node.parent -%><% if var1.kpath_match?('NDI') -%>image<% elsif true -%>not an image<% end -%><% end -%>"
res: "not an image"

Expand All @@ -22,16 +22,16 @@ class_conditional_in_array:
tem: "/Cannot scope class in list .use each before filtering./"

case_when:
src: "<r:case><r:Document>this is a document</r:Document><r:Page>Page</r:Page><r:when test='v.status == 50'>Pub</r:when></r:case>"
src: "<r:case><r:Document?>this is a document</r:Document?><r:Page?>Page</r:Page?><r:when test='v.status == 50'>Pub</r:when></r:case>"
tem: "<% if false -%><% elsif @node.kpath_match?('ND') -%>this is a document<% elsif @node.kpath_match?('NP') -%>Page<% elsif (@node.version.status==50) -%>Pub<% end -%>"
res: "Page"

case_when_else:
src: "<r:case><r:Document>Document</r:Document><r:else>Not a document</r:else></r:case>"
src: "<r:case><r:Document?>Document</r:Document?><r:else>Not a document</r:else></r:case>"
res: "Not a document"

case_when_else_text:
src: "<r:case><r:Document>Document</r:Document><r:else do='t' text='Not a document'/></r:case>"
src: "<r:case><r:Document?>Document</r:Document?><r:else do='t' text='Not a document'/></r:case>"
res: "Not a document"

case_when_test:
Expand Down
8 changes: 7 additions & 1 deletion test/integration/zafu_compiler/errors.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,10 @@ zazen_bad_attr:

toggle_bad_finder_class:
src: "<span do='toggle' set='favorite' for='visitor'/>"
res: "/Invalid class 'for' parameter: User/"
res: "/Invalid class 'for' parameter: User/"

map_join:
context:
node: 'cleanWater'
src: "<r:children do='map(:flobo).join(\",\")'/>"
tem: "/unknown method .*flobo\(\)/"
2 changes: 1 addition & 1 deletion test/integration/zafu_compiler/relations.yml
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,7 @@ find_count:

same_name_as_class:
# we create a relation with 'page' role from Letter.
src: "<r:Letter do='pages'/>"
src: "<r:Letter? do='pages'/>"
tem: '/nodes.id = links.source_id AND links.relation_id/'

start:
Expand Down

0 comments on commit 14e5990

Please sign in to comment.