Class: VirtualClass
- Includes:
- Property::StoredSchema, Zena::Use::Fulltext::VirtualClassMethods, Zena::Use::PropEval::VirtualClassMethods, Zena::Use::Relations::ClassMethods, Zena::Use::ScopeIndex::VirtualClassMethods
- Defined in:
- app/models/virtual_class.rb
Overview
The virtual class holds type information and other attributes to build indices and computed properties. This class acts as the “schema” for nodes.
Since this class also uses Property to store some of it’s data, confusion must not be made between the VirtualClass as a schema (containing Node property definitions) and the VirtualClass’ own schema.
The roles in the vclass contain self, super and all the attached roles like this (roles for the Letter virtual class):
[
<VirtualClass:'Letter' paper, search_mono, search>,
[
<VirtualClass:'Note' >,
--> <Role:'Note' >,
[
<VirtualClass:'Node' >,
--> <Role:'Node' cached_role_ids, title, text, summary>,
<Role:'Original' weight, origin, tz>,
<Role:'Task' assigned>
]
]
]
Elements marked with ‘–>’ above are the ‘schema’ roles used by the real classes to store ruby declared properties. Since Zena is multi-site, there is one VirtualClass instance of the real classes for each site: this is why the ruby declarations are not stored in the VirtualClass itself for real classes.
Defined Under Namespace
Classes: Cache
Class Attribute Summary collapse
-
.caches_by_site ⇒ Object
Returns the value of attribute caches_by_site.
-
.export_attributes ⇒ Object
Returns the value of attribute export_attributes.
Instance Attribute Summary collapse
-
#import_result ⇒ Object
Returns the value of attribute import_result.
Attributes inherited from Role
Class Method Summary collapse
- .[](name) ⇒ Object
- .all_classes(base_kpath = 'N', without_list = nil) ⇒ Object
- .expire_cache! ⇒ Object
- .find_by_id(id) ⇒ Object
- .find_by_kpath(kpath) ⇒ Object
- .find_by_name(name) ⇒ Object
-
.safe_method_type(signature, receiver) ⇒ Object
Methods on VirtualClass instances, not nodes.
-
.split_kpath(kpath) ⇒ Object
Class path hierarchy.
Instance Method Summary collapse
-
#<(other_class) ⇒ Object
Test ancestry.
-
#<=(other_class) ⇒ Object
Test ancestry.
-
#ancestors ⇒ Object
This is used by RubyLess in method signatures: [:zen_path, #<VirtualClass ‘Post’>] —> [:zen_path, Node].
-
#build_query(*args) ⇒ Object
Build SQLiss query.
-
#classes_for_form(opts = {}) ⇒ Object
FIXME: how to make sure all sub-classes of Node are loaded before this is called ? TODO: move into helper.
- #content_type_re ⇒ Object
-
#create_instance(*args) ⇒ Object
Create new nodes instances of this VirtualClass.
-
#defined_safe_columns ⇒ Object
Return safe columns including super class’s safe columns.
-
#do_find(*args) ⇒ Object
Execute find.
- #export ⇒ Object
-
#filtered_relations(group_filter) ⇒ Object
List all relations that can be set for this class, filtering by relation group.
-
#index_groups ⇒ Object
Cache index groups.
-
#kpath_match?(kpath) ⇒ Boolean
check inheritance chain through kpath.
-
#load_attached_roles! ⇒ Object
Include all roles into the this schema.
-
#new_instance(hash = {}) ⇒ Object
Build new nodes instances of this VirtualClass.
-
#query_compiler ⇒ Object
Return the SQLiss query compiler.
- #real_class ⇒ Object
- #real_class=(klass) ⇒ Object
-
#real_class? ⇒ Boolean
Return true if the class reflects a real class (proxy for Ruby class).
-
#safe_column_types ⇒ Object
Returns a hash of all column types that are RubyLess safe (declared as safe in a real class or just dynamic properties declared in the DB).
-
#safe_columns ⇒ Object
Return safe columns including super class’s safe columns.
-
#safe_method_type(signature, receiver = nil) ⇒ Object
We use the VirtualClass to act as a proxy for the real class when resolving RubyLess methods.
-
#sorted_roles ⇒ Object
List all roles ordered by ascending kpath and name.
- #split_kpath ⇒ Object
- #sub_classes ⇒ Object
-
#superclass ⇒ Object
Return virtual class’ super class or Node for the virtual class of Node.
- #to_s ⇒ Object
-
#zafu_new(hash = {}) ⇒ Object
Build new nodes instances of this VirtualClass from zafu.
Methods included from Zena::Use::ScopeIndex::VirtualClassMethods
Methods included from Zena::Use::PropEval::VirtualClassMethods
Methods included from Zena::Use::Fulltext::VirtualClassMethods
Methods included from Zena::Use::Relations::ClassMethods
Methods inherited from Role
#defined_safe_column, export, #icon=, import, #import_columns, #import_relations, #superclass=
Class Attribute Details
.caches_by_site ⇒ Object
Returns the value of attribute caches_by_site.
208 209 210 |
# File 'app/models/virtual_class.rb', line 208 def caches_by_site @caches_by_site end |
.export_attributes ⇒ Object
Returns the value of attribute export_attributes.
34 35 36 |
# File 'app/models/virtual_class.rb', line 34 def export_attributes @export_attributes end |
Instance Attribute Details
#import_result ⇒ Object
Returns the value of attribute import_result.
39 40 41 |
# File 'app/models/virtual_class.rb', line 39 def import_result @import_result end |
Class Method Details
.[](name) ⇒ Object
210 211 212 |
# File 'app/models/virtual_class.rb', line 210 def [](name) find_by_name(name) end |
.all_classes(base_kpath = 'N', without_list = nil) ⇒ Object
231 232 233 |
# File 'app/models/virtual_class.rb', line 231 def all_classes(base_kpath = 'N', without_list = nil) (self.caches_by_site[current_site.id] ||= Cache.new).all_classes(base_kpath, without_list) end |
.expire_cache! ⇒ Object
226 227 228 229 |
# File 'app/models/virtual_class.rb', line 226 def expire_cache! Zena::Db.set_attribute(current_site, 'roles_updated_at', Time.now.utc) self.caches_by_site[current_site.id] = Cache.new end |
.find_by_id(id) ⇒ Object
214 215 216 |
# File 'app/models/virtual_class.rb', line 214 def find_by_id(id) (self.caches_by_site[current_site.id] ||= Cache.new).find_by_id(id) end |
.find_by_kpath(kpath) ⇒ Object
218 219 220 |
# File 'app/models/virtual_class.rb', line 218 def find_by_kpath(kpath) (self.caches_by_site[current_site.id] ||= Cache.new).find_by_kpath(kpath) end |
.find_by_name(name) ⇒ Object
222 223 224 |
# File 'app/models/virtual_class.rb', line 222 def find_by_name(name) (self.caches_by_site[current_site.id] ||= Cache.new).find_by_name(name) end |
.safe_method_type(signature, receiver) ⇒ Object
Methods on VirtualClass instances, not nodes.
66 67 68 69 70 71 72 |
# File 'app/models/virtual_class.rb', line 66 def self.safe_method_type(signature, receiver) if signature.first == 'new' {:method => 'zafu_new', :class => receiver.literal} else super end end |
.split_kpath(kpath) ⇒ Object
Class path hierarchy. Example for (Post) : N, NN, NNP
75 76 77 78 79 |
# File 'app/models/virtual_class.rb', line 75 def self.split_kpath(kpath) parts = [] kpath.split(//).each_index { |i| parts << kpath[0..i] } parts end |
Instance Method Details
#<(other_class) ⇒ Object
Test ancestry
485 486 487 488 489 490 491 492 493 494 495 496 |
# File 'app/models/virtual_class.rb', line 485 def <(other_class) if other_class.kind_of?(VirtualClass) kpath = other_class.kpath self.kpath != kpath && self.kpath[0..(kpath.length-1)] == kpath elsif real_class.kpath != self.kpath # Sub class of real_class real_class <= other_class else # VirtualClass of the real_class real_class < other_class end end |
#<=(other_class) ⇒ Object
Test ancestry
475 476 477 478 479 480 481 482 |
# File 'app/models/virtual_class.rb', line 475 def <=(other_class) if other_class.kind_of?(VirtualClass) kpath = other_class.kpath self.kpath[0..(kpath.length-1)] == kpath else real_class <= other_class end end |
#ancestors ⇒ Object
This is used by RubyLess in method signatures: [:zen_path, #<VirtualClass ‘Post’>] —> [:zen_path, Node]
470 471 472 |
# File 'app/models/virtual_class.rb', line 470 def ancestors @ancestors ||= [real_class] + real_class.ancestors end |
#build_query(*args) ⇒ Object
Build SQLiss query.
505 506 507 |
# File 'app/models/virtual_class.rb', line 505 def build_query(*args) real_class.build_query(*args) end |
#classes_for_form(opts = {}) ⇒ Object
FIXME: how to make sure all sub-classes of Node are loaded before this is called ? TODO: move into helper
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 |
# File 'app/models/virtual_class.rb', line 240 def classes_for_form(opts={}) group_ids = visitor.group_ids if klass = opts.delete(:class) if klass = VirtualClass[klass] base_kpath = klass.kpath else base_kpath = self.kpath end else base_kpath = self.kpath end kpath_len = base_kpath.size attribute = opts[:class_attr] || 'name' VirtualClass.all_classes(base_kpath, opts[:without]).map do |vclass| # Only insert allowed classes. if vclass.create_group_id.nil? || group_ids.include?(vclass.create_group_id) # white spaces are insecable spaces (not ' ') a, b = vclass.kpath, vclass.name [(' ' * (a.size - kpath_len)) + b, vclass[attribute]] else nil end end.compact end |
#content_type_re ⇒ Object
272 273 274 275 |
# File 'app/models/virtual_class.rb', line 272 def content_type_re # if content_type is empty => match all @content_type_re ||= %r{^#{content_type || ".*"}$} end |
#create_instance(*args) ⇒ Object
Create new nodes instances of this VirtualClass
525 526 527 528 529 |
# File 'app/models/virtual_class.rb', line 525 def create_instance(*args) obj = self.new_instance(*args) obj.save obj end |
#defined_safe_columns ⇒ Object
Return safe columns including super class’s safe columns
410 411 412 413 414 415 416 417 418 419 420 421 422 |
# File 'app/models/virtual_class.rb', line 410 def defined_safe_columns @defined_safe_columns ||= if real_class? # Get columns from the 'native' schema of the real class (this schema is a Property::Role, # not a VirtualClass or ::Role). # # Only columns explicitly declared safe are safe here real_class.schema.defined_columns.values.select do |col| real_class.safe_method_type([col.name]) end.sort {|a,b| a.name <=> b.name} else super end end |
#do_find(*args) ⇒ Object
Execute find
510 511 512 |
# File 'app/models/virtual_class.rb', line 510 def do_find(*args) real_class.do_find(*args) end |
#export ⇒ Object
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 |
# File 'app/models/virtual_class.rb', line 298 def export res = super self.class.export_attributes.each do |k| value = self.send(k) next if value.blank? res[k] = value end subclasses = secure(::Role) do ::Role.find(:all, :conditions => [ '(kpath LIKE ? and type = ?) OR (kpath = ? AND id <> ?)', "#{kpath}_", "VirtualClass", kpath, self.id.to_i ], :order => 'kpath ASC') end if real_class? # insert native subclasses Node.native_classes.each do |kpath, klass| if kpath =~ /\A#{self.kpath}.\Z/ subclasses ||= [] subclasses << VirtualClass[klass.name] end end if subclasses subclasses.sort! do |a,b| if a.class != b.class # Roles before subclasses b.class <=> a.class else a.kpath <=> b.kpath end end end end if subclasses subclasses.each do |sub| res[sub.name] = sub.export end end relations = Relation.all( :conditions => ['source_kpath = ? AND site_id = ?', kpath, site_id], :order => 'target_role ASC' ) if !relations.empty? res['relations'] = list = Zafu::OrderedHash.new relations.each do |rel| list[rel.target_role] = rel.export end end res end |
#filtered_relations(group_filter) ⇒ Object
List all relations that can be set for this class, filtering by relation group.
545 546 547 |
# File 'app/models/virtual_class.rb', line 545 def filtered_relations(group_filter) all_relations(nil, group_filter) end |
#index_groups ⇒ Object
Cache index groups
550 551 552 |
# File 'app/models/virtual_class.rb', line 550 def index_groups @index_groups ||= super end |
#kpath_match?(kpath) ⇒ Boolean
check inheritance chain through kpath
351 352 353 |
# File 'app/models/virtual_class.rb', line 351 def kpath_match?(kpath) self.kpath =~ /^#{kpath}/ end |
#load_attached_roles! ⇒ Object
Include all roles into the this schema. By including the superclass and all roles related to this class.
279 280 281 282 283 284 285 286 287 288 289 290 291 292 |
# File 'app/models/virtual_class.rb', line 279 def load_attached_roles! return if @attached_roles_loaded super_kpath = kpath[0..-2] if super_kpath != '' include_role VirtualClass.find_by_kpath(super_kpath) end attached_roles.each do |role| include_role role end @attached_roles_loaded = true end |
#new_instance(hash = {}) ⇒ Object
Build new nodes instances of this VirtualClass
515 516 517 |
# File 'app/models/virtual_class.rb', line 515 def new_instance(hash={}) real_class.new(hash, self) end |
#query_compiler ⇒ Object
Return the SQLiss query compiler.
500 501 502 |
# File 'app/models/virtual_class.rb', line 500 def query_compiler real_class.query_compiler end |
#real_class ⇒ Object
531 532 533 534 535 536 537 |
# File 'app/models/virtual_class.rb', line 531 def real_class @real_class ||= begin klass = Module::const_get(self[:real_class] || 'Node') raise NameError unless klass.ancestors.include?(Node) klass end end |
#real_class=(klass) ⇒ Object
539 540 541 |
# File 'app/models/virtual_class.rb', line 539 def real_class=(klass) @real_class = klass end |
#real_class? ⇒ Boolean
Return true if the class reflects a real class (proxy for Ruby class).
356 357 358 |
# File 'app/models/virtual_class.rb', line 356 def real_class? @is_real_class end |
#safe_column_types ⇒ Object
Returns a hash of all column types that are RubyLess safe (declared as safe in a real class or just dynamic properties declared in the DB). In the Role: everything is safe (see VirtualClass#safe_column_types).
437 438 439 440 441 |
# File 'app/models/virtual_class.rb', line 437 def safe_column_types @safe_column_types ||= Hash[*safe_columns.map do |column| [column.name, RubyLess::SafeClass.safe_method_type_for_column(column, true)] end.flatten] end |
#safe_columns ⇒ Object
Return safe columns including super class’s safe columns. The columns are sorted by kpath, origin (VirtualClass first, Role next) and name.
426 427 428 429 430 431 432 |
# File 'app/models/virtual_class.rb', line 426 def safe_columns @safe_columns ||= begin (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 end |
#safe_method_type(signature, receiver = nil) ⇒ Object
We use the VirtualClass to act as a proxy for the real class when resolving RubyLess methods. If the class reflects a ‘real’ class, only the methods explicitely declared as safe are safe. If the VirtualClass reflects a virtual class, all properties are considered safe.
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 |
# File 'app/models/virtual_class.rb', line 366 def safe_method_type(signature, receiver = nil) if type = real_class.safe_method_type(signature, receiver) return type elsif signature.size == 1 method = signature.first if receiver && (query = receiver.opts[:query]) if query.select_keys.include?(method) # Resolve by using information in the SELECT part # of the custom_query that found this node # In order to use types other then String, we use the overwritten property's # type. if type_name = query.types[method] klass = Node.cast_to_class(type_name) if klass == String return {:method => "attributes[#{method.inspect}]", :nil => true, :class => klass} elsif klass return {:method => "rcast(#{method.inspect}, #{type_name.inspect})", :nil => true, :class => klass} else return nil end elsif type = safe_column_types[method] return type.merge(:method => "attributes[#{method.inspect}]", :nil => true) elsif type = real_class.safe_method_type(signature) return type.merge(:nil => true) else return {:class => String, :method => "attributes[#{method.inspect}]", :nil => true} end end end if type = safe_column_types[method] type elsif method =~ /^(.+)_((id|zip|status|comment)(s?))\Z/ #&& !instance_methods.include?(method) return nil if $1 == 'cached_role' key = $3 == 'id' ? "zip#{$4}" : $2 {:method => "rel[#{$1.inspect}].try(:other_#{key})", :nil => true, :class => ($4.blank? ? Number : [Number])} end else nil end end |
#sorted_roles ⇒ Object
List all roles ordered by ascending kpath and name
444 445 446 447 448 449 450 451 452 453 454 455 456 457 |
# File 'app/models/virtual_class.rb', line 444 def sorted_roles @sorted_roles ||= begin res = [] 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| res << role unless role.defined_safe_columns.empty? end res.flatten! res end end |
#split_kpath ⇒ Object
81 82 83 |
# File 'app/models/virtual_class.rb', line 81 def split_kpath @split_kpath ||= VirtualClass.split_kpath(kpath) end |
#sub_classes ⇒ Object
268 269 270 |
# File 'app/models/virtual_class.rb', line 268 def sub_classes @sub_classes ||= VirtualClass.all_classes(self.kpath).sort {|a,b| a.name <=> b.name} end |
#superclass ⇒ Object
Return virtual class’ super class or Node for the virtual class of Node.
461 462 463 464 465 466 467 |
# File 'app/models/virtual_class.rb', line 461 def superclass if kpath && kpath.size > 1 VirtualClass.find_by_kpath(kpath[0..-2]) else Node end end |
#to_s ⇒ Object
294 295 296 |
# File 'app/models/virtual_class.rb', line 294 def to_s name end |
#zafu_new(hash = {}) ⇒ Object
Build new nodes instances of this VirtualClass from zafu.
520 521 522 |
# File 'app/models/virtual_class.rb', line 520 def zafu_new(hash={}) real_class.new(Node.transform_attributes(hash.stringify_keys), self) end |