Class: FlexiRecord::Reference
- Inherits:
-
Object
- Object
- FlexiRecord::Reference
- Defined in:
- lib/flexirecord.rb
Overview
Objects of this class are used to describe a reference between two tables. You can create and register them by calling FlexiRecord::BaseRecord.add_many_to_one_reference or FlexiRecord::RaseRecord.add_one_to_one_reference. For using many-to-many relationships you have to extend the class FlexiRecord::Relationship and create two connected ManyToOneReference’s for that class by calling FlexiRecord::BaseRecord.add_connected_references.
Direct Known Subclasses
Instance Attribute Summary collapse
-
#destination_class ⇒ Object
readonly
Class, whose objects are referred by others.
-
#destination_columns ⇒ Object
readonly
Columns in the referred class, providing a unique or primary key.
-
#dst_to_src_attr ⇒ Object
readonly
Name (String) of the attribute in the destination class, which is referring to one or many objects of the source class.
-
#source_class ⇒ Object
readonly
Class, whose objects are referring to others.
-
#source_columns ⇒ Object
readonly
Columns in the referring class, providing the foreign key.
-
#src_to_dst_attr ⇒ Object
readonly
Name (String) of the attribute in the source class, which is referring to one object of the destination class.
Instance Method Summary collapse
-
#combine(*arguments) ⇒ Object
Deprecated.
-
#connect(other_reference, own_attr, other_attr) ⇒ Object
Connects two ManyToOneReference’s to form a many-to-many relation.
-
#initialize(source_class, destination_class, column_info, src_to_dst_attr, dst_to_src_attr = nil) ⇒ Reference
constructor
Returns a new reference object, describing a relation where objects of the ‘source_class’ refer to objects of the ‘destination_class’.
-
#many_to_one? ⇒ Boolean
Returns true, if the object describes a many-to-one relation.
-
#one_to_one? ⇒ Boolean
Returns true, if the object describes a one-to-one relation.
Constructor Details
#initialize(source_class, destination_class, column_info, src_to_dst_attr, dst_to_src_attr = nil) ⇒ Reference
Returns a new reference object, describing a relation where objects of the ‘source_class’ refer to objects of the ‘destination_class’. The ‘column_info’ field describes the columns used for that reference. If the ‘column_info’ field is a string, the primary key is used in the destination class, and the primary key prefixed by the string given in ‘column_info’ is used as the foreign key in the source class. If ‘column_info’ is an array, it contains the columns in the source class, followed by the columns in the destination class. The field ‘src_to_dst_attr’ contains the name of the attribute in the source class, which is referring to one object of the destination class. The field ‘dst_to_src_attr’ contains the name of the attribute in the destination class, which is referring to one or many objects of the source class. After the reference has been created, reader, loader and setter functions are added to the ‘source_class’ and ‘destination_class’, to provide access to referenced and referring objects. This method is private, use one of the child classes OneToOneReference or ManyToOneReference, which get the same arguments, to generate references of a given type.
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 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 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 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 |
# File 'lib/flexirecord.rb', line 181 def initialize(source_class, destination_class, column_info, src_to_dst_attr, dst_to_src_attr=nil) unless source_class.kind_of? Class and destination_class.kind_of? Class raise TypeError, "Class expected" end @source_class = source_class @destination_class = destination_class if column_info.respond_to? :to_ary column_info = column_info.to_ary.flatten unless column_info.length % 2 == 0 raise ArgumentError, "Flattened 'column_info' array contains odd number of elements." end @source_columns = column_info[0, column_info.length / 2].collect { |column| column.to_s.dup.freeze } @destination_columns = column_info[column_info.length / 2, column_info.length / 2].collect { |column| column.to_s.dup.freeze } elsif column_info.respond_to? :to_str column_info = column_info.to_str @source_columns = [] @destination_columns = [] destination_class.primary_columns.each do |column| @source_columns << "#{column_info}#{column}".freeze @destination_columns << column end else raise ArgumentError, "Array or String expected" end @source_columns.freeze @destination_columns.freeze @src_to_dst_attr = src_to_dst_attr ? src_to_dst_attr.to_s.dup.freeze : nil @dst_to_src_attr = dst_to_src_attr ? dst_to_src_attr.to_s.dup.freeze : nil # Work at the source_class: if @src_to_dst_attr @source_class.set_loader(@src_to_dst_attr) do |source_records, arguments| unless arguments.empty? raise ArgumentError, "No extra arguments may be specified for outgoing reference columns." end destination_records = @destination_class.select_by_value_set( @destination_columns, source_records.collect { |source_record| @source_columns.collect { |column| source_record[column] } }, *arguments ) destination_record_hash = {} destination_records.each do |destination_record| destination_record_hash[@destination_columns.collect { |column| destination_record[column] }] = destination_record if self.one_to_one? and @dst_to_src_attr destination_record[@dst_to_src_attr] = nil end end source_records.each do |source_record| destination_record = destination_record_hash[@source_columns.collect { |column| source_record[column] }] source_record[@src_to_dst_attr, *arguments] = destination_record if destination_record and self.one_to_one? and @dst_to_src_attr destination_record[@dst_to_src_attr] = source_record end end next destination_records end @source_columns.each_index do |column_index| source_column = @source_columns[column_index] destination_column = @destination_columns[column_index] @source_class.set_reader(source_column) do |source_record, arguments| destination_record = source_record[@src_to_dst_attr] next destination_record ? destination_record[destination_column] : source_record[source_column] end @source_class.set_setter(source_column) do |source_record, value| source_record.delete_from_cache(@src_to_dst_attr) source_record[source_column] = value end end if self.one_to_one? and @dst_to_src_attr @source_class.set_setter(@src_to_dst_attr) do |source_record, value| old_destination_record = source_record[@src_to_dst_attr] if old_destination_record old_destination_record[@dst_to_src_attr] = nil end source_record[@src_to_dst_attr] = value if value value[@dst_to_src_attr] = source_record end end end end # Work at the destination_class: if @dst_to_src_attr @destination_class.set_loader(@dst_to_src_attr) do |destination_records, arguments| unless arguments.empty? unless arguments[0].respond_to? :to_str raise "First argument of reader method is not a SQL snippet string." end arguments[0] = arguments[0].to_str end source_records = @source_class.select_by_value_set( @source_columns, destination_records.collect { |destination_record| @destination_columns.collect { |column| destination_record[column] } }, *arguments ) destination_record_hash = {} destination_records.each do |destination_record| (destination_record_hash[@destination_columns.collect { |column| destination_record[column] }] ||= []) << destination_record destination_record[@dst_to_src_attr, *arguments] = if self.many_to_one? FlexiRecord::RecordArray.new(@source_class) else nil end end source_records.each do |source_record| matching_destination_records = destination_record_hash[@source_columns.collect { |column| source_record[column] }] || [] if @src_to_dst_attr source_record[@src_to_dst_attr] = matching_destination_records.first end matching_destination_records.each do |destination_record| if self.many_to_one? destination_record[@dst_to_src_attr, *arguments] << source_record else destination_record[@dst_to_src_attr, *arguments] = source_record end end end if self.many_to_one? destination_records.each do |destination_record| destination_record[@dst_to_src_attr, *arguments].freeze end end next source_records end if self.one_to_one? and @src_to_dst_attr @destination_class.set_setter(@dst_to_src_attr) do |destination_record, value| old_source_record = destination_record[@dst_to_src_attr] if old_source_record old_source_record[@src_to_dst_attr] = nil end destination_record[@dst_to_src_attr] = value if value value[@src_to_dst_attr] = destination_record end end end end return self end |
Instance Attribute Details
#destination_class ⇒ Object (readonly)
Class, whose objects are referred by others.
402 403 404 |
# File 'lib/flexirecord.rb', line 402 def destination_class @destination_class end |
#destination_columns ⇒ Object (readonly)
Columns in the referred class, providing a unique or primary key.
408 409 410 |
# File 'lib/flexirecord.rb', line 408 def destination_columns @destination_columns end |
#dst_to_src_attr ⇒ Object (readonly)
Name (String) of the attribute in the destination class, which is referring to one or many objects of the source class.
414 415 416 |
# File 'lib/flexirecord.rb', line 414 def dst_to_src_attr @dst_to_src_attr end |
#source_class ⇒ Object (readonly)
Class, whose objects are referring to others.
399 400 401 |
# File 'lib/flexirecord.rb', line 399 def source_class @source_class end |
#source_columns ⇒ Object (readonly)
Columns in the referring class, providing the foreign key.
405 406 407 |
# File 'lib/flexirecord.rb', line 405 def source_columns @source_columns end |
#src_to_dst_attr ⇒ Object (readonly)
Name (String) of the attribute in the source class, which is referring to one object of the destination class.
411 412 413 |
# File 'lib/flexirecord.rb', line 411 def src_to_dst_attr @src_to_dst_attr end |
Instance Method Details
#combine(*arguments) ⇒ Object
Deprecated. Use FlexiRecord::Reference#connect instead.
394 395 396 |
# File 'lib/flexirecord.rb', line 394 def combine(*arguments) self.connect(*arguments) end |
#connect(other_reference, own_attr, other_attr) ⇒ Object
Connects two ManyToOneReference’s to form a many-to-many relation. ‘other_reference’ is another Reference object (as returned by FlexiRecord::Reference.new, ‘own_attr’ is the attribute to be installed in the destination_class of this Reference object for accessing objects of the destination_class of the ‘other_reference’ object, and ‘other_attr’ is the attribute to be installed in the destination_class of the ‘other_reference’ object for accessing objects of the destination_class of this Reference object.
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 358 359 360 361 362 363 364 365 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 |
# File 'lib/flexirecord.rb', line 327 def connect(other_reference, own_attr, other_attr) unless other_reference.kind_of? FlexiRecord::Reference raise TypeError, "Object of class FlexiRecord::Reference expected." end reference1 = self reference2 = other_reference attr1 = own_attr.to_s attr2 = other_attr.to_s unless self.source_class == other_reference.source_class "Combining references having different source classes is not possible." end relationship_class = self.source_class [ [reference1, reference2, attr1, attr2], [reference2, reference1, attr2, attr1] ].each do |source_reference, destination_reference, source_attr, destination_attr| source_class = source_reference.destination_class destination_class = destination_reference.destination_class tmp1 = [] destination_reference.source_columns.each_index do |column_index| tmp1 << [ destination_reference.source_columns[column_index], destination_reference.destination_columns[column_index] ] end source_class.set_loader source_attr do |source_records, arguments| sql_arguments = arguments.dup sql_snippet = sql_arguments.shift unless sql_snippet.nil? unless sql_snippet.respond_to? :to_str raise "First argument of reader method is not a SQL snippet string." end sql_snippet = sql_snippet.to_str end destination_records = unless source_records.empty? destination_class.sql( 'SELECT ' << FlexiRecord::DefaultTableAlias << '.* FROM (' << 'SELECT "obj".*, ' << relationship_class.columns.collect { |column| '"rel"."' << column << '" AS "_flexirecord_rel_' << column << '"' }.join(', ') << ' FROM ' << relationship_class.table << ' "rel" JOIN ' << destination_class.table << ' "obj" ON ' << tmp1.collect { |tmp1a, tmp1b| '"rel"."' << tmp1a << '" = "obj"."' << tmp1b << '"' }.join(' AND ') << ' WHERE (' << source_reference.source_columns.collect { |column| '"rel"."' << column << '"' }.join(', ') << ') IN (' << source_records.collect { |record| '(' << source_reference.source_columns.collect { '$' }.join(', ') << ')' }.join(', ') << ')' << ') AS ' << FlexiRecord::DefaultTableAlias << ' JOIN ' << relationship_class.table << ' ' << FlexiRecord::RelationshipTableAlias << ' ON ' << relationship_class.primary_columns.collect { |column| '' << FlexiRecord::RelationshipTableAlias << '."' << column << '" = ' << FlexiRecord::DefaultTableAlias << '."_flexirecord_rel_' << column << '"' }.join(' AND ') << ' ' << sql_snippet.to_s, *(source_records.collect { |record| source_class.primary_columns.collect { |column| record.read(column) } } + sql_arguments) ) else FlexiRecord::RecordArray.new(destination_class) end destination_record_hash = {} destination_records.each do |destination_record| (destination_record_hash[ source_reference.source_columns.collect { |column| destination_record['_flexirecord_rel_' << column] } ] ||= FlexiRecord::RecordArray.new(destination_class)) << destination_record relationship_hash = { destination_reference.src_to_dst_attr => destination_record } relationship_class.columns.each do |column| relationship_hash[column] = destination_record.delete_from_cache("_flexirecord_rel_#{column}") end destination_record[FlexiRecord::RelationshipColumn] = relationship_class.new(relationship_hash) end source_records.each do |source_record| source_record[source_attr, *arguments] = ((destination_record_hash[source_reference.destination_columns.collect { |column| source_record[column] } ]) || FlexiRecord::RecordArray.new(destination_class)).freeze end next destination_records end end end |
#many_to_one? ⇒ Boolean
Returns true, if the object describes a many-to-one relation.
422 423 424 |
# File 'lib/flexirecord.rb', line 422 def many_to_one? not @one_to_one end |
#one_to_one? ⇒ Boolean
Returns true, if the object describes a one-to-one relation.
417 418 419 |
# File 'lib/flexirecord.rb', line 417 def one_to_one? @one_to_one end |