rubyMorphbank 0.2.0
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.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/README.rdoc +17 -0
- data/Rakefile +54 -0
- data/VERSION +1 -0
- data/init.rb +1 -0
- data/lib/request.rb +53 -0
- data/lib/response.rb +74 -0
- data/lib/rubyMorphbank.rb +71 -0
- data/test/helper.rb +10 -0
- data/test/test_rubyMorphbank.rb +93 -0
- metadata +84 -0
data/.document
ADDED
data/.gitignore
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
= rubyMorphbank
|
2
|
+
|
3
|
+
A request/response library for the Morphbank (http://morphbank.net) API.
|
4
|
+
|
5
|
+
== Note on Patches/Pull Requests
|
6
|
+
|
7
|
+
* Fork the project.
|
8
|
+
* Make your feature addition or bug fix.
|
9
|
+
* Add tests for it. This is important so I don't break it in a
|
10
|
+
future version unintentionally.
|
11
|
+
* Commit, do not mess with rakefile, version, or history.
|
12
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
13
|
+
* Send me a pull request. Bonus points for topic branches.
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2010 mjy. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "rubyMorphbank"
|
8
|
+
gem.summary = %Q{Simple Morphbank (http://morphbank.net) services accessor.}
|
9
|
+
gem.description = %Q{Uses the Morphbank API to query and return results from the Morphbank database.}
|
10
|
+
gem.email = "diapriid@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/mjy/rubyMorphbank"
|
12
|
+
gem.authors = ["mjy"]
|
13
|
+
gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
|
14
|
+
gem.files += FileList['init.rb', 'lib/**/*.rb'].to_a
|
15
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
16
|
+
end
|
17
|
+
Jeweler::GemcutterTasks.new
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'rake/testtask'
|
23
|
+
Rake::TestTask.new(:test) do |test|
|
24
|
+
test.libs << 'lib' << 'test'
|
25
|
+
test.pattern = 'test/**/test_*.rb'
|
26
|
+
test.verbose = true
|
27
|
+
end
|
28
|
+
|
29
|
+
begin
|
30
|
+
require 'rcov/rcovtask'
|
31
|
+
Rcov::RcovTask.new do |test|
|
32
|
+
test.libs << 'test'
|
33
|
+
test.pattern = 'test/**/test_*.rb'
|
34
|
+
test.verbose = true
|
35
|
+
end
|
36
|
+
rescue LoadError
|
37
|
+
task :rcov do
|
38
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
task :test => :check_dependencies
|
43
|
+
|
44
|
+
task :default => :test
|
45
|
+
|
46
|
+
require 'rake/rdoctask'
|
47
|
+
Rake::RDocTask.new do |rdoc|
|
48
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
49
|
+
|
50
|
+
rdoc.rdoc_dir = 'rdoc'
|
51
|
+
rdoc.title = "rubyMorphbank #{version}"
|
52
|
+
rdoc.rdoc_files.include('README*')
|
53
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
54
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.0
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'rubyMorphbank'
|
data/lib/request.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
class Request
|
2
|
+
|
3
|
+
# defaults
|
4
|
+
METHOD = 'search'
|
5
|
+
OBJECTTYPE = 'Image'
|
6
|
+
LIMIT = 10
|
7
|
+
DEPTH = 1
|
8
|
+
FORMAT = 'id'
|
9
|
+
|
10
|
+
# a subset of VALID_OPTIONS, these are concatenated in the request
|
11
|
+
URI_PARAMS = [:limit, :format, :depth, :objecttype, :first_result, :keywords, :id]
|
12
|
+
|
13
|
+
# possible options for a Request instance
|
14
|
+
VALID_OPTIONS = {'v3' => URI_PARAMS + [:version, :method] }
|
15
|
+
|
16
|
+
attr_reader(:request_url, :request_options)
|
17
|
+
|
18
|
+
def initialize(options = {})
|
19
|
+
opt = {
|
20
|
+
:version => 'v3',
|
21
|
+
:method => METHOD,
|
22
|
+
:limit => LIMIT,
|
23
|
+
:format => FORMAT, # 'id' (brief results) or 'svc' (schema based results)
|
24
|
+
:depth => DEPTH,
|
25
|
+
:objecttype => OBJECTTYPE, # [nil, Taxon, Image, Character, Specimen, View, Matrix, Locality, Collection, OTU ] (nil = all)
|
26
|
+
:first_result => 0,
|
27
|
+
:keywords => ''
|
28
|
+
}.merge!(options)
|
29
|
+
|
30
|
+
# check for legal parameters
|
31
|
+
opt.keys.each do |p|
|
32
|
+
raise RubyMorphbankError, "#{p} is not a valid parameter" if !VALID_OPTIONS[opt[:version]].include?(p)
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
# create the request
|
37
|
+
@request_url = SERVICES_URI + "method=#{opt[:method]}" +
|
38
|
+
opt.keys.sort{|a,b| a.to_s <=> b.to_s}.collect{
|
39
|
+
|k| ( (URI_PARAMS.include?(k) && (!opt[k].nil? || opt[k].empty?)) ? "&#{k.to_s}=#{opt[k]}" : '')
|
40
|
+
}.join
|
41
|
+
|
42
|
+
# and some housekeepers
|
43
|
+
@request_options = opt
|
44
|
+
end
|
45
|
+
|
46
|
+
def get_response
|
47
|
+
Response.new(self)
|
48
|
+
end
|
49
|
+
|
50
|
+
# .root.elements.each(xpath){}.map do |row|
|
51
|
+
|
52
|
+
## REXML::XPath.match(@xml, '//object/width').collect{|e| e.text.to_s}
|
53
|
+
end
|
data/lib/response.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
class Response
|
2
|
+
|
3
|
+
attr_reader(
|
4
|
+
:response, # the Net:HTTP response
|
5
|
+
:doc, # the REXML:Document for :response
|
6
|
+
:request # cache the request object for reference in the response
|
7
|
+
)
|
8
|
+
|
9
|
+
# all the objects returned
|
10
|
+
attr :objects, true
|
11
|
+
attr :ids, true
|
12
|
+
|
13
|
+
attr :annotations
|
14
|
+
attr :images
|
15
|
+
|
16
|
+
def initialize(request = Request.new)
|
17
|
+
|
18
|
+
|
19
|
+
begin
|
20
|
+
@response = Net::HTTP.get_response(URI.parse(request.request_url)).body
|
21
|
+
rescue SocketError
|
22
|
+
raise "can not connect to socket, check SERVICES_URI"
|
23
|
+
end
|
24
|
+
|
25
|
+
# TODO: check that we're getting a legit XML document back before trying to parse it
|
26
|
+
@doc = REXML::Document.new(@response)
|
27
|
+
|
28
|
+
@request = request # TODO: can this be aliased some how?
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
# return an Array of ids representing morphbankIds to images
|
33
|
+
def mb_image_ids
|
34
|
+
if request.request_options[:format] == 'id'
|
35
|
+
REXML::XPath.match(@doc, "//id").collect{|e| e.text.to_s}
|
36
|
+
else
|
37
|
+
raise RubyMorphbankError, "you must use method='image' in requests to use the mb_image_ids method"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# get the Integer of the first match
|
42
|
+
def get_int(element_name)
|
43
|
+
REXML::XPath.first(@doc, "//#{element_name}").nil? ? 0 : REXML::XPath.first(@doc, "//#{element_name}").text.to_i
|
44
|
+
end
|
45
|
+
|
46
|
+
# get the String of the first match
|
47
|
+
def get_text(element_name)
|
48
|
+
REXML::XPath.first(@doc, "//#{element_name}").nil? ? 0 : REXML::XPath.first(@doc, "//#{element_name}").text
|
49
|
+
end
|
50
|
+
|
51
|
+
# return an Array of XML elements whose root is an object element
|
52
|
+
def objects
|
53
|
+
REXML::XPath.match(@doc, "//object")
|
54
|
+
end
|
55
|
+
|
56
|
+
# TODO: the has conversion is NOT optimal (no attributes, children of root only), see extension in rubyMorpbank
|
57
|
+
def hashified_objects
|
58
|
+
self.doc.records("//object")
|
59
|
+
end
|
60
|
+
|
61
|
+
# pagination
|
62
|
+
def link_forward?
|
63
|
+
((get_int('numResultsReturned') + get_int('firstResult')) < get_int('numResults')) and return true
|
64
|
+
false
|
65
|
+
end
|
66
|
+
|
67
|
+
# pagination
|
68
|
+
def link_back?
|
69
|
+
(get_int('firstResult') > @request.request_options[:limit]) and return true
|
70
|
+
false
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# Written by Matt Yoder, 2010
|
2
|
+
# see source on github
|
3
|
+
|
4
|
+
require 'ruby-debug'
|
5
|
+
require 'rexml/document'
|
6
|
+
require 'net/http'
|
7
|
+
|
8
|
+
module RubyMorphbank
|
9
|
+
|
10
|
+
# schema is at http://morphbank.net/schema/mbsvc3.xsd
|
11
|
+
# web interface to API is at http://services.morphbank.net/mb3
|
12
|
+
|
13
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '../lib/Request'))
|
14
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '../lib/Response'))
|
15
|
+
|
16
|
+
class RubyMorphbankError < StandardError
|
17
|
+
end
|
18
|
+
|
19
|
+
SERVICES_URI = 'http://services.morphbank.net/mb3/request?'
|
20
|
+
|
21
|
+
class Rmb
|
22
|
+
attr_accessor :opt
|
23
|
+
|
24
|
+
def initialize(options = {})
|
25
|
+
opt = {
|
26
|
+
}.merge!(options)
|
27
|
+
end
|
28
|
+
|
29
|
+
def request(options = {})
|
30
|
+
opt = {
|
31
|
+
}.merge!(options)
|
32
|
+
Request.new(opt)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# returns a Hash with properties set to values !! no attributes, nothing other than child elements of root (see extensions below) !!
|
37
|
+
def metadata_hash_for_one_image(image_id)
|
38
|
+
Rmb.new.request(:id => image_id, :format => 'svc', :objecttype => 'Image', :method => 'id', :limit => 1).get_response.doc.record('//object')
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
## Base/REXML extension ##
|
45
|
+
|
46
|
+
# modified from http://snippets.dzone.com/posts/show/6181
|
47
|
+
|
48
|
+
# convert an array into a hash
|
49
|
+
class Array
|
50
|
+
def to_h
|
51
|
+
Hash[*self]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# neither of these extensions is particularly good, they don't recurse etc.
|
56
|
+
class REXML::Document
|
57
|
+
def record(xpath)
|
58
|
+
self.root.elements.each(xpath + '/*'){}.inject([]) do |r,node|
|
59
|
+
r << node.name.to_s << node.text.to_s.strip
|
60
|
+
end.to_h
|
61
|
+
end
|
62
|
+
|
63
|
+
def records(xpath)
|
64
|
+
self.root.elements.each(xpath){}.map do |row|
|
65
|
+
row.elements.each{}.inject([]) do |r,node|
|
66
|
+
r << node.name.to_s << node.text.to_s.strip
|
67
|
+
end.to_h
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
data/test/helper.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
|
2
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '../lib/rubyMorphbank'))
|
3
|
+
|
4
|
+
include RubyMorphbank
|
5
|
+
|
6
|
+
|
7
|
+
class TestRubyMorphbank < Test::Unit::TestCase
|
8
|
+
|
9
|
+
should "initialize a new instance without parameters" do
|
10
|
+
assert @bhl = Rmb.new()
|
11
|
+
end
|
12
|
+
|
13
|
+
should "return an hash object using get_metadata_for_one_image" do
|
14
|
+
# WARNING: this XML to hash is not a terribly useful conversion, the process drops all children and attributes
|
15
|
+
hash = metadata_hash_for_one_image(195815) # It's fixed data, not the best test, but if it's gone something is seriously wrong anyways
|
16
|
+
assert_equal '1360', hash['width']
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
class TestRequest < Test::Unit::TestCase
|
23
|
+
|
24
|
+
should "return a properly formatted request url with no options" do
|
25
|
+
@rmb = Rmb.new
|
26
|
+
assert @request = @rmb.request
|
27
|
+
assert_equal "http://services.morphbank.net/mb3/request?method=search&depth=1&first_result=0&format=id&keywords=&limit=10&objecttype=Image", @request.request_url
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
class TestResponse < Test::Unit::TestCase
|
35
|
+
|
36
|
+
should "return initialize with a unparameterized request" do
|
37
|
+
@request = Rmb.new.request
|
38
|
+
@response = @request.get_response
|
39
|
+
assert_equal 'mbresponse', @response.doc.elements.first.name
|
40
|
+
end
|
41
|
+
|
42
|
+
should "return 10 results when unparameterized" do
|
43
|
+
@request = Rmb.new.request
|
44
|
+
assert_equal 10, @request.get_response.get_int('numResultsReturned')
|
45
|
+
end
|
46
|
+
|
47
|
+
should "return many more than 10 results in MB when unparameterized" do
|
48
|
+
@request = Rmb.new.request
|
49
|
+
assert (10 < @request.get_response.get_int('numResults'))
|
50
|
+
end
|
51
|
+
|
52
|
+
should "should link_forward when unparameterized" do
|
53
|
+
@request = Rmb.new.request
|
54
|
+
assert @request.get_response.link_forward?
|
55
|
+
end
|
56
|
+
|
57
|
+
should "should not link_back when unparameterized" do
|
58
|
+
@request = Rmb.new.request
|
59
|
+
assert !@request.get_response.link_back?
|
60
|
+
end
|
61
|
+
|
62
|
+
should "return an array of morphbank image ids when using :format => id and mb_image_ids" do
|
63
|
+
@request = Rmb.new.request(:format => 'id')
|
64
|
+
@response = @request.get_response
|
65
|
+
assert @response.mb_image_ids.size == 10 # not a good test, but variable data return on a vanilla request
|
66
|
+
# this is an array of ids like ["32029", ... "32038"]
|
67
|
+
end
|
68
|
+
|
69
|
+
# test get_int
|
70
|
+
should "return a fixnum using get_int for element numResultsReturned" do
|
71
|
+
@request = Rmb.new.request()
|
72
|
+
assert_equal 10, @request.get_response.get_int('numResultsReturned')
|
73
|
+
end
|
74
|
+
|
75
|
+
# test get_text
|
76
|
+
should "return a mbresponse of objecttypes == Image by default" do
|
77
|
+
@request = Rmb.new.request()
|
78
|
+
assert_equal 'Image', @request.get_response.get_text('objecttypes')
|
79
|
+
end
|
80
|
+
|
81
|
+
# return a link to a numbnail in a call for a single image
|
82
|
+
should "return a URI to a thumb for a single object query" do
|
83
|
+
foo = Rmb.new.request(:format => 'svc', :id => 195815, :method => 'id')
|
84
|
+
assert_equal 'http://images.morphbank.net/?id=195815&imgType=thumb', foo.get_response.get_text('thumbUrl')
|
85
|
+
end
|
86
|
+
|
87
|
+
should "return an array of hashified array of Morphbank objects" do
|
88
|
+
foo = Rmb.new.request(:format => 'svc').get_response.hashified_objects
|
89
|
+
assert_equal 10, foo.size
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
metadata
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rubyMorphbank
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 2
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- mjy
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-05-12 00:00:00 -04:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: thoughtbot-shoulda
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
version: "0"
|
30
|
+
type: :development
|
31
|
+
version_requirements: *id001
|
32
|
+
description: Uses the Morphbank API to query and return results from the Morphbank database.
|
33
|
+
email: diapriid@gmail.com
|
34
|
+
executables: []
|
35
|
+
|
36
|
+
extensions: []
|
37
|
+
|
38
|
+
extra_rdoc_files:
|
39
|
+
- README.rdoc
|
40
|
+
files:
|
41
|
+
- .document
|
42
|
+
- .gitignore
|
43
|
+
- README.rdoc
|
44
|
+
- Rakefile
|
45
|
+
- VERSION
|
46
|
+
- init.rb
|
47
|
+
- lib/request.rb
|
48
|
+
- lib/response.rb
|
49
|
+
- lib/rubyMorphbank.rb
|
50
|
+
- test/helper.rb
|
51
|
+
- test/test_rubyMorphbank.rb
|
52
|
+
has_rdoc: true
|
53
|
+
homepage: http://github.com/mjy/rubyMorphbank
|
54
|
+
licenses: []
|
55
|
+
|
56
|
+
post_install_message:
|
57
|
+
rdoc_options:
|
58
|
+
- --charset=UTF-8
|
59
|
+
require_paths:
|
60
|
+
- lib
|
61
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
segments:
|
66
|
+
- 0
|
67
|
+
version: "0"
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
segments:
|
73
|
+
- 0
|
74
|
+
version: "0"
|
75
|
+
requirements: []
|
76
|
+
|
77
|
+
rubyforge_project:
|
78
|
+
rubygems_version: 1.3.6
|
79
|
+
signing_key:
|
80
|
+
specification_version: 3
|
81
|
+
summary: Simple Morphbank (http://morphbank.net) services accessor.
|
82
|
+
test_files:
|
83
|
+
- test/helper.rb
|
84
|
+
- test/test_rubyMorphbank.rb
|