intermine 0.98.03 → 0.98.04
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/intermine/query.rb +5 -0
- data/lib/intermine/results.rb +211 -45
- data/lib/intermine/service.rb +3 -0
- data/lib/intermine/version.rb +1 -1
- data/test/data/resultrow.json +5 -1
- data/test/test_result_row.rb +30 -6
- data/test/unit_tests.rb +1 -0
- metadata +4 -4
data/lib/intermine/query.rb
CHANGED
@@ -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
|
|
data/lib/intermine/results.rb
CHANGED
@@ -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
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
29
|
-
|
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
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
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
|
-
|
65
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
|
data/lib/intermine/service.rb
CHANGED
data/lib/intermine/version.rb
CHANGED
data/test/data/resultrow.json
CHANGED
@@ -1 +1,5 @@
|
|
1
|
-
[
|
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
|
+
]
|
data/test/test_result_row.rb
CHANGED
@@ -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"
|
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
|
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
|
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
|
|
data/test/unit_tests.rb
CHANGED
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:
|
4
|
+
hash: 415
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 98
|
9
|
-
-
|
10
|
-
version: 0.98.
|
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-
|
18
|
+
date: 2011-08-02 00:00:00 +01:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|