Merge branch '3618-column-ordering' closes #3618
[arvados.git] / apps / workbench / app / models / arvados_resource_list.rb
1 class ArvadosResourceList
2   include ArvadosApiClientHelper
3   include Enumerable
4
5   def initialize resource_class=nil
6     @resource_class = resource_class
7   end
8
9   def eager(bool=true)
10     @eager = bool
11     self
12   end
13
14   def limit(max_results)
15     @limit = max_results
16     self
17   end
18
19   def offset(skip)
20     @offset = skip
21     self
22   end
23
24   def order(orderby_spec)
25     @orderby_spec = orderby_spec
26     self
27   end
28
29   def select(columns=nil)
30     # If no column arguments were given, invoke Enumerable#select.
31     if columns.nil?
32       super()
33     else
34       @select ||= []
35       @select += columns
36       self
37     end
38   end
39
40   def filter _filters
41     @filters ||= []
42     @filters += _filters
43     self
44   end
45
46   def where(cond)
47     cond = cond.dup
48     cond.keys.each do |uuid_key|
49       if cond[uuid_key] and (cond[uuid_key].is_a? Array or
50                              cond[uuid_key].is_a? ArvadosBase)
51         # Coerce cond[uuid_key] to an array of uuid strings.  This
52         # allows caller the convenience of passing an array of real
53         # objects and uuids in cond[uuid_key].
54         if !cond[uuid_key].is_a? Array
55           cond[uuid_key] = [cond[uuid_key]]
56         end
57         cond[uuid_key] = cond[uuid_key].collect do |item|
58           if item.is_a? ArvadosBase
59             item.uuid
60           else
61             item
62           end
63         end
64       end
65     end
66     cond.keys.select { |x| x.match /_kind$/ }.each do |kind_key|
67       if cond[kind_key].is_a? Class
68         cond = cond.merge({ kind_key => 'arvados#' + arvados_api_client.class_kind(cond[kind_key]) })
69       end
70     end
71     api_params = {
72       _method: 'GET',
73       where: cond
74     }
75     api_params[:eager] = '1' if @eager
76     api_params[:limit] = @limit if @limit
77     api_params[:offset] = @offset if @offset
78     api_params[:select] = @select if @select
79     api_params[:order] = @orderby_spec if @orderby_spec
80     api_params[:filters] = @filters if @filters
81     res = arvados_api_client.api @resource_class, '', api_params
82     @results = arvados_api_client.unpack_api_response res
83     self
84   end
85
86   def results
87     self.where({}) if !@results
88     @results
89   end
90
91   def results=(r)
92     @results = r
93   end
94
95   def all
96     where({})
97   end
98
99   def each(&block)
100     results.each do |m|
101       block.call m
102     end
103     self
104   end
105
106   def collect
107     results.collect do |m|
108       yield m
109     end
110   end
111
112   def first
113     results.first
114   end
115
116   def last
117     results.last
118   end
119
120   def [](*x)
121     results.send('[]', *x)
122   end
123
124   def |(x)
125     if x.is_a? Hash
126       self.to_hash | x
127     else
128       results | x.to_ary
129     end
130   end
131
132   def to_ary
133     results
134   end
135
136   def to_hash
137     Hash[results.collect { |x| [x.uuid, x] }]
138   end
139
140   def empty?
141     results.empty?
142   end
143
144   def items_available
145     results.items_available if results.respond_to? :items_available
146   end
147
148   def result_limit
149     results.limit if results.respond_to? :limit
150   end
151
152   def result_offset
153     results.offset if results.respond_to? :offset
154   end
155
156   def result_links
157     results.links if results.respond_to? :links
158   end
159
160   # Return links provided with API response that point to the
161   # specified object, and have the specified link_class. If link_class
162   # is false or omitted, return all links pointing to the specified
163   # object.
164   def links_for item_or_uuid, link_class=false
165     return [] if !result_links
166     unless @links_for_uuid
167       @links_for_uuid = {}
168       result_links.each do |link|
169         if link.respond_to? :head_uuid
170           @links_for_uuid[link.head_uuid] ||= []
171           @links_for_uuid[link.head_uuid] << link
172         end
173       end
174     end
175     if item_or_uuid.respond_to? :uuid
176       uuid = item_or_uuid.uuid
177     else
178       uuid = item_or_uuid
179     end
180     (@links_for_uuid[uuid] || []).select do |link|
181       link_class == false or link.link_class == link_class
182     end
183   end
184
185   # Note: this arbitrarily chooses one of (possibly) multiple names.
186   def name_for item_or_uuid
187     links_for(item_or_uuid, 'name').first.andand.name
188   end
189
190 end