Skip to content

Commit

Permalink
[new] added 'encode_params' to urls, added page_name access with '...…
Browse files Browse the repository at this point in the history
…' when page count too large. Updated Proc defined safe_methods to use new RubyLess with receiver. Fixed scope_index bug. Not parsing dates in links during attribute transform anymore.
  • Loading branch information
gaspard committed Dec 6, 2010
1 parent 749be1a commit 9a510c1
Show file tree
Hide file tree
Showing 25 changed files with 293 additions and 70 deletions.
4 changes: 2 additions & 2 deletions app/controllers/nodes_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def search
format.html do
begin
do_search
rescue QueryBuilder::Error => err
rescue ::QueryBuilder::Error => err
flash[:error] = err.message
end
render_and_cache :mode => '+search', :cache => false
Expand All @@ -74,7 +74,7 @@ def search
else
render :xml => [].to_xml(:root => 'nodes')
end
rescue QueryBuilder::Error => err
rescue ::QueryBuilder::Error => err
render :xml => [{:message => err.message}].to_xml(:root => 'errors'), :status => 401
end
end
Expand Down
16 changes: 9 additions & 7 deletions app/models/node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@ class Node < ActiveRecord::Base
include Property

# This must come before the first call to make_schema.
include Zena::Use::Kpath::InstanceMethods
include Zena::Use::Kpath::InstanceMethods

def virtual_class
@virtual_class ||= if self[:vclass_id]
VirtualClass.find_by_id(self[:vclass_id])
Expand Down Expand Up @@ -136,7 +136,7 @@ def self.make_schema
# def safe_eval(code)
# eval RubyLess.translate(schema, code)
# end

def safe_method_type(signature, receiver = nil)
schema.safe_method_type(signature, receiver)
end
Expand Down Expand Up @@ -188,7 +188,7 @@ def safe_method_type(signature, receiver = nil)

# Dynamic resolution of the author class from the user prototype
def self.author_proc
Proc.new do |h, s|
Proc.new do |h, r, s|
res = {:method => 'author', :nil => true}
if prototype = visitor.prototype
res[:class] = prototype.vclass
Expand Down Expand Up @@ -841,13 +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 date}.include?(key)
elsif %w{log_at event_at v_publish_from}.include?(key)
if value.kind_of?(Time)
res[key] = value
elsif value
# parse date
if key == 'date'
if key == 'date' # never true, 'date' is not in list anymore
# 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 Down Expand Up @@ -1563,7 +1565,7 @@ def node_before_validation
# Make sure the node is complete before creating it (check parent and project references)
def validate_node
errors.add(:title, "can't be blank") if title.blank?

if @parent_zip_error
errors.add('parent_id', @parent_zip_error)
@parent_zip_error = nil
Expand Down
2 changes: 1 addition & 1 deletion app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class User < ActiveRecord::Base

# Dynamic resolution of the author class from the prototype
def self.node_user_proc
Proc.new do |h, s|
Proc.new do |h, r, s|
res = {:method => 'node', :nil => true}
if prototype = visitor.prototype
res[:class] = prototype.vclass
Expand Down
10 changes: 10 additions & 0 deletions lib/zena/acts/secure.rb
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,16 @@ def err(obj)
end
end

def rename_prop(list, old_key, new_key)
list.each do |rec|
prop = rec.prop
if value = prop.delete(old_key)
prop[new_key] = value
Zena::Db.execute "UPDATE #{rec.class.table_name} SET properties=#{Zena::Db.quote(rec.class.encode_properties(prop))} WHERE id=#{rec[:id]}"
end
end
end

def login(name, host = nil)
finder = {}
finder[:conditions] = cond = [[]]
Expand Down
4 changes: 2 additions & 2 deletions lib/zena/site_worker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ module Zena
class SiteWorker < Struct.new(:site_id, :action, :page)
include Zena::Acts::Secure

# Execute operations on 50 nodes at a time
CHUNK_SIZE = 50
# Execute operations on 250 nodes at a time
CHUNK_SIZE = 250

def self.perform(site, action, page = 1)
action = new(site.id, action, page)
Expand Down
2 changes: 1 addition & 1 deletion lib/zena/use/action.rb
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ def self.included(base)
def r_debug
%Q{
<pre style='background:white; color:black; border:1px solid red; display:table;'>
class #{node.klass}: #{Array(node.klass).first.schema.columns.keys.join(', ')}
class #{node.klass}: #{Array(node.klass).first.columns.keys.join(', ')}
</pre>
}
end
Expand Down
21 changes: 16 additions & 5 deletions lib/zena/use/ajax.rb
Original file line number Diff line number Diff line change
Expand Up @@ -208,23 +208,34 @@ def process_drag

