intermine 0.98.03 → 0.98.04

Sign up to get free protection for your applications and to get access to all the features.
@@ -357,7 +357,12 @@ module InterMine::PathQuery
357
357
  end
358
358
 
359
359
  # Get your own result reader for handling the results at a low level.
360
+ # If no columns have been selected for output before requesting results,
361
+ # all attribute columns will be selected.
360
362
  def results_reader(start=0, size=nil)
363
+ if @views.empty?
364
+ select("*")
365
+ end
361
366
  return Results::ResultsReader.new(@url, self, start, size)
362
367
  end
363
368
 
@@ -3,10 +3,52 @@ require "json"
3
3
  require 'stringio'
4
4
  require 'net/http'
5
5
 
6
+ # == Code for handling results
7
+ #
8
+ # The classes in this module are used to process
9
+ # the results of queries. They include mechanisms
10
+ # for reading from the connection in a record
11
+ # orientated format, and for interpreting the values
12
+ # received.
13
+ #
6
14
  module InterMine::Results
7
15
 
16
+ # == Synopsis
17
+ #
18
+ # query.each_row do |row|
19
+ # puts row[0], row[1], row[2]
20
+ # puts row["symbol"], row["proteins.name"]
21
+ # end
22
+ #
23
+ # == Description
24
+ #
25
+ # A dual-faced object, these representations of rows
26
+ # of results are intended to provide a single object that
27
+ # allows Array-like and Hash-like access to an individual
28
+ # row of data. The primary means of access for this is
29
+ # the use of item access with ResultsRow#[], which accepts
30
+ # Array like index arguments, as well as Hash like key arguments.
31
+ #
32
+ # As well as value retrieval via indices and keys, iteration is also supported
33
+ # with ResultsRow#each and the aliases each_pair and each_value. If
34
+ # one parameter is requested the iteration will be by value, if
35
+ # two are requested it will be by pair.
36
+ #
37
+ # There is no attempt to fully emulate the complete Hash and Array
38
+ # interfaces - that would make little sense as it does not make any
39
+ # sense to insert values into a row, or to re-sort it in place. If you
40
+ # want to do such things, call ResultsRow#to_a or ResultsRow#to_h to
41
+ # get real Hash or Array values you are free to manipulate.
42
+ #
43
+ #:include:contact_header.rdoc
44
+ #
8
45
  class ResultsRow
9
46
 
47
+ # Construct a new result row.
48
+ #
49
+ # You will not need to do this - ResultsRow objects are created for
50
+ # you when the results are parsed.
51
+ #
10
52
  def initialize(results, columns)
11
53
  @results = results.is_a?(Array) ? results : JSON.parse(results)
12
54
  unless @results.is_a?(Array)
@@ -19,27 +61,104 @@ module InterMine::Results
19
61
  @columns = columns
20
62
  end
21
63
 
22
- def [](key)
23
- if key.is_a?(Integer)
24
- idx = key
25
- else
26
- idx = index_for(key)
64
+ # == Retrieve values from the row
65
+ #
66
+ # row[0] # first element
67
+ # row[-1] # last element
68
+ # row[2, 3] # Slice of three cells, starting at cell no. 3 (index 2)
69
+ # row[1..3] # Slice of three cells, starting at cell no. 2 (index 1)
70
+ # row["length"] # Get a cell's value by column name
71
+ # row["Gene.length"] # Use of the root class is optional
72
+ # row[:length] # For bare attributes, a symbol may be used.
73
+ #
74
+ # This method emulated both the array and hash style of access, based
75
+ # on argument type. Passing in integer indexes or ranges retrieves
76
+ # values in a manner that treats the row as a list of values. Passing in
77
+ # string or symbols retrieves values in a manner that treates the
78
+ # row as a Hash. It is possible to access the values in the row by using
79
+ # either the short or long forms of the column name.
80
+ #
81
+ # Unlike the corresponding method in either Hash or Array, this method
82
+ # throws errors when trying to access single elements (not when requesting
83
+ # array slices) and the result cannot be found.
84
+ #
85
+ def [](arg, length=nil)
86
+ unless length.nil?
87
+ raise ArgumentError, "when providing a length, the first argument must be an Integer" unless arg.is_a? Integer
88
+ return @results[arg, length].map {|x| x["value"]}
27
89
  end
28
- if idx.nil?
29
- raise IndexError, "Bad key: #{key}"
90
+
91
+ case arg
92
+ when Range
93
+ return @results[arg].map {|x| x["value"]}
94
+ when Integer
95
+ idx = arg
96
+ else
97
+ idx = index_for(arg)
30
98
  end
