intermine 0.98.03 → 0.98.04

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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