node = pre_filter_node

# We do not want to have duplicate ids so we use our own dom prefix.
if node.dom_prefix.blank?
if @name.blank?
# make sure we have a scope
set_dom_prefix(node)
end

markup.set_id(node.dom_id)
# We do not want to use the same id as the 'each' loop but we also want to
# avoid changing the node context
@drag_prefix ||= root.get_unique_name('drag', true).gsub(/[^\d\w\/]/,'_')
markup.set_id(node.dom_id(:dom_prefix => @drag_prefix))

markup.append_param(:class, 'drag')

drag = 'drag_handle' if drag == 'true'

js_options = drag == 'all' ? ['false'] : [drag.inspect]
if drag == 'all'
js_options = ['false']
else
unless @blocks.detect{|b| b.kind_of?(String) ? b =~ /class=.#{drag}/ : (b.params[:class] == drag || (b.markup && b.markup.params[:class] == drag))}
handle = "<span class='#{drag}'>&nbsp;</span>"
end
js_options = [drag.inspect]
end

if revert = @params.delete(:revert)
js_options << (%w{true false}.include?(revert) ? revert : revert.inspect)
end

markup.pre_wrap[:drag] = "<% add_drag_id(\"#{node.dom_id(:erb => false)}\", #{js_options.join(', ').inspect}) -%>"
markup.pre_wrap[:drag] = "#{handle}<% add_drag_id(\"#{node.dom_id(:dom_prefix => @drag_prefix, :erb => false)}\", #{js_options.join(', ').inspect}) -%>"
end

# Display an input field to filter a remote block
Expand Down
19 changes: 14 additions & 5 deletions lib/zena/use/context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module ViewMethods

# Dynamic resolution of the author class from the user prototype
def self.visitor_node_proc
Proc.new do |h, s|
Proc.new do |h, r, s|
res = {:method => 'visitor.node', :nil => true}
if prototype = visitor.prototype
res[:class] = prototype.vclass
Expand Down Expand Up @@ -113,21 +113,30 @@ def page_numbers(current, count, join_string = nil, max_count = nil)
join_str = ''
if count <= max_count
1.upto(count) do |p|
yield(p, join_str)
yield(p, join_str, p)
join_str = join_string
end
else
# only first pages (centered around current page)
if current - (max_count/2) > 0
finish = [current + (max_count/2),count].min
max = current + (max_count/2)
else
finish = [max_count,count].min
max = max_count
end

if count > max
finish = end_dots = max
else
finish = count
end

start = [finish - max_count + 1,1].max
if start > 1
start_dots = start
end

start.upto(finish) do |p|
yield(p, join_str)
yield(p, join_str, (p == start_dots || p == end_dots) ? '…' : p.to_s)
join_str = join_string
end
end
Expand Down
26 changes: 25 additions & 1 deletion lib/zena/use/forms.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,24 @@ def make_checkbox(node, opts)
res.join('')
end
end

# Find a params value from an input name (q[foo] ==> safe params[q][foo])
def param_value(name)
# q[foo.bar][xxx]
list = name.gsub(']','').split('[')
# q foo.bar xxx
base = params
while true
key = list.shift
if base.kind_of?(Hash)
base = base[key]
else
return nil
end
break if list.empty?
end
base
end
end # ViewMethods

module ZafuMethods
Expand Down Expand Up @@ -549,7 +567,13 @@ def get_input_params(params = @params)
else
res[:value] = "<%= fquote #{value} %>"
end
elsif type = node.klass.safe_method_type([attribute])
elsif params[:param]
if name =~ /^[a-z_]+$/
res[:value] = "<%= fquote params[:#{name}] %>"
else
res[:value] = "<%= fquote param_value(#{name.inspect}) %>"
end
elsif attribute && type = node.klass.safe_method_type([attribute])
res[:value] = "<%= fquote #{node}.#{type[:method]} %>"
end
end
Expand Down
62 changes: 48 additions & 14 deletions lib/zena/use/query_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ def find_node_by_zip(zip)
secure(Node) { Node.find_by_zip(zip) }
end

def query(class_name, node_name, pseudo_sql)
def query(class_name, node_name, pseudo_sql, opts = {})
type = opts[:type] || :find
if klass = VirtualClass[class_name]
begin
query = klass.build_query(:all, pseudo_sql,
Expand All @@ -20,14 +21,17 @@ def query(class_name, node_name, pseudo_sql)
# mixed in and strangely RubyLess cannot access the helpers from 'self'.
:rubyless_helper => zafu_helper.helpers
)
klass.do_find(:all, eval(query.to_s))
if type == :count
return klass.do_find(:count, eval(query.to_s(:count)))
else
return klass.do_find(:all, eval(query.to_s))
end
rescue ::QueryBuilder::Error => err
# FIXME: how to return error messages to the user ?
nil
end
else
nil
end
# error
type == :count ? 0 : nil
end

# Takes a hash of parameters and builds query arguments for pseudo sql
Expand All @@ -43,7 +47,7 @@ def query_parse(params)
res << clause unless clause.blank?
end
end
res.empty? ? 'true' : res.join(' and ')
res.empty? ? '1=1' : res.join(' and ')
end

private
Expand Down Expand Up @@ -107,14 +111,41 @@ def self.included(base)
base.process_unknown :querybuilder_eval
end

# Dynamic query mocks the QueryBuilder::Query
class DynamicQuery
def initialize(default, node, sql)
@default, @node, @sql = default, node, sql
end

def to_s(type = :find)
base = "'#{@node.klass}', #{@node.to_s.inspect}, #{@sql}"
case type
when :find
return "query(#{base})"
else
return "query(#{base}, :type => #{type.inspect})"
end
end

# Pass all other methods to the default query.
def method_missing(meth, *args)
@default.send(meth, *args)
end
end

# Open a list context with a query comming from the url params. Default param name is
# "qb"
def r_query
return parser_error("Cannot be used in list context (#{node.class_name})") if node.list_context?
return parser_error("Missing 'default' query") unless default = @params[:default]
return parser_error("Missing 'default' query") unless default_psql = @params[:default]

begin
default = build_finder(:all, default_psql, {})
default_query = default[:query]
rescue ::QueryBuilder::Error => err
return parser_error(err.message)
end

default_query = build_query(:all, default)
klass = [default_query.main_class]

can_be_nil = true
Expand All @@ -124,20 +155,19 @@ def r_query
return parser_error("Invalid compilation result for #{sql.inspect} (#{sql.klass})")
end
can_be_nil = sql.opts[:nil]
elsif sql = @params[:text]
elsif sql = @params[:select]
sql = RubyLess.translate_string(self, sql)
can_be_nil = sql.opts[:nil]
else
sql = "params[:qb]"
end

if can_be_nil
sql = "#{sql} || #{default.inspect}"
sql = "#{sql} || #{default_psql.inspect}"
end

method = "query('#{node.klass}', #{node.to_s.inspect}, #{sql})"

expand_with_finder(:method => method, :class => klass, :nil => true)
query = DynamicQuery.new(default_query, node, sql)
expand_with_finder(:method => query.to_s, :class => [query.main_class], :nil => true, :query => query)
end

# Pre-processing of the 'find("...")' method.
Expand Down Expand Up @@ -193,8 +223,12 @@ def node_context_vars(finder)
page_count = get_var_name('paginate', 'count', sub_context)
curr_page = get_var_name('paginate', 'current', sub_context)

# Give access to the pagination key.
# Give access to the page number through the pagination key.
set_context_var('set_var', pagination_key, RubyLess::TypedString.new(curr_page, Number))
# Give access to page_count and count
# FIXME: DOC
set_context_var('set_var', 'page_count', RubyLess::TypedString.new(page_count, Number))
set_context_var('set_var', 'count', RubyLess::TypedString.new(node_count, Number))

out "<% #{node_count} = Node.do_find(:count, #{query.to_s(:count)}); #{page_count} = (#{node_count} / #{query.page_size.to_f}).ceil; #{curr_page} = [1,params[:#{pagination_key}].to_i].max -%>"
elsif finder[:method].kind_of?(RubyLess::TypedString)
Expand Down
3 changes: 2 additions & 1 deletion lib/zena/use/query_node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ def do_find(count, query)
res = find_by_sql(query).first
secure_result(res)
when :count
count_by_sql(query)
# query can be a number when we use the 'query' helper to count.
query.kind_of?(Fixnum) ? query : count_by_sql(query)
else
nil
end
Expand Down
6 changes: 3 additions & 3 deletions lib/zena/use/scope_index.rb
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,11 @@ def self.included(base)
end

def self.scope_index_proc
Proc.new do |helper, signature|
if helper.idx_class && klass = Zena.resolve_const(helper.idx_class) rescue nil
Proc.new do |helper, receiver, signature|
if receiver.respond_to?('idx_class') && receiver.idx_class && klass = Zena.resolve_const(receiver.idx_class) rescue nil
{:method => 'scope_index', :nil => true, :class => klass}
else
raise RubyLess::NoMethodError.new(helper, helper, signature)
raise RubyLess::NoMethodError.new(receiver, receiver, signature)
end
end
end
Expand Down

0 comments on commit 9a510c1

Please sign in to comment.