31
- begin
32
- result = @results[idx]["value"]
33
- rescue NoMethodError
34
- raise IndexError, "Bad key: #{key}"
99
+
100
+ raise ArgumentError, "Bad argument: #{arg}" if idx.nil?
101
+
102
+ cell = @results[idx]
103
+
104
+ raise IndexError, "Argument out of range" if cell.nil?
105
+
106
+ return cell["value"]
107
+ end
108
+
109
+ # Returns the first value in this row
110
+ def first
111
+ return @results[0]["value"]
112
+ end
113
+
114
+ # Returns the last value in this row
115
+ def last
116
+ return @results.last["value"]
117
+ end
118
+
119
+ # Iterate over the values in this row in the
120
+ # order specified by the query's view.
121
+ #
122
+ # row.each do |value|
123
+ # puts value
124
+ # end
125
+ #
126
+ # row.each do |column, value|
127
+ # puts "#{column} is #{value}"
128
+ # end
129
+ #
130
+ # If one parameter is specified, then the iteration
131
+ # simply includes values, if more than one is specified,
132
+ # then it will be by column/value pairs.
133
+ #
134
+ def each(&block)
135
+ if block
136
+ if block.arity == 1
137
+ @results.each {|x| block.call(x["value"])}
138
+ else block.arity == 2
139
+ (0 ... @results.size).to_a.each {|i| block.call(@columns[i], @results[i]["value"])}
140
+ end
35
141
  end
36
- return result
142
+ return self
37
143
  end
38
144
 
145
+ alias each_value each
146
+ alias each_pair each
147
+
148
+ # Return the number of cells in this row.
149
+ def size
150
+ return @results.size
151
+ end
152
+
153
+ alias length size
154
+
155
+ # Return an Array version of the row.
39
156
  def to_a
40
157
  return @results.map {|x| x["value"]}
41
158
  end
42
159
 
160
+ # Return a Hash version of the row. All keys in this
161
+ # hash will be full length column headers.
43
162
  def to_h
44
163
  hash = {}
45
164
  @results.each_index do |x|
@@ -49,6 +168,7 @@ module InterMine::Results
49
168
  return hash
50
169
  end
51
170
 
171
+ # Return a readable representation of the information in this row
52
172
  def to_s
53
173
  bufs = []
54
174
  @results.each_index do |idx|
@@ -61,12 +181,16 @@ module InterMine::Results
61
181
  return @columns.first.rootClass.name + ": " + bufs.join(",\t")
62
182
  end
63
183
 
64
- def to_csv
65
- return @results.map {|x| x["value"].to_s}.join("\t")
184
+ # Return the information in this row as a line suitable for prining in a
185
+ # CSV or TSV file. The optional separator argument will be used to delimit
186
+ # columns
187
+ def to_csv(sep="\t")
188
+ return @results.map {|x| x["value"].inspect}.join(sep)
66
189
  end
67
190
 
68
191
  private
69
192
 
193
+ # Return the index for a string or symbol key.
70
194
  def index_for(key)
71
195
  if @indexes.nil?
72
196
  @indexes = {}
@@ -81,13 +205,30 @@ module InterMine::Results
81
205
  end
82
206
  end
83
207
  end
84
- return @indexes[key]
208
+ return @indexes[key.to_s]
85
209
  end
86
210
  end
87
211
 
88
212
 
213
+ # The class responsible for retrieving results and processing them
214
+ #
215
+ # query.each_row do |row|
216
+ # puts row[2..3]
217
+ # end
218
+ #
219
+ # Queries delegate their handling of results to these objects, which
220
+ # are responsible for creating the ResultsRow objects or model objects
221
+ # that the results represent.
222
+ #
223
+ #:include:contact_header.rdoc
224
+ #
89
225
  class ResultsReader
90
226
 
227
+ # Construct a new ResultsReader.
228
+ #
229
+ # You will not need to do this yourself. It is handled by
230
+ # queries themselves.
231
+ #
91
232
  def initialize(uri, query, start, size)
92
233
  @uri = URI(uri)
93
234
  @query = query
@@ -95,12 +236,7 @@ module InterMine::Results
95
236
  @size = size
96
237
  end
97
238
 
98
- def params(format)
99
- p = @query.params.merge("start" => @start, "format" => format)
100
- p["size"] = @size unless @size.nil?
101
- return p
102
- end
103
-
239
+ # Run a request to get the size of the result set.
104
240
  def get_size
