| 1 | require 'model_helper' |
|---|
| 2 | require 'node_link_association' |
|---|
| 3 | require 'anchor' |
|---|
| 4 | require 'nav_attribute' |
|---|
| 5 | |
|---|
| 6 | class Node < AbstractModel |
|---|
| 7 | include InheritanceCollector |
|---|
| 8 | |
|---|
| 9 | property :created_at, "Time" |
|---|
| 10 | property :updated_at, "Time" |
|---|
| 11 | |
|---|
| 12 | has_many :links, :class_name => "NodeLink", :dependent => true, :order => "type, position" |
|---|
| 13 | has_many :link_references, :class_name => "NodeLink", :foreign_key => "target_node_id", :dependent => true, :order => "type, position" |
|---|
| 14 | |
|---|
| 15 | attr_accessor :container |
|---|
| 16 | |
|---|
| 17 | after_update :refresh_cache |
|---|
| 18 | after_create :refresh_cache |
|---|
| 19 | after_save :refresh_cache |
|---|
| 20 | after_destroy :refresh_cache |
|---|
| 21 | |
|---|
| 22 | @@find_cache = {} |
|---|
| 23 | |
|---|
| 24 | def Node.refresh_cache |
|---|
| 25 | @@find_cache = {} |
|---|
| 26 | end |
|---|
| 27 | |
|---|
| 28 | def refresh_cache |
|---|
| 29 | Node.refresh_cache |
|---|
| 30 | end |
|---|
| 31 | |
|---|
| 32 | class << self |
|---|
| 33 | |
|---|
| 34 | def find_by_sql(sql, raw_records = false) |
|---|
| 35 | sql = sanitize_sql(sql) |
|---|
| 36 | sql_hash = MD5.hexdigest(sql + raw_records.to_s) |
|---|
| 37 | |
|---|
| 38 | unless @@find_cache[sql_hash].nil? |
|---|
| 39 | return @@find_cache[sql_hash] |
|---|
| 40 | end |
|---|
| 41 | |
|---|
| 42 | records = connection.select(sql) |
|---|
| 43 | if raw_records then |
|---|
| 44 | records = records |
|---|
| 45 | else |
|---|
| 46 | records = records.inject([]) { |objects, record| objects << instantiate(record) } |
|---|
| 47 | end |
|---|
| 48 | @@find_cache[sql_hash] = records |
|---|
| 49 | records |
|---|
| 50 | end |
|---|
| 51 | |
|---|
| 52 | end |
|---|
| 53 | |
|---|
| 54 | |
|---|
| 55 | def self.reset_nav_classes_cache |
|---|
| 56 | @@nav_classes = nil |
|---|
| 57 | @@nav_attributes = nil |
|---|
| 58 | end |
|---|
| 59 | |
|---|
| 60 | def nav_class |
|---|
| 61 | @@nav_classes ||= {} |
|---|
| 62 | if @@nav_classes.include?(self.class.name) |
|---|
| 63 | return @@nav_classes[self.class.name] |
|---|
| 64 | else |
|---|
| 65 | return refresh_nav_class |
|---|
| 66 | end |
|---|
| 67 | end |
|---|
| 68 | |
|---|
| 69 | def nav_attributes |
|---|
| 70 | @@nav_attributes ||= {} |
|---|
| 71 | if @@nav_attributes.include?(self.class.name) |
|---|
| 72 | return @@nav_attributes[self.class.name] |
|---|
| 73 | else |
|---|
| 74 | return refresh_nav_attributes |
|---|
| 75 | end |
|---|
| 76 | end |
|---|
| 77 | |
|---|
| 78 | def refresh_nav_class |
|---|
| 79 | nc = NavClass.find_first ["name = ?", self.class.name] |
|---|
| 80 | @@nav_classes[self.class.name] = nc |
|---|
| 81 | end |
|---|
| 82 | |
|---|
| 83 | def refresh_nav_attributes |
|---|
| 84 | na = attribute_collect(nav_class).inject({}) do |hash, attr| |
|---|
| 85 | hash[attr.name] = attr |
|---|
| 86 | hash |
|---|
| 87 | end |
|---|
| 88 | @@nav_attributes[self.class.name] = na |
|---|
| 89 | end |
|---|
| 90 | |
|---|
| 91 | def after_save |
|---|
| 92 | after_node_save |
|---|
| 93 | end |
|---|
| 94 | |
|---|
| 95 | def after_node_save() end |
|---|
| 96 | |
|---|
| 97 | def after_destroy |
|---|
| 98 | NodeLink.destroy_all ["node_id = ? OR target_node_id = ?", self.id, self.id] |
|---|
| 99 | after_node_delete |
|---|
| 100 | end |
|---|
| 101 | |
|---|
| 102 | def after_node_delete() end |
|---|
| 103 | |
|---|
| 104 | def label |
|---|
| 105 | if nav_class.label_attribute? |
|---|
| 106 | read_attribute(nav_class.label_attribute).to_s rescue id.to_s |
|---|
| 107 | else |
|---|
| 108 | id.to_s |
|---|
| 109 | end |
|---|
| 110 | end |
|---|
| 111 | |
|---|
| 112 | def nav_operations |
|---|
| 113 | @nav_operations ||= read_nav_operations |
|---|
| 114 | end |
|---|
| 115 | |
|---|
| 116 | def read_nav_operations |
|---|
| 117 | ops = operation_collect(nav_class) |
|---|
| 118 | ops = ops.inject({}) { |hash, op| hash[op.name] = op; hash } |
|---|
| 119 | end |
|---|
| 120 | |
|---|
| 121 | def method_missing(method_id, *arguments) |
|---|
| 122 | method_name = method_id.id2name |
|---|
| 123 | |
|---|
| 124 | if method_name =~ read_method? |
|---|
| 125 | return read_attribute($1) if nav_attributes.include?($1) |
|---|
| 126 | return read_link($1) if node_links.include?($1) |
|---|
| 127 | return do_operation($1, *arguments) if nav_operations.include?($1) |
|---|
| 128 | super |
|---|
| 129 | elsif method_name =~ query_method? |
|---|
| 130 | return query_attribute($1) if nav_attributes.include?($1) |
|---|
| 131 | return query_link($1) if node_links.include?($1) |
|---|
| 132 | super |
|---|
| 133 | else |
|---|
| 134 | super |
|---|
| 135 | end |
|---|
| 136 | end |
|---|
| 137 | |
|---|
| 138 | def do_operation(op_name, *arguments) |
|---|
| 139 | op = nav_operations[op_name] |
|---|
| 140 | code = op.code |
|---|
| 141 | params = op.params if op.params && !op.params.empty? |
|---|
| 142 | |
|---|
| 143 | begin |
|---|
| 144 | buffer = <<-EOS |
|---|
| 145 | def self.#{op_name}(context = nil, controller = nil #{params ? ',' + params : ''}) |
|---|
| 146 | #{code} |
|---|
| 147 | end |
|---|
| 148 | EOS |
|---|
| 149 | instance_eval(buffer, op_name, 0) |
|---|
| 150 | ret = send(op_name, *arguments) |
|---|
| 151 | rescue => ex |
|---|
| 152 | ln = -1 |
|---|
| 153 | buffer_with_ln = buffer.split("\n").map { |line| ln += 1; "#{ln}:#{line}" }.join("\n") |
|---|
| 154 | err_buffer = "ERROR on trying operation #{op_name}...\nSOURCE CODE:\n#{buffer_with_ln}\nMESSAGE: #{ex.message}\nSTACK TRACE:\n#{ex.backtrace.join("\n")}" |
|---|
| 155 | logger.info err_buffer |
|---|
| 156 | $stdout.puts err_buffer |
|---|
| 157 | raise |
|---|
| 158 | end |
|---|
| 159 | ret |
|---|
| 160 | end |
|---|
| 161 | |
|---|
| 162 | def node_links |
|---|
| 163 | @node_links ||= load_links |
|---|
| 164 | end |
|---|
| 165 | |
|---|
| 166 | def load_links |
|---|
| 167 | node_links = {} |
|---|
| 168 | for l in (nav_class.links_all) |
|---|
| 169 | k = l.name.underscore |
|---|
| 170 | node_links[k] = { :link => l, :nodes => nil } |
|---|
| 171 | end |
|---|
| 172 | node_links |
|---|
| 173 | end |
|---|
| 174 | |
|---|
| 175 | def read_link(name) |
|---|
| 176 | return nil if node_links[name].nil? |
|---|
| 177 | sql = %{ {link} sr:target_node_id {id}; rdf:type {?}; |
|---|
| 178 | sr:node_id {?}; [ sr:position {node_link_position} ] } |
|---|
| 179 | link = node_links[name][:link] |
|---|
| 180 | nodes = node_links[name][:nodes] |
|---|
| 181 | if nodes.nil? |
|---|
| 182 | target_klass = Object.const_get(link.target_nav_class.name) |
|---|
| 183 | nodes = target_klass.find_all nil, "node_link_position", |
|---|
| 184 | nil, [sql, link.name.to_uri, self.id] |
|---|
| 185 | nodes.extend(NodeLinkAssociation) |
|---|
| 186 | nodes.parent_node = self |
|---|
| 187 | nodes.link_type = link.name |
|---|
| 188 | node_links[name][:nodes] = nodes |
|---|
| 189 | end |
|---|
| 190 | return nodes |
|---|
| 191 | end |
|---|
| 192 | |
|---|
| 193 | def query_link(name) |
|---|
| 194 | node_links.include?(name) |
|---|
| 195 | end |
|---|
| 196 | |
|---|
| 197 | def query_attribute(name) |
|---|
| 198 | not read_attribute(name).nil? |
|---|
| 199 | end |
|---|
| 200 | |
|---|
| 201 | def read_attribute(attr_name) |
|---|
| 202 | val = super(attr_name) |
|---|
| 203 | if val.nil? |
|---|
| 204 | begin |
|---|
| 205 | attr_def = nav_attributes[attr_name] |
|---|
| 206 | case attr_def |
|---|
| 207 | when IndexNavAttribute |
|---|
| 208 | val = Index.find(attr_def.index.id) |
|---|
| 209 | params_values = instance_eval(attr_def.params) if attr_def.params? |
|---|
| 210 | val.entries(params_values) |
|---|
| 211 | val.container = self |
|---|
| 212 | when ContextAnchorNavAttribute |
|---|
| 213 | val = ContextAnchorValue.new(self, attr_def) |
|---|
| 214 | val.container = self |
|---|
| 215 | when IndexAnchorNavAttribute |
|---|
| 216 | val = IndexAnchorValue.new(self, attr_def) |
|---|
| 217 | val.container = self |
|---|
| 218 | when ComputedNavAttribute |
|---|
| 219 | val = instance_eval(attr_def.code) rescue nil |
|---|
| 220 | else |
|---|
| 221 | val = nil |
|---|
| 222 | end |
|---|
| 223 | rescue => ex |
|---|
| 224 | logger.error ex.inspect |
|---|
| 225 | raise "Could not read '#{self.class.name}##{attr_name}' value for node '#{self.id}'\n#{ex}." |
|---|
| 226 | end |
|---|
| 227 | end |
|---|
| 228 | val |
|---|
| 229 | end |
|---|
| 230 | |
|---|
| 231 | end |
|---|