Class: InterMine::Metadata::ClassDescriptor

Inherits:
Object
  • Object
show all
Includes:
SetHashKey
Defined in:
lib/intermine/model.rb

Overview

A class representing a table in the InterMine data model

A class descriptor represents a logical abstraction of a table in the InterMine model, and contains information about the columns in the table and the other tables that are referenced by this table.

It can be used to construct queries directly, when obtained from a webservice.

cld = service.model.table('Gene')
cld.where(:symbol => 'zen').each_row {|row| puts row}

:include:contact_header.rdoc

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from SetHashKey

#set_key_value

Constructor Details

#initialize(opts, model) ⇒ ClassDescriptor

ClassDescriptors are constructed automatically when the model itself is parsed. They should not be constructed on their own.

Arguments:

opts

A Hash containing the information to initialise this ClassDescriptor.

model

The model this ClassDescriptor belongs to.


353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
# File 'lib/intermine/model.rb', line 353

def initialize(opts, model)
    @model = model
    @fields = {}
    @klass = nil
    @module = nil

    field_types = {
        "attributes" => AttributeDescriptor,
        "references" => ReferenceDescriptor,
        "collections" => CollectionDescriptor
    }

    opts.each do |k,v|
        if (field_types.has_key?(k))
            v.each do |name, field| 
                @fields[name] = field_types[k].new(field, model)
            end
        else
            set_key_value(k, v)
        end
    end
end

Instance Attribute Details

#fieldsObject (readonly)

The Hash containing the fields of this model


344
345
346
# File 'lib/intermine/model.rb', line 344

def fields
  @fields
end

#modelObject (readonly)

The InterMine Model


341
342
343
# File 'lib/intermine/model.rb', line 341

def model
  @model
end

Instance Method Details

#attributesObject

call-seq:

attributes => Array[AttributeDescriptor]

Returns an Array of all fields in the current table that represent attributes (ie. columns that can hold values, rather than references to other tables.)

The array returned will be sorted in alphabetical order by field-name.


456
457
458
459
460
461
# File 'lib/intermine/model.rb', line 456

def attributes
    return @fields.
        select {|_, v| v.is_a?(AttributeDescriptor)}.
        sort   {|(k0, _), (k1, _)| k0 <=> k1}.
        map    {|(_, v)| v}
end

#get_field(name) ⇒ Object Also known as: field

call-seq:

get_field(name) => FieldDescriptor

Returns the field of the given name if it exists in the referenced table.


432
433
434
# File 'lib/intermine/model.rb', line 432

def get_field(name)
    return @fields[name]
end

#has_field?(name) ⇒ Boolean

call-seq:

has_field?(name) => bool

Returns true if the table has a field of the given name.

Returns:

  • (Boolean)

443
444
445
# File 'lib/intermine/model.rb', line 443

def has_field?(name)
    return @fields.has_key?(name)
end

#inspectObject

Return a fuller string representation.


469
470
471
# File 'lib/intermine/model.rb', line 469

def inspect 
    return "<#{self.class.name}:#{self.object_id} #{to_s}>"
end

#new_queryObject Also known as: query

call-seq:

new_query => PathQuery::Query

Construct a new query for the service this ClassDescriptor belongs to rooted on this table.

query = model.table('Gene').new_query

384
385
386
387
# File 'lib/intermine/model.rb', line 384

def new_query
    q = @model.service.new_query(self.name)
    return q
end

#select(*cols) ⇒ Object

call-seq:

select(*columns) => PathQuery::Query

Construct a new query on this table in the originating service with given columns selected for output.

query = model.table('Gene').select(:symbol, :name, "organism.name", "alleles.*")

query.each_result do |gene|
    puts "#{gene.symbol} (#{gene.organism.name}): #{gene.alleles.size} Alleles"
end

403
404
405
406
407
# File 'lib/intermine/model.rb', line 403

def select(*cols)
    q = new_query
    q.add_views(cols)
    return q
end

#subclass_of?(other) ⇒ Boolean

call-seq:

subclass_of?(other) => bool

Returns true if the class this ClassDescriptor describes is a subclass of the class the other element evaluates to. The other may be a ClassDescriptor, or a Path, or a String describing a path.

model.table('Gene').subclass_of?(model.table('SequenceFeature'))
true.

model.table('Gene').subclass_of?(model.table('Protein'))
false.

Returns:

  • (Boolean)

486
487
488
489
490
491
492
493
494
495
496
497
498
499
# File 'lib/intermine/model.rb', line 486

def subclass_of?(other)
    path = Path.new(other, @model)
    if @extends.include? path.end_type
        return true
    else
        @extends.each do |x|
            superCls = @model.get_cd(x)
            if superCls.subclass_of?(path)
                return true
            end
        end
    end
    return false
end

#to_classObject

call-seq:

to_class => Class

Returns a Class that can be used to instantiate new objects representing rows of data in the InterMine database.


637
638
639
640
641
642
643
644
645
646
647
648
649
# File 'lib/intermine/model.rb', line 637

def to_class
    if @klass.nil?
        mod = to_module
        kls = Class.new(InterMineObject)
        cd = self
        kls.class_eval do
            include mod
            @__cd__ = cd
        end
        @klass = kls
    end
    return @klass
end

#to_moduleObject

call-seq:

to_module => Module

Produces a module containing the logic this ClassDescriptor represents, suitable for including into a class definition.

The use of modules enables multiple inheritance, which is supported in the InterMine data model, to be represented in the classes instantiated in the client.


511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
# File 'lib/intermine/model.rb', line 511

def to_module
    if @module.nil?
        nums = Model::FLOAT_TYPES
        ints = Model::INT_TYPES
        bools = Model::BOOL_TYPES

        supers = @extends.map { |x| @model.get_cd(x).to_module }

        klass = Module.new
        fd_names = @fields.values.map { |x| x.name }
        attr_names = @fields.values.select { |x| x.is_a?(AttributeDescriptor)}.map {|x| x.name}
        klass.class_eval do
            include *supers
            attr_reader *attr_names
        end

        @fields.values.each do |fd|
            if fd.is_a?(CollectionDescriptor)
                klass.class_eval do
                    define_method("add" + fd.name.capitalize) do |*vals|
                        type = fd.referencedType
                        instance_var = instance_variable_get("@" + fd.name)
                        instance_var ||= []
                        vals.each do |item|
                            if item.is_a?(Hash)
                                item = type.model.make_new(type.name, item)
                            end
                            if !item.is_a?(type)
                                raise ArgumentError, "Arguments to #{fd.name} in #{@name} must be #{type.name}s"
                            end
                            instance_var << item
                        end
                        instance_variable_set("@" + fd.name, instance_var)
                    end
                end
            end
            
            if fd.is_a?(ReferenceDescriptor)
                klass.class_eval do 
                    define_method(fd.name) do 
                        instance_var = instance_variable_get("@" + fd.name)
                        if fd.referencedType.model.is_lazy and instance_var.nil? and not instance_variable_get("@" + fd.name + "_ISNULL")
                            q = __cd__.select(:id, fd.name + ".*").where(:id => objectId).outerjoin(fd.name)
                            first_result = q.results.first
                            unless first_result.nil?
                                instance_var = first_result[fd.name]
                                if instance_var.nil?
                                    if fs.is_a?(CollectionDescriptor) and instance_var.nil?
                                        instance_var = []
                                    else
                                        instance_variable_set("@" + fd.name + "_ISNULL", true)
                                    end
                                end
                                instance_variable_set("@" + fd.name, instance_var)
                            end
                        end
                        if instance_var.nil? and fd.is_a?(CollectionDescriptor)
                            return []
                        else
                            return instance_var
                        end
                    end
                end
            end

            klass.class_eval do
                define_method(fd.name + "=") do |val|
                    if fd.is_a?(AttributeDescriptor)
                        type = fd.dataType
                        if nums.include?(type)
                            if !val.is_a?(Numeric)
                                raise ArgumentError, "Arguments to #{fd.name} in #{@name} must be numeric"
                            end
                        elsif ints.include?(type)
                            if !val.is_a?(Integer)
                                raise ArgumentError,  "Arguments to #{fd.name} in #{@name} must be integers"
                            end
                        elsif bools.include?(type)
                            if !val.is_a?(TrueClass) && !val.is_a?(FalseClass)
                                raise ArgumentError,   "Arguments to #{fd.name} in #{@name} must be booleans"
                            end
                        end
                        instance_variable_set("@" + fd.name, val)
                    else
                        type = fd.referencedType
                        if fd.is_a?(CollectionDescriptor)
                            instance_var = []
                            unless val.nil?
                                val.each do |item|
                                    if item.is_a?(Hash)
                                        item = type.model.make_new(type.name, item)
                                    end
                                    if !item.is_a?(type)
                                        raise ArgumentError, "Arguments to #{fd.name} in #{@name} must be #{type.name}s"
                                    end
                                    instance_var << item
                                end
                            end
                            instance_variable_set("@" + fd.name, instance_var)
                        else
                            if val.nil?
                                instance_variable_set("@" + fd.name + "_ISNULL", true)
                            elsif val.is_a?(Hash)
                                val = type.model.make_new(type.name, val)
                            end
                            if !val.is_a?(type)
                                raise ArgumentError, "Arguments to #{fd.name} in #{@name} must be #{type.name}s"
                            end
                            instance_variable_set("@" + fd.name, val)
                        end
                    end
                end

            end
        end
        @module = klass
    end
    return @module
end

#to_sObject

Returns a human readable string


464
465
466
# File 'lib/intermine/model.rb', line 464

def to_s
    return "#{@model.name}.#{@name}"
end

#where(*args) ⇒ Object

call-seq:

where(*constraints) => PathQuery::Query

Returns a new query on this table in the originating service will all attribute columns selected for output and the given constraints applied.

zen = model.table('Gene').where(:symbol => 'zen').one
puts "Zen is short for #{zen.name}, and has a length of #{zen.length}"

419
420
421
422
423
424
# File 'lib/intermine/model.rb', line 419

def where(*args)
    q = new_query
    q.select("*")
    q.where(*args)
    return q
end