105
241
  query = params("jsoncount")
106
242
  res = Net::HTTP.post_form(@uri, query)
@@ -113,31 +249,10 @@ module InterMine::Results
113
249
  end
114
250
  end
115
251
 
116
- def each_line(data)
117
- req = Net::HTTP::Post.new(@uri.path)
118
- req.set_form_data(data)
119
- Net::HTTP.new(@uri.host, @uri.port).start {|http|
120
- http.request(req) {|resp|
121
- holdover = ""
122
- resp.read_body {|chunk|
123
- sock = StringIO.new(holdover + chunk)
124
- sock.each_line {|line|
125
- if sock.eof?
126
- holdover = line
127
- else
128
- yield line
129
- end
130
- }
131
- sock.close
132
- }
133
- yield holdover
134
- }
135
- }
136
- end
137
-
252
+ # Iterate over the result set one ResultsRow at a time
138
253
  def each_row
139
254
  container = ''
140
- self.each_line(params("jsonrows")) do |line|
255
+ each_line(params("jsonrows")) do |line|
141
256
  if line.start_with?("[")
142
257
  begin
143
258
  row = ResultsRow.new(line.chomp("," + $/), @query.views)
@@ -152,10 +267,25 @@ module InterMine::Results
152
267
  check_result_set(container)
153
268
  end
154
269
 
270
+ # Iterate over the resultset, one object at a time, where the
271
+ # object is the instantiation of the type of object specified as the
272
+ # query's root type. The actual type returned depends on the query
273
+ # itself, and any subclass information returned by the webservice.
274
+ #
275
+ # query = service.query("Gene").select("*")
276
+ # query.each_result do |gene|
277
+ # puts gene.symbol, gene.length
278
+ # end
279
+ #
280
+ # query = service.query("Organism").select("*")
281
+ # query.each_result do |organism|
282
+ # puts organism.shortName, organism.taxonId
283
+ # end
284
+ #
155
285
  def each_result
156
286
  model = @query.model
157
287
  container = ''
158
- self.each_line(params("jsonobjects")) do |line|
288
+ each_line(params("jsonobjects")) do |line|
159
289
  line.chomp!("," + $/)
160
290
  if line.start_with?("{") and line.end_with?("}")
161
291
  begin
@@ -176,6 +306,41 @@ module InterMine::Results
176
306
 
177
307
  private
178
308
 
309
+ # Retrieve the results from the webservice, one line at a time.
310
+ # This method has responsibility for ensuring that the lines are
311
+ # complete, and not split over two or more chunks.
312
+ def each_line(data)
313
+ req = Net::HTTP::Post.new(@uri.path)
314
+ req.set_form_data(data)
315
+ Net::HTTP.new(@uri.host, @uri.port).start {|http|
316
+ http.request(req) {|resp|
317
+ holdover = ""
318
+ resp.read_body {|chunk|
319
+ sock = StringIO.new(holdover + chunk)
320
+ sock.each_line {|line|
321
+ if sock.eof?
322
+ holdover = line
323
+ else
324
+ yield line
325
+ end
326
+ }
327
+ sock.close
328
+ }
329
+ yield holdover
330
+ }
331
+ }
332
+ end
333
+
334
+
335
+ # Get the parameters for this request, given the specified format.
336
+ def params(format)
337
+ p = @query.params.merge("start" => @start, "format" => format)
338
+ p["size"] = @size unless @size.nil?
339
+ return p
340
+ end
341
+
342
+ # Check that the request was successful according the metadata
343
+ # passed with the result.
179
344
  def check_result_set(container)
180
345
  begin
181
346
  result_set = JSON.parse(container)
@@ -189,6 +354,7 @@ module InterMine::Results
189
354
  end
190
355
  end
191
356
 
357
+ # Errors if there are problems retrieving results from the webservice.
192
358
  class ServiceError < RuntimeError
193
359
  end
194
360
 
@@ -84,6 +84,9 @@ module InterMine
84
84
  # is fully introspectible and queriable. This allows for sophisticated meta-programming
85
85
  # and dynamic query generation.
86
86
  #
87
+ #
88
+ #:include:contact_header.rdoc
89
+ #
87
90
  class Service
88
91
 
89
92
  extend Forwardable
@@ -1,3 +1,3 @@
1
1
  module Intermine
2
- VERSION = "0.98.03"
2
+ VERSION = "0.98.04"
3
3
  end
