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_in_db? ⇒ Boolean
-
#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.
217 218 219 |
# File 'app/models/virtual_class.rb', line 217 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
219 220 221 |
# File 'app/models/virtual_class.rb', line 219 def [](name) find_by_name(name) end |
.all_classes(base_kpath = 'N', without_list = nil) ⇒ Object
240 241 242 |
# File 'app/models/virtual_class.rb', line 240 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
235 236 237 238 |
# File 'app/models/virtual_class.rb', line 235 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
223 224 225 |
# File 'app/models/virtual_class.rb', line 223 def find_by_id(id) (self.caches_by_site[current_site.id] ||= Cache.new).find_by_id(id) end |
.find_by_kpath(kpath) ⇒ Object
227 228 229 |
# File 'app/models/virtual_class.rb', line 227 def find_by_kpath(kpath) (self.caches_by_site[current_site.id] ||= Cache.new).find_by_kpath(kpath) end |
.find_by_name(name) ⇒ Object
231 232 233 |
# File 'app/models/virtual_class.rb', line 231 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
501 502 503 504 505 506 507 508 509 510 511 512 |
# File 'app/models/virtual_class.rb', line 501 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
491 492 493 494 495 496 497 498 |
# File 'app/models/virtual_class.rb', line 491 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]
486 487 488 |
# File 'app/models/virtual_class.rb', line 486 def ancestors @ancestors ||= [real_class] + real_class.ancestors end |
#build_query(*args) ⇒ Object
Build SQLiss query.
521 522 523 |
# File 'app/models/virtual_class.rb', line 521 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
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 |
# File 'app/models/virtual_class.rb', line 249 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
281 282 283 284 |
# File 'app/models/virtual_class.rb', line 281 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
541 542 543 544 545 |
# File 'app/models/virtual_class.rb', line 541 def create_instance(*args) obj = self.new_instance(*args) obj.save obj end |
#defined_in_db? ⇒ Boolean
418 419 420 |
# File 'app/models/virtual_class.rb', line 418 def defined_in_db? !id.blank? end |
#defined_safe_columns ⇒ Object
Return safe columns including super class’s safe columns
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 |
# File 'app/models/virtual_class.rb', line 423 def defined_safe_columns @defined_safe_columns ||= begin # If we have list = defined_in_db? ? super : [] 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 list += real_class.schema.defined_columns.values.select do |col| real_class.safe_method_type([col.name]) end end list.sort {|a,b| a.name <=> b.name} end end |
#do_find(*args) ⇒ Object
Execute find
526 527 528 |
# File 'app/models/virtual_class.rb', line 526 def do_find(*args) real_class.do_find(*args) end |
#export ⇒ Object
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 349 350 351 352 353 354 355 356 357 |
# File 'app/models/virtual_class.rb', line 307 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.
561 562 563 |
# File 'app/models/virtual_class.rb', line 561 def filtered_relations(group_filter) all_relations(nil, group_filter) end |
#index_groups ⇒ Object
Cache index groups
566 567 568 |
# File 'app/models/virtual_class.rb', line 566 def index_groups @index_groups ||= super end |
#kpath_match?(kpath) ⇒ Boolean
check inheritance chain through kpath
360 361 362 |
# File 'app/models/virtual_class.rb', line 360 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.
288 289 290 291 292 293 294 295 296 297 298 299 300 301 |
# File 'app/models/virtual_class.rb', line 288 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
531 532 533 |
# File 'app/models/virtual_class.rb', line 531 def new_instance(hash={}) real_class.new(hash, self) end |
#query_compiler ⇒ Object
Return the SQLiss query compiler.
516 517 518 |
# File 'app/models/virtual_class.rb', line 516 def query_compiler real_class.query_compiler end |
#real_class ⇒ Object
547 548 549 550 551 552 553 |
# File 'app/models/virtual_class.rb', line 547 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
555 556 557 |
# File 'app/models/virtual_class.rb', line 555 def real_class=(klass) @real_class = klass end |
#real_class? ⇒ Boolean
Return true if the class reflects a real class (proxy for Ruby class).
365 366 367 |
# File 'app/models/virtual_class.rb', line 365 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).
453 454 455 456 457 |
# File 'app/models/virtual_class.rb', line 453 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.
442 443 444 445 446 447 448 |
# File 'app/models/virtual_class.rb', line 442 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.
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 408 409 410 411 412 413 414 415 416 |
# File 'app/models/virtual_class.rb', line 375 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
460 461 462 463 464 465 466 467 468 469 470 471 472 473 |
# File 'app/models/virtual_class.rb', line 460 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
277 278 279 |
# File 'app/models/virtual_class.rb', line 277 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.
477 478 479 480 481 482 483 |
# File 'app/models/virtual_class.rb', line 477 def superclass if kpath && kpath.size > 1 VirtualClass.find_by_kpath(kpath[0..-2]) else Node end end |
#to_s ⇒ Object
303 304 305 |
# File 'app/models/virtual_class.rb', line 303 def to_s name end |
#zafu_new(hash = {}) ⇒ Object
Build new nodes instances of this VirtualClass from zafu.
536 537 538 |
# File 'app/models/virtual_class.rb', line 536 def zafu_new(hash={}) real_class.new(Node.transform_attributes(hash.stringify_keys), self) end |