class InterMine::Lists::ListManager

Synopsis

An internal class for managing lists throughout the lifetime of a program. The main Service object delegates list functionality to this class.

# Creation
list = service.create_list("path/to/some/file.txt", "Gene", "my-favourite-genes")
# Retrieval
other_list = service.list("my-previously-saved-list")
# Combination
intersection = service.intersection_of([list, other_list])
# Deletion
service.delete_lists(list, other_list)

Description

This class contains logic for reading and updating the lists available to a given user at a webservice. This class in particular is responsible for parsing list responses, and performing the operations that combine lists into new result sets (intersection, union, symmetric difference, subtraction).

author

Alex Kalderimis dev@intermine.org

homepage

www.intermine.org

Licence

Copyright (C) 2002-2011 FlyMine

This code may be freely distributed and modified under the terms of the GNU Lesser General Public Licence. This should be distributed with the code. See the LICENSE file for more information or www.gnu.org/copyleft/lesser.html.

Constants

DEFAULT_DESCRIPTION

The description given by default to all new lists for which you do not provide a description explicitly. The purpose of this is to help you identify automatically created lists in you profile.

DEFAULT_LIST_NAME

The name given by default to all lists you do not explicitly name

l = service.create_list("genes.txt", "Gene")
puts l.name
=> "my_list_1"

Attributes

service[R]

The service this manager belongs to

temporary_lists[R]

The temporary lists created in this session. These will be deleted at program exit.

Public Class Methods

new(service) click to toggle source

Construct a new ListManager.

You will never need to call this constructor yourself.

    # File lib/intermine/lists.rb
492 def initialize(service)
493     @service = service
494     @lists = {}
495     @temporary_lists = []
496     @uri = URI.parse(@service.root)
497     @http = Net::HTTP.new(@uri.host, @uri.port)
498     if @uri.scheme == 'https'
499         @http.use_ssl = true
500     end
501     do_at_exit(self)
502 end

Public Instance Methods

add_tags(list, *tags) click to toggle source

Add tags to a list.

Returns the current tags

    # File lib/intermine/lists.rb
672 def add_tags(list, *tags)
673     uri = URI.parse(@service.root + Service::LIST_TAG_PATH)
674     params = @service.params.merge("name" => list.name, "tags" => tags.join(";"))
675     res = @http.start() do |http|
676         Net::HTTP.post_form(uri, params)
677     end
678     check_response_for_error(res)
679     return JSON.parse(res.body)["tags"]
680 end
create_list(content, type=nil, tags=[], name=nil, description=nil) click to toggle source

Create a new List with the given content.

Creates a new List and stores it on the appropriate webservice:

Arguments:

content

Can be a string with delimited identifiers, an Array of identifiers, a File object containing identifiers, or a name of an unopened readable file containing identifiers. It can also be another List (in which case the list is cloned) or a query that describes a result set.

type