@@ -1 +1,5 @@
1
- [{"id":320000014,"value":"DepartmentA1","class":"Department","url":"/report.do?id=320000014"},{"id":320000015,"value":"EmployeeA1","class":"Manager","url":"/report.do?id=320000015"}]
1
+ [
2
+ {"id":320000014,"value":"DepartmentA1","class":"Department","url":"/report.do?id=320000014"},
3
+ {"id":320000015,"value":"EmployeeA1","class":"Manager","url":"/report.do?id=320000015"},
4
+ {"id":320000015,"value":33,"class":"Manager","url":"/report.do?id=320000015"}
5
+ ]
@@ -14,8 +14,8 @@ class TestResults < Test::Unit::TestCase
14
14
  File.dirname(__FILE__) + "/data/resultrow.json", "r")
15
15
  @data = file.read
16
16
  @json = JSON.parse(@data)
17
- @view = ["Company.departments.name","Company.departments.manager.name"]
18
- @bad_view = ["Company.departments.name","Company.departments.manager.name", "Comany.name"]
17
+ @view = ["Company.departments.name","Company.departments.manager.name", "Company.departments.manager.age"]
18
+ @bad_view = ["Company.departments.name","Company.departments.manager.name"]
19
19
  end
20
20
 
21
21
  def test_initialize
@@ -54,7 +54,11 @@ class TestResults < Test::Unit::TestCase
54
54
  assert_equal(rr[0], "DepartmentA1")
55
55
  assert_equal(rr[1], "EmployeeA1")
56
56
 
57
- rr = ResultsRow.new(@json, @view)
57
+ assert_equal(rr[1..2], ["EmployeeA1", 33])
58
+ assert_equal(rr[0, 2], ["DepartmentA1", "EmployeeA1"])
59
+
60
+ assert_equal(rr.first, "DepartmentA1")
61
+ assert_equal(rr.last, 33)
58
62
 
59
63
  assert_raise IndexError do
60
64
  rr[3]
@@ -62,6 +66,25 @@ class TestResults < Test::Unit::TestCase
62
66
 
63
67
  end
64
68
 
69
+ def test_each
70
+
71
+ rr = ResultsRow.new(@data, @view)
72
+ expected = %w{DepartmentA1 EmployeeA1} << 33
73
+
74
+ c = 0
75
+ rr.each do |val|
76
+ assert_equal(expected[c], val)
77
+ c += 1
78
+ end
79
+
80
+ c = 0
81
+ rr.each do |key, val|
82
+ assert_equal(@view[c], key)
83
+ assert_equal(expected[c], val)
84
+ c += 1
85
+ end
86
+ end
87
+
65
88
  def test_hash_access
66
89
 
67
90
  rr = ResultsRow.new(@data, @view)
@@ -76,7 +99,7 @@ class TestResults < Test::Unit::TestCase
76
99
 
77
100
  rr = ResultsRow.new(@json, @view)
78
101
 
79
- assert_raise IndexError do
102
+ assert_raise ArgumentError do
80
103
  rr["foo"]
81
104
  end
82
105
 
@@ -84,7 +107,7 @@ class TestResults < Test::Unit::TestCase
84
107
 
85
108
  def test_to_a
86
109
 
87
- expected = %w{DepartmentA1 EmployeeA1}
110
+ expected = %w{DepartmentA1 EmployeeA1} << 33
88
111
 
89
112
  rr = ResultsRow.new(@data, @view)
90
113
 
@@ -101,7 +124,8 @@ class TestResults < Test::Unit::TestCase
101
124
 
102
125
  expected = {
103
126
  "Company.departments.name" => "DepartmentA1",
104
- "Company.departments.manager.name" => "EmployeeA1"
127
+ "Company.departments.manager.name" => "EmployeeA1",
128
+ "Company.departments.manager.age" => 33
105
129
  }
106
130
  assert_equal(expected, rr.to_h)
107
131
 
@@ -4,3 +4,4 @@ require 'test/unit'
4
4
  require 'test_model'
5
5
  require 'test_query'
6
6
  require 'test_sugar'
7
+ require 'test_result_row'
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: intermine
3
3
  version: !ruby/object:Gem::Version
4
- hash: 401
4
+ hash: 415
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 98
9
- - 3
10
- version: 0.98.03
9
+ - 4
10
+ version: 0.98.04
11
11
  platform: ruby
12
12
  authors:
13
13
  - Alex Kalderimis
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-08-01 00:00:00 +01:00
18
+ date: 2011-08-02 00:00:00 +01:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency