Skip to content

Commit

Permalink
Caching VirtualClass and optimizing schema loading.
Browse files Browse the repository at this point in the history
  • Loading branch information
gaspard committed Nov 15, 2010
1 parent 9437dfd commit 0bbc51b
Show file tree
Hide file tree
Showing 28 changed files with 147 additions and 106 deletions.
2 changes: 0 additions & 2 deletions app/controllers/nodes_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,6 @@ def create

# modifications of the node itself (dates, groups, revert editions, etc)
def edit
@node.load_roles!

respond_to do |format|
format.html do
@title_for_layout = title_for_layout
Expand Down
1 change: 0 additions & 1 deletion app/controllers/versions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ def edit
def custom_tab
render :file => template_url(:mode=>'+edit', :format=>'html'), :layout=>false
rescue ActiveRecord::RecordNotFound
@node.load_roles!
render :action => 'custom_tab'
end

Expand Down
1 change: 0 additions & 1 deletion app/helpers/users_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ def node_form
begin
res =render :file => template_url(:mode => '+user', :format => 'html')
rescue ActiveRecord::RecordNotFound
@node.load_roles!
res = render :file => 'versions/custom_tab'
end
ensure
Expand Down
17 changes: 2 additions & 15 deletions app/models/node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -174,9 +174,9 @@ def self.author_proc
Proc.new do |h, s|
res = {:method => 'author', :nil => true}
if prototype = visitor.prototype
res[:class] = Zena::Acts::Enrollable.make_class(prototype.vclass)
res[:class] = prototype.vclass
else
res[:class] = Node
res[:class] = VirtualClass['Node']
end
res
end
Expand Down Expand Up @@ -422,19 +422,6 @@ def get_role(rel)
Role.first(:conditions => ['name = ? AND site_id = ?', role_name, current_site.id])
end

# Return a new object of the class name or nil if the class name does not exist.
def new_from_class(rel)
if k = get_class(rel, :create => true)
k.new_instance
else
nil
end
end

def get_class_from_kpath(kp)
native_classes[kp] || VirtualClass.find(:first, :conditions=>["site_id = ? AND kpath = ?",current_site[:id], kp])
end

# Find a node's attribute based on a pseudo (id or path). Used by zazen to create a link for ""::art or "":(people/ant) for example.
def translate_pseudo_id(id, sym = :id, base_node = nil)
if id.to_s =~ /\A(-?)(\d+)\Z/
Expand Down
8 changes: 4 additions & 4 deletions app/models/relation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,25 +31,25 @@ def valid_relation
end

if self[:source_role].blank?
if klass = Node.get_class_from_kpath(source_kpath)
if klass = VirtualClass.find_by_kpath(source_kpath)
self.source_role = klass.to_s.underscore
else
klass = nil
end
else
klass = Node.get_class_from_kpath(source_kpath)
klass = VirtualClass.find_by_kpath(source_kpath)
end

errors.add(:source_kpath, 'invalid (could not find class)') unless klass

if self[:target_role].blank?
if klass = Node.get_class_from_kpath(target_kpath)
if klass = VirtualClass.find_by_kpath(target_kpath)
self.target_role = klass.to_s.underscore
else
klass = nil
end
else
klass = Node.get_class_from_kpath(target_kpath)
klass = VirtualClass.find_by_kpath(target_kpath)
end

errors.add(:target_kpath, 'invalid (could not find class)') unless klass
Expand Down
2 changes: 1 addition & 1 deletion app/models/relation_proxy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def find_by_role(role, source_kpath = nil)
def get_proxy(node, role)
# TODO: use find_by_role(role, node.kpath) when all tests are clear
rel = find_by_role(role)
if rel && (node.new_record? || node.vclass.kpath =~ /\A#{rel.this_kpath}/)
if rel && (node.new_record? || node.kpath =~ /\A#{rel.this_kpath}/)
rel.start = node
rel
else
Expand Down
2 changes: 1 addition & 1 deletion app/models/role.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def superclass
if new_record?
Node
else
Node.get_class_from_kpath(kpath)
VirtualClass.find_by_kpath(kpath)
end
end

Expand Down
4 changes: 2 additions & 2 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ def self.node_user_proc
Proc.new do |h, s|
res = {:method => 'node', :nil => true}
if prototype = visitor.prototype
res[:class] = Zena::Acts::Enrollable.make_class(prototype.vclass)
res[:class] = prototype.vclass
else
res[:class] = Node
res[:class] = VirtualClass['Node']
end
res
end
Expand Down
59 changes: 51 additions & 8 deletions app/models/virtual_class.rb
Original file line number Diff line number Diff line change
Expand Up @@ -239,9 +239,9 @@ def classes_for_form(opts={})
else
base_kpath = self.kpath
end

kpath_len = base_kpath.size

VirtualClass.all_classes(base_kpath, opts[:without]).map do |vclass|
if vclass.create_group_id.nil? || group_ids.include?(vclass.create_group_id)
# white spaces are insecable spaces (not ' ')
Expand Down Expand Up @@ -283,16 +283,59 @@ def kpath_match?(kpath)
self.kpath =~ /^#{kpath}/
end

# Proxy methods for real class --------------

def superclass
if new_record?
Node
real_class || Node
else
VirtualClass.find_by_kpath(kpath[0..-2])
end
end

# This is used by RubyLess in method signatures: [:zen_path, #<VirtualClass 'Post'>] ---> [:zen_path, Node]
def ancestors
@ancestors ||= [real_class] + real_class.ancestors
end

# Test ancestry
def <=(other_class)
if other_class.kind_of?(VirtualClass)
kpath = other_class.kpath
self.kpath[0..(kpath.length-1)] == kpath
else
Node.get_class_from_kpath(kpath[0..-2])
real_class <= other_class
end
end

# Test ancestry
def <(other_class)
if other_class.kind_of?(VirtualClass)
kpath = other_class.kpath
self.kpath != kpath && self.kpath[0..(kpath.length-1)] == kpath
else
real_class < other_class
end
end


# Return the pseudo sql query compiler.
def query_compiler
real_class.query_compiler
end

# Build pseudo sql query.
def build_query(*args)
real_class.build_query(*args)
end

# Execute find
def do_find(*args)
real_class.do_find(*args)
end

def superclass=(klass)
if k = Node.get_class(klass)
if k = VirtualClass[klass]
@superclass = k
else
errors.add('superclass', 'invalid')
Expand All @@ -313,7 +356,7 @@ def create_instance(*args)

def real_class
@real_class ||= begin
klass = Module::const_get(self[:real_class])
klass = Module::const_get(self[:real_class] || 'Node')
raise NameError unless klass.ancestors.include?(Node)
klass
end
Expand Down Expand Up @@ -345,8 +388,8 @@ def valid_virtual_class
kpath = nil
while index < self[:name].length
try_kpath = @superclass.kpath + self[:name][index..index].upcase
if found = Node.get_class_from_kpath(try_kpath)
if found.kind_of?(VirtualClass) && found[:id] == self[:id]
if found = VirtualClass.find_by_kpath(try_kpath)
if found.id && found.id == self[:id]
kpath = try_kpath
break
end
Expand Down
4 changes: 2 additions & 2 deletions app/views/relations/_li.erb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
:url => edit_relation_path(li),
:method => :get) %></td>
<td class="ruby relation" colspan='7'>
<span class='constant min_width'><%= Node.get_class_from_kpath(li.source_kpath) %></span> <span class='arity'><%= li.target_unique? ? _('has_one') : _('has_many') %></span> <span class='string'>'<%= li.target_role %>'</span>, <span class='symbol'>:class</span> => <span class='constant'><%= Node.get_class_from_kpath(li.target_kpath).to_s %></span><br/>
<span class='constant min_width'><%= Node.get_class_from_kpath(li.target_kpath) %></span> <span class='arity'><%= li.source_unique? ? _('has_one') : _('has_many') %></span> <span class='string'>'<%= li.source_role %>'</span>, <span class='symbol'>:class</span> => <span class='constant'><%= Node.get_class_from_kpath(li.source_kpath).to_s %></span><br/>
<span class='constant min_width'><%= VirtualClass.find_by_kpath(li.source_kpath) %></span> <span class='arity'><%= li.target_unique? ? _('has_one') : _('has_many') %></span> <span class='string'>'<%= li.target_role %>'</span>, <span class='symbol'>:class</span> => <span class='constant'><%= VirtualClass.find_by_kpath(li.target_kpath).to_s %></span><br/>
<span class='constant min_width'><%= VirtualClass.find_by_kpath(li.target_kpath) %></span> <span class='arity'><%= li.source_unique? ? _('has_one') : _('has_many') %></span> <span class='string'>'<%= li.source_role %>'</span>, <span class='symbol'>:class</span> => <span class='constant'><%= VirtualClass.find_by_kpath(li.source_kpath).to_s %></span><br/>
</td>
</tr>
2 changes: 1 addition & 1 deletion config/gems.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ shoulda: '= 2.10.3'

querybuilder: '>= 0.9.3'
yamltest: '>= 0.7.0'
rubyless: '>= 0.7.0'
rubyless: '>= 0.8.0'
property: '>= 2.1.0'
versions: '>= 0.3.1'
zafu: '>= 0.7.5'
Expand Down
13 changes: 6 additions & 7 deletions lib/zena/acts/enrollable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class << base
end
end
end

# FIXME: remove all this and simply use VirtualClass['NNP'] in RubyLess:
# write 'safe_method_type' for VirtualClass and forward from there to the real_class
# def self.make_class(klass)
Expand All @@ -24,12 +24,12 @@ class << base
# else
# return klass
# end
#
#
#
#
# res_class.to_s = klass.name
# res_class.kpath = klass.kpath
# res_class.klass = klass
#
#
# res_class.load_roles!
# res_class
# end
Expand Down Expand Up @@ -123,21 +123,20 @@ def has_role?(role_id)
end

def zafu_possible_roles
roles = virtual_class.roles.reject {|r| r.class == Role }
roles = virtual_class.roles.flatten.uniq.select {|r| r.class == Role }
roles.empty? ? nil : roles
end

def zafu_roles
return nil unless role_ids = self.prop['cached_role_ids']
role_ids = role_ids.split(',').map(&:to_i)
roles = (zafu_possible_roles || []).select do |role|
role_ids.include?(role.id)
end
roles.empty? ? nil : roles
end

private

# Do not go any further if the object contains errors
# def check_unknown_attributes
# if @unknown_attribute_error
Expand Down
1 change: 0 additions & 1 deletion lib/zena/acts/serializable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ def default_serialization_options
end

def export_properties
load_roles!
res = {}
prop = self.prop
schema.column_names.each do |key|
Expand Down
7 changes: 1 addition & 6 deletions lib/zena/test_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,8 @@ def render_js

def test_compile

if klass = params.delete(:class)
klass = Node.get_class(klass)
else
klass = Node
end
klass = VirtualClass[params.delete(:class) || 'Node']

klass = Zena::Acts::Enrollable.make_class(klass)
render :text => Zena::ZafuCompiler.new_with_url(@test_url, :helper => zafu_helper).to_erb(:dev => params['dev'], :node => Zafu::NodeContext.new('@node', klass))
rescue => err
render :text => ([err.message] + err.backtrace[0..4]).join(" \n").tap {|x| puts x}
Expand Down
4 changes: 2 additions & 2 deletions lib/zena/use/context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ def self.visitor_node_proc
Proc.new do |h, s|
res = {:method => 'visitor.node', :nil => true}
if prototype = visitor.prototype
res[:class] = Zena::Acts::Enrollable.make_class(prototype.vclass)
res[:class] = prototype.vclass
else
res[:class] = Node
res[:class] = VirtualClass['Node']
end
res
end
Expand Down
2 changes: 1 addition & 1 deletion lib/zena/use/forms.rb
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ def make_form
if name
type ||= node.klass.safe_method_type([name])
# do we have a property ?
if type && (node.klass.column_names.include?(name) || node.klass.schema.column_names.include?(name))
if type && (node.real_class.column_names.include?(name) || node.klass.column_names.include?(name))
# create an input field
out make_input(form_helper, name, type[:class], textarea)
else
Expand Down
30 changes: 16 additions & 14 deletions lib/zena/use/query_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,24 @@ def find_node_by_zip(zip)
end

def query(class_name, node_name, pseudo_sql)
klass = get_class(class_name)
begin
query = klass.build_query(:all, pseudo_sql,
:node_name => node_name,
:main_class => klass,
# We use 'zafu_helper' (which is slower) instead of 'self' because our helper needs to have helper modules
# mixed in and strangely RubyLess cannot access the helpers from 'self'.
:rubyless_helper => zafu_helper.helpers
)
rescue ::QueryBuilder::Error => err
# FIXME: how to return error messages to the user ?
if klass = VirtualClass[class_name]
begin
query = klass.build_query(:all, pseudo_sql,
:node_name => node_name,
:main_class => klass,
# We use 'zafu_helper' (which is slower) instead of 'self' because our helper needs to have helper modules
# mixed in and strangely RubyLess cannot access the helpers from 'self'.
:rubyless_helper => zafu_helper.helpers
)
rescue ::QueryBuilder::Error => err
# FIXME: how to return error messages to the user ?
nil
end

klass.do_find(:all, eval(query.to_s))
else
nil
end

klass.do_find(:all, eval(query.to_s))
end
end # ViewMethods

Expand Down Expand Up @@ -51,7 +54,6 @@ def self.included(base)
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("No query compiler for (#{node.class_name})") if !node.klass.respond_to?(:build_query)


default_query = build_query(:all, default)
Expand Down
8 changes: 4 additions & 4 deletions lib/zena/use/query_node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ def process_field(field_name)
return processing_filter? ? "(#{fld})" : fld
elsif processing_filter? && field_name =~ /\A[A-Z]+_\w+\Z/
# scope index
klass = @query.main_class.klass
klass = @query.main_class
if index_model = klass.kind_of?(VirtualClass) ? klass.idx_class : nil
index_model = Zena.resolve_const(index_model) rescue NilClass
if index_model < Zena::Use::ScopeIndex::IndexMethods && index_model.column_names.include?(field_name)
Expand Down Expand Up @@ -507,9 +507,9 @@ def filter_relation(relation)
add_filter "#{table}.kpath LIKE #{quote("#{res_class.kpath}%")}" unless res_class.kpath == 'N'
true
elsif role = Node.get_role(relation)
klass = Zena::Acts::Enrollable.make_class(@query.main_class)
klass.include_role role
set_main_class(klass)
if klass = VirtualClass.find_by_kpath(role.kpath)
set_main_class(klass)
end

add_table(main_table)
add_table('nodes_roles')
Expand Down

0 comments on commit 0bbc51b

Please sign in to comment.