Required when identifiers are being given (but not when the content is a PathQuery::Query or a List. This should be the kind of object to look for (such as “Gene”).

tags

An Array of tags to apply to the new list. If a list is supplied as the content, these tags will be added to the existing tags.

name

The name of the new list. One will be generated if none is provided. Lists created with generated names are considered temporary and will be deleted upon program exit.

description

An informative description of the list

# With Array of Ids
list = service.create_list(%{eve bib zen}) 

# From a file
list = service.create_list("path/to/some/file.txt", "Gene", [], "my-stored-genes")

# With a query
list = service.create_list(service.query("Gene").select(:id).where(:length => {"<" => 500}))
    # File lib/intermine/lists.rb
577 def create_list(content, type=nil, tags=[], name=nil, description=nil)
578     name ||= get_unused_list_name
579     description ||= DEFAULT_DESCRIPTION
580 
581     if content.is_a?(List) 
582         tags += content.tags
583         response = create_list_from_query(content.list_query, tags, name, description)
584     elsif content.respond_to?(:list_upload_uri)
585         response = create_list_from_query(content, tags, name, description)
586     else 
587         response = create_list_from_ids(content, type, tags, name, description)
588     end
589 
590     return process_list_creation_response(response)
591 end
delete_lists(*lists) click to toggle source

Deletes the given lists from the webservice. The lists can be supplied as List objects, or as their names as Strings.

Raises errors if problems occur with the deletion of these lists, including if the lists do not exist.

    # File lib/intermine/lists.rb
599 def delete_lists(*lists)
600     lists.map {|x| x.is_a?(List) ? x.name : x.to_s}.uniq.each do |name|
601         uri = URI.parse(@service.root + Service::LISTS_PATH)
602         params = {"name" => name}
603         req = Net::HTTP::Delete.new(uri.path + "?" + params_to_query_string(params))
604         res = @http.start do |http|
605             http.request(req)
606         end
607         check_response_for_error(res)
608     end
609     refresh_lists
610 end
get_lists_with_tags(*tags) click to toggle source

Gets all lists with the given tags. If more than one tag is supplied, then a list must have all given tags to be returned.

tagged = service.get_lists_with_tags("tagA", "tagB")
    # File lib/intermine/lists.rb
528 def get_lists_with_tags(*tags)
529     return lists.select do |l|
530         union = l.tags | tags
531         union.size == l.tags.size
532     end
533 end
intersection_of(lists=[], tags=[], name=nil, description=nil) click to toggle source

Create a new list in the webservice from the intersection of two or more lists, and return a List object that represents it.

See ListManager#create_list for an explanation of tags, name and description

    # File lib/intermine/lists.rb
632 def intersection_of(lists=[], tags=[], name=nil, description=nil)
633     do_commutative_list_operation(Service::LIST_INTERSECTION_PATH, "Intersection", lists, tags, name, description)
634 end
list(name) click to toggle source

Get a list by name. Returns nil if the list does not exist.

    # File lib/intermine/lists.rb
518 def list(name)
519     refresh_lists
520     return @lists[name]
521 end
list_names() click to toggle source

Get the names of the lists currently available in the webservice

    # File lib/intermine/lists.rb
512 def list_names
513     refresh_lists
514     return @lists.keys.sort
515 end
lists() click to toggle source

Get the lists currently available in the webservice.

    # File lib/intermine/lists.rb
506 def lists
507     refresh_lists
508     return @lists.values
509 end
params_to_query_string(p) click to toggle source

only handles single value keys!

    # File lib/intermine/lists.rb
716 def params_to_query_string(p)
717     return @service.params.merge(p).map { |k,v| "#{k}=#{CGI::escape(v.to_s)}" }.join('&')
718 end
process_list_creation_response(response) click to toggle source

Common code to all list requests for interpreting the response from the webservice.

    # File lib/intermine/lists.rb
657 def process_list_creation_response(response)
658     check_response_for_error(response)
659     new_list = JSON.parse(response.body)
660     new_name = new_list["listName"]
661     failed_matches = new_list["unmatchedIdentifiers"] || []
662     refresh_lists
663     ret = list(new_name)
664     ret.unmatched_identifiers.replace(failed_matches)
665     return ret
666 end
refresh_lists() click to toggle source

Update the stored record of lists. This method is called before all list retrieval methods.

    # File lib/intermine/lists.rb
537 def refresh_lists
538     lists = JSON.parse(@service.get_list_data)
539     @lists = {}
540     lists["lists"].each {|hash| 
541         l = List.new(hash, self)
542         @lists[l.name] = l
543     }
544 end
remove_tags(list, *tags) click to toggle source

Remove tags from a list

Returns the current tags

    # File lib/intermine/lists.rb
686 def remove_tags(list, *tags)
687     uri = URI.parse(@service.root + Service::LIST_TAG_PATH)
688     params = @service.params.merge(
689         "name" => list.name, 
690         "tags" => tags.join(";")
691     )
692     req_path = uri.path + "?" + params_to_query_string(params)
693     req = Net::HTTP::Delete.new(req_path)
694     res = @http.start() do |http|
695         http.request(req)
696     end
697     check_response_for_error(res)
698     return JSON.parse(res.body)["tags"]
699 end
subtract(references=[], delenda=[], tags=[], name=nil, description=nil) click to toggle source

Create a new list in the webservice by subtracting all the elements in the 'delenda' lists from all the elements in the 'reference' lists, and return a List object that represents it.

See ListManager#create_list for an explanation of tags, name and description

    # File lib/intermine/lists.rb
641 def subtract(references=[], delenda=[], tags=[], name=nil, description=nil)
642     ref_names = make_list_names(references)
643     del_names = make_list_names(delenda)
644     name ||= get_unused_list_name
645     description ||= "Subtraction of #{del_names[0 .. -2].join(", ")} and #{del_names.last} from #{ref_names[0 .. -2].join(", ")} and #{ref_names.last}"
646     uri = URI.parse(@service.root + Service::LIST_SUBTRACTION_PATH)
647     params = @service.params.merge("name" => name, "description" => description, "references" => ref_names.join(';'),
648                                    "subtract" => del_names.join(';'), "tags" => tags.join(';'))
649     res = @http.start() do |http|
650         Net::HTTP.post_form(uri, params)
651     end
652     return process_list_creation_response(res)
653 end
symmetric_difference_of(lists=[], tags=[], name=nil, description=nil) click to toggle source

Create a new list in the webservice from the symmetric difference of two or more lists, and return a List object that represents it.

See ListManager#create_list for an explanation of tags, name and description

    # File lib/intermine/lists.rb
616 def symmetric_difference_of(lists=[], tags=[], name=nil, description=nil)
617     do_commutative_list_operation(Service::LIST_DIFFERENCE_PATH, "Symmetric difference", lists, tags, name, description)
618 end
tags_for(list) click to toggle source

Get the current tags for a list

    # File lib/intermine/lists.rb
702 def tags_for(list)
703     uri = URI.parse(@service.root + Service::LIST_TAG_PATH)
704     params = @service.params.merge(
705         "name" => list.name
706     )
707     req_path = uri.path + "?" + params_to_query_string(params)
708     res = @http.start() do |http|
709         http.get(req_path)
710     end
711     check_response_for_error(res)
712     return JSON.parse(res.body)["tags"]
713 end
union_of(lists=[], tags=[], name=nil, description=nil) click to toggle source

Create a new list in the webservice from the union of two or more lists, and return a List object that represents it.

See ListManager#create_list for an explanation of tags, name and description

    # File lib/intermine/lists.rb
624 def union_of(lists=[], tags=[], name=nil, description=nil)
625     do_commutative_list_operation(Service::LIST_UNION_PATH, "Union", lists, tags, name, description)
626 end

Private Instance Methods

check_response_for_error(response) click to toggle source

Error checking routine.

    # File lib/intermine/lists.rb
777 def check_response_for_error(response)
778     case response
779     when Net::HTTPSuccess
780         # Ok
781     else
782         begin
783             container = JSON.parse(response.body)
784             raise ServiceError, container["error"]
785         rescue
786             response.error!
787         end
788     end
789 end
create_list_from_ids(ids, type, tags=[], name=nil, description=nil) click to toggle source

Routine for creating a List in a webservice from a list of Ids.

    # File lib/intermine/lists.rb
807 def create_list_from_ids(ids, type, tags=[], name=nil, description=nil)
808     if @service.model.get_cd(type).nil?
809         raise ArgumentError, "Invalid type (#{type.inspect})"
810     end
811     uri = URI.parse(@service.root + Service::LISTS_PATH)
812     list_params = {
813         "name" => name,
814         "description" => description,
815         "tags" => tags.join(";"),
816         "type" => type
817     }
818     if ids.is_a?(File)
819         f = ids
820     elsif ids.is_a?(Array)
821         f = StringIO.new(ids.map {|x| '"' + x.gsub(/"/, '\"') + '"'}.join(" "))
822     elsif File.readable?(ids.to_s)
823         f = File.open(ids, "r")
824     else
825         f = StringIO.new(ids.to_s)
826     end
827     req = Net::HTTP::Post.new(uri.path + "?" + params_to_query_string(list_params))
828     req.body_stream = f
829     req.content_type = "text/plain"
830     req.content_length = f.size
831 
832     res = @http.start() do |http|
833         http.request(req)
834     end
835     f.close
836     return res
837 end
create_list_from_query(query, tags=[], name=nil, description=nil) click to toggle source

Routine for creating a list from a PathQuery::Query

    # File lib/intermine/lists.rb
792 def create_list_from_query(query, tags=[], name=nil, description=nil)
793     uri = query.list_upload_uri
794     list_params = {
795         "listName" => name,
796         "description" => description,
797         "tags" => tags.join(";")
798     }
799     service_params = @service.params
800     params = query.params.merge(list_params).merge(service_params)
801     @http.start() do |http|
802         return Net::HTTP.post_form(URI.parse(uri), params)
803     end
804 end
do_at_exit(this) click to toggle source

Clean up after itself by deleting any temporary lists left lying about

    # File lib/intermine/lists.rb
724 def do_at_exit(this)
725     at_exit do 
726         unless this.temporary_lists.empty?
727             this.lists.each do |x|
728                 begin
729                     x.delete if this.temporary_lists.include?(x.name)
730                 rescue
731                     # Ignore errors here.
732                 end
733             end
734         end
735     end
736 end
do_commutative_list_operation(path, operation, lists, tags=[], name=nil, description=nil) click to toggle source

Common code behind the operation of union, intersection and symmetric difference operations.

    # File lib/intermine/lists.rb
760 def do_commutative_list_operation(path, operation, lists, tags=[], name=nil, description=nil)
761     list_names = make_list_names(lists)
762     name ||= get_unused_list_name
763     description ||= "#{operation} of #{list_names[0 .. -2].join(", ")} and #{list_names.last}"
764 
765     uri = URI.parse(@service.root + path)
766     params = @service.params.merge(
767         "name" => name, "lists" => list_names.join(";"), 
768         "description" => description, "tags" => tags.join(';')
769     )
770     res = @http.start() do |http|
771         Net::HTTP::post_form(uri, params)
772     end
773     return process_list_creation_response(res)
774 end
get_unused_list_name(no=1) click to toggle source

Helper to get an unused default name

    # File lib/intermine/lists.rb
840 def get_unused_list_name(no=1)
841     name = DEFAULT_LIST_NAME + "_" + no.to_s
842     names = list_names
843     while names.include?(name)
844         no += 1
845         name = DEFAULT_LIST_NAME + "_" + no.to_s
846     end
847     @temporary_lists.push(name)
848     return name
849 end
make_list_names(objs) click to toggle source

Transform a collection of objects containing Lists, Queries and Strings into a collection of Strings with list names.

Raises errors if an object cannot be resolved to an accessible list.

    # File lib/intermine/lists.rb
743 def make_list_names(objs)
744     current_names = list_names
745     return objs.map do |x|
746         case x
747         when List
748             x.name
749         when x.respond_to?(:list_upload_uri)
750             create_list(x).name
751         when current_names.include?(x.to_s)
752             x.to_s
753         else
754             raise ArgumentError, "#{x} is not a list you can access"
755         end
756     end
757 end