solvebio 1.6.1 → 1.7.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.
Files changed (119) hide show
  1. data/.bumpversion.cfg +6 -0
  2. data/.gitignore +5 -4
  3. data/.travis.yml +1 -1
  4. data/Gemfile +3 -0
  5. data/README.md +34 -34
  6. data/Rakefile +1 -18
  7. data/bin/solvebio.rb +14 -16
  8. data/installer +64 -0
  9. data/lib/solvebio.rb +50 -11
  10. data/lib/solvebio/acccount.rb +4 -0
  11. data/lib/solvebio/annotation.rb +11 -0
  12. data/lib/solvebio/api_operations.rb +147 -0
  13. data/lib/solvebio/api_resource.rb +32 -0
  14. data/lib/solvebio/cli.rb +75 -0
  15. data/lib/solvebio/cli/auth.rb +106 -0
  16. data/lib/solvebio/cli/credentials.rb +54 -0
  17. data/lib/{cli → solvebio/cli}/irb.rb +0 -23
  18. data/lib/solvebio/cli/irbrc.rb +48 -0
  19. data/lib/solvebio/cli/tutorial.rb +12 -0
  20. data/lib/solvebio/client.rb +149 -0
  21. data/lib/solvebio/dataset.rb +60 -0
  22. data/lib/solvebio/dataset_field.rb +12 -0
  23. data/lib/solvebio/depository.rb +38 -0
  24. data/lib/solvebio/depository_version.rb +40 -0
  25. data/lib/solvebio/errors.rb +64 -0
  26. data/lib/solvebio/filter.rb +315 -0
  27. data/lib/solvebio/list_object.rb +73 -0
  28. data/lib/solvebio/locale.rb +43 -0
  29. data/lib/solvebio/query.rb +341 -0
  30. data/lib/solvebio/sample.rb +54 -0
  31. data/lib/solvebio/singleton_api_resource.rb +25 -0
  32. data/lib/solvebio/solve_object.rb +164 -0
  33. data/lib/solvebio/tabulate.rb +589 -0
  34. data/lib/solvebio/user.rb +4 -0
  35. data/lib/solvebio/util.rb +59 -0
  36. data/lib/solvebio/version.rb +3 -0
  37. data/solvebio.gemspec +10 -18
  38. data/test/helper.rb +6 -2
  39. data/test/solvebio/data/.gitignore +1 -0
  40. data/test/solvebio/data/.netrc +6 -0
  41. data/test/{data → solvebio/data}/netrc-save +0 -0
  42. data/test/solvebio/data/sample.vcf.gz +0 -0
  43. data/test/solvebio/data/test_creds +3 -0
  44. data/test/solvebio/test_annotation.rb +45 -0
  45. data/test/solvebio/test_client.rb +29 -0
  46. data/test/solvebio/test_conversion.rb +14 -0
  47. data/test/solvebio/test_credentials.rb +67 -0
  48. data/test/solvebio/test_dataset.rb +52 -0
  49. data/test/solvebio/test_depository.rb +24 -0
  50. data/test/solvebio/test_depositoryversion.rb +22 -0
  51. data/test/solvebio/test_error.rb +31 -0
  52. data/test/solvebio/test_filter.rb +86 -0
  53. data/test/solvebio/test_query.rb +282 -0
  54. data/test/solvebio/test_query_batch.rb +38 -0
  55. data/test/solvebio/test_query_init.rb +30 -0
  56. data/test/solvebio/test_query_tabulate.rb +73 -0
  57. data/test/solvebio/test_ratelimit.rb +31 -0
  58. data/test/solvebio/test_resource.rb +29 -0
  59. data/test/solvebio/test_sample_access.rb +60 -0
  60. data/test/solvebio/test_sample_download.rb +20 -0
  61. data/test/solvebio/test_tabulate.rb +129 -0
  62. data/test/solvebio/test_util.rb +39 -0
  63. metadata +100 -85
  64. data/Makefile +0 -17
  65. data/demo/README.md +0 -14
  66. data/demo/cheatsheet.rb +0 -31
  67. data/demo/dataset/facets.rb +0 -13
  68. data/demo/dataset/field.rb +0 -13
  69. data/demo/depository/README.md +0 -24
  70. data/demo/depository/all.rb +0 -13
  71. data/demo/depository/retrieve.rb +0 -13
  72. data/demo/depository/versions-all.rb +0 -13
  73. data/demo/query/query-filter.rb +0 -30
  74. data/demo/query/query.rb +0 -13
  75. data/demo/query/range-filter.rb +0 -18
  76. data/demo/test-api.rb +0 -98
  77. data/lib/cli/auth.rb +0 -122
  78. data/lib/cli/help.rb +0 -13
  79. data/lib/cli/irbrc.rb +0 -54
  80. data/lib/cli/options.rb +0 -75
  81. data/lib/client.rb +0 -154
  82. data/lib/credentials.rb +0 -67
  83. data/lib/errors.rb +0 -81
  84. data/lib/filter.rb +0 -312
  85. data/lib/locale.rb +0 -47
  86. data/lib/main.rb +0 -46
  87. data/lib/query.rb +0 -414
  88. data/lib/resource/annotation.rb +0 -23
  89. data/lib/resource/apiresource.rb +0 -241
  90. data/lib/resource/dataset.rb +0 -91
  91. data/lib/resource/datasetfield.rb +0 -37
  92. data/lib/resource/depository.rb +0 -50
  93. data/lib/resource/depositoryversion.rb +0 -69
  94. data/lib/resource/main.rb +0 -123
  95. data/lib/resource/sample.rb +0 -75
  96. data/lib/resource/solveobject.rb +0 -122
  97. data/lib/resource/user.rb +0 -5
  98. data/lib/tabulate.rb +0 -706
  99. data/lib/util.rb +0 -29
  100. data/test/Makefile +0 -9
  101. data/test/data/sample.vcf.gz +0 -0
  102. data/test/test-annotation.rb +0 -46
  103. data/test/test-auth.rb +0 -58
  104. data/test/test-client.rb +0 -27
  105. data/test/test-conversion.rb +0 -13
  106. data/test/test-dataset.rb +0 -42
  107. data/test/test-depository.rb +0 -35
  108. data/test/test-error.rb +0 -36
  109. data/test/test-filter.rb +0 -70
  110. data/test/test-netrc.rb +0 -52
  111. data/test/test-query-batch.rb +0 -40
  112. data/test/test-query-init.rb +0 -29
  113. data/test/test-query-paging.rb +0 -102
  114. data/test/test-query.rb +0 -71
  115. data/test/test-resource.rb +0 -40
  116. data/test/test-sample-access.rb +0 -59
  117. data/test/test-sample-download.rb +0 -20
  118. data/test/test-tabulate.rb +0 -131
  119. data/test/test-util.rb +0 -42
@@ -0,0 +1,32 @@
1
+ module SolveBio
2
+ class APIResource < SolveObject
3
+ def self.retrieve(id)
4
+ instance = self.new(id)
5
+ instance.refresh()
6
+ instance
7
+ end
8
+
9
+ def self.class_name
10
+ self.name.split('::')[-1]
11
+ end
12
+
13
+ def self.url
14
+ if self == APIResource
15
+ raise NotImplementedError.new('APIResource is an abstract class and has no url.')
16
+ end
17
+ "/v1/#{Util.pluralize(Util.camelcase_to_underscore(class_name))}"
18
+ end
19
+
20
+ def url
21
+ unless id = self.id
22
+ raise InvalidRequestError.new("Could not determine which URL to request: #{self.class} instance has invalid ID: #{id.inspect}", 'id')
23
+ end
24
+ "#{self.class.url}/#{id}"
25
+ end
26
+
27
+ def refresh
28
+ response = Client.get(url)
29
+ refresh_from(response)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+
4
+ require 'irb'
5
+ require 'netrc'
6
+ require 'fileutils'
7
+ require 'optparse'
8
+ require 'readline'
9
+ require 'io/console'
10
+
11
+ require 'solvebio/cli/credentials'
12
+ require 'solvebio/cli/auth'
13
+ require 'solvebio/cli/irb'
14
+ require 'solvebio/cli/tutorial'
15
+
16
+ module SolveBio
17
+ module CLI
18
+ def process_options(argv)
19
+ options = {}
20
+ opts = setup_options(options)
21
+
22
+ begin
23
+ opts.parse!(argv)
24
+ rescue OptionParser::ParseError => error
25
+ $stderr.puts error
26
+ $stderr.puts "(-h or --help will show valid options)"
27
+ exit 1
28
+ end
29
+
30
+ SolveBio.api_host = options[:api_host] if options[:api_host]
31
+ SolveBio.api_key = options[:api_key] if options[:api_key]
32
+
33
+ return options, argv
34
+ end
35
+
36
+ # Main parser for the SolveBio command line client
37
+ def setup_options(options, stdout=$stdout, stderr=$stderr)
38
+ OptionParser.new do |opts|
39
+ opts.banner = "Usage: solvebio.rb [options] <command> [<args>]"
40
+ opts.on_tail('-v', '--version',
41
+ 'print the version') do
42
+ options[:version] = true
43
+ stdout.puts "solvebio-ruby #{SolveBio::VERSION}"
44
+ exit 0
45
+ end
46
+
47
+ opts.on('--api-host STRING', String,
48
+ 'Override the default SolveBio API host') do
49
+ |api_host|
50
+ options[:api_host] = api_host
51
+ end
52
+
53
+ opts.on('--api-key STRING', String,
54
+ 'Manually provide a SolveBio API key') do
55
+ |api_key|
56
+ options[:api_key] = api_key
57
+ end
58
+
59
+ opts.on('-h', '--help', 'Display this screen') do
60
+ puts opts
61
+ puts <<-EOH
62
+
63
+ SolveBio Commands:
64
+ login Login and save credentials.
65
+ logout Logout and delete saved credentials.
66
+ whoami Show your SolveBio email address.
67
+ shell Open a SolveBio IRB shell.
68
+ tutorial Start the SolveBio Ruby tutorial.
69
+ EOH
70
+ exit
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,106 @@
1
+ module SolveBio
2
+ module CLI
3
+ module Auth
4
+ include SolveBio::CLI::Credentials
5
+
6
+ module_function
7
+ def print_message(msg)
8
+ if SolveBio.api_host != 'https://api.solvebio.com'
9
+ msg += " (#{SolveBio.api_host})"
10
+ end
11
+ puts msg + '.'
12
+ end
13
+
14
+ def ask_for_credentials()
15
+ print_message('Enter your SolveBio credentials')
16
+ email = Readline.readline('Email: ', true)
17
+ puts 'Password (typing will be hidden): '
18
+ password = STDIN.noecho(&:gets).chomp
19
+ puts
20
+ return email, password
21
+ end
22
+
23
+ def send_install_report
24
+ require 'rbconfig';
25
+ data = {
26
+ :client => 'ruby',
27
+ :client_version => SolveBio::VERSION,
28
+ :ruby_version => RbConfig::CONFIG['RUBY_PROGRAM_VERSION'],
29
+ :ruby_implementation => RbConfig::CONFIG['RUBY_SO_NAME'],
30
+ :architecture => RbConfig::CONFIG['arch'],
31
+ }
32
+ Client.request('post', '/v1/reports/install', {:payload => data}) rescue nil
33
+ end
34
+
35
+ def login
36
+ email, password = ask_for_credentials
37
+
38
+ if not email or not password
39
+ puts "Email and password are both required."
40
+ return false
41
+ end
42
+
43
+ data = {
44
+ :email => email,
45
+ :password => password
46
+ }
47
+
48
+ begin
49
+ response = Client.post('/v1/auth/token', {:payload => data})
50
+ rescue SolveBio::SolveError => e
51
+ puts "Login failed: #{e.to_s}"
52
+ return false
53
+ end
54
+
55
+ delete_credentials
56
+ save_credentials(email.downcase, response[:token])
57
+ SolveBio.api_key = response[:token]
58
+ send_install_report
59
+ print_message("You are now logged-in as #{email}")
60
+ return true
61
+ end
62
+
63
+ def logout
64
+ if get_credentials
65
+ delete_credentials
66
+ print_message('You have been logged out')
67
+ return true
68
+ end
69
+
70
+ print_message('You are not logged-in')
71
+ return false
72
+ end
73
+
74
+ def whoami
75
+ email = nil
76
+ api_key = SolveBio.api_key
77
+
78
+ # Override local credentials with existing key
79
+ if SolveBio.api_key
80
+ begin
81
+ user = Client.get('/v1/user')
82
+ email = user[:email]
83
+ rescue SolveBio::SolveError => e
84
+ SolveBio.api_key = nil
85
+ api_key = nil
86
+ print_message("Error: #{e.to_s}")
87
+ end
88
+ else
89
+ begin
90
+ email, api_key = get_credentials
91
+ rescue
92
+ nil
93
+ end
94
+ end
95
+
96
+ if not email.nil?
97
+ print_message("You are logged-in as #{email}")
98
+ else
99
+ print_message("You are not logged-in")
100
+ end
101
+
102
+ return email, api_key
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+
4
+ class CredentialsError < RuntimeError
5
+ end
6
+
7
+ module SolveBio
8
+ module CLI
9
+ module Credentials
10
+ module_function
11
+ def api_host
12
+ Addressable::URI.parse(SolveBio.api_host).host
13
+ end
14
+
15
+ def netrc_path
16
+ raise IOError, "$HOME is not set and is needed for SolveBio credentials" unless
17
+ ENV['HOME']
18
+
19
+ path = File.join(ENV['HOME'], '.solvebio', 'credentials')
20
+
21
+ dirname = File.dirname(path)
22
+ FileUtils.mkdir_p(dirname) unless File.directory?(dirname)
23
+
24
+ # create an empty credentials file if it doesn't exist
25
+ FileUtils.touch path unless File.exist? path
26
+ FileUtils.chmod 0600, path
27
+ path
28
+ end
29
+
30
+ def get_credentials
31
+ begin
32
+ n = Netrc.read(netrc_path)
33
+ return n[api_host]
34
+ rescue Netrc::Error => e
35
+ raise CredentialsError, "Could not read credentials file: #{e}"
36
+ end
37
+ end
38
+ module_function :get_credentials
39
+
40
+ def delete_credentials
41
+ n = Netrc.read(netrc_path)
42
+ n.delete(api_host)
43
+ n.save
44
+ end
45
+
46
+ def save_credentials(email, api_key)
47
+ n = Netrc.read(netrc_path)
48
+ # Overwrites any existing credentials
49
+ n[api_host] = email, api_key
50
+ n.save
51
+ end
52
+ end
53
+ end
54
+ end
@@ -1,27 +1,9 @@
1
1
  #!/usr/bin/env ruby
2
2
  # -*- coding: utf-8 -*-
3
- # Open the SolveBio shell, an IRB wrapper
4
- require 'irb'
5
- require_relative 'help'
6
3
 
7
4
  module IRB
8
-
9
- # Stuff to add IRB commands goes here
10
- # module ExtendCommand
11
- # class SolveHelp
12
- # def self.execute(conf, *opts)
13
- # SolveBio::help
14
- # end
15
- # end
16
-
17
- # end
18
-
19
5
  module_function
20
-
21
- # ExtendCommandBundle.def_extend_command 'solvehelp', :SolveHelp
22
-
23
6
  def shell
24
-
25
7
  # Set to run the standard trepan IRB profile
26
8
  irbrc = File.
27
9
  expand_path(File.join(File.dirname(__FILE__), 'irbrc.rb'))
@@ -51,8 +33,3 @@ module IRB
51
33
  end
52
34
  end
53
35
  end
54
-
55
- # Demo it.
56
- if __FILE__ == $0
57
- IRB::shell
58
- end
@@ -0,0 +1,48 @@
1
+ # -*- ruby -*-
2
+ # irbrc profile for SolveBio
3
+ IRB.conf[:PROMPT_MODE] = :SIMPLE
4
+ IRB.conf[:PROMPT][:SIMPLE] = {
5
+ :PROMPT_C => '[SolveBio] In ?: ', # Prompt when continuing a statement
6
+ :PROMPT_I => '[SolveBio] In : ', # Normal prompt
7
+ :PROMPT_N => '[SolveBio] In +: ', # Prompt when indenting code
8
+ :PROMPT_S => '[SolveBio] In %l: ', # Prompt when continuing a string
9
+ :RETURN => "[SolveBio] Out : %s\n"
10
+ }
11
+
12
+ require 'solvebio'
13
+ require 'solvebio/cli'
14
+
15
+ have_completion = nil
16
+ begin
17
+ require 'bond' and require 'bond/completion'
18
+ have_completion = 'bond'
19
+ rescue LoadError
20
+ begin
21
+ have_completion = require 'irb/completion'
22
+ rescue LoadError
23
+ have_completion = false
24
+ end
25
+ end
26
+
27
+ unless have_completion
28
+ if have_completion != 'bond'
29
+ puts "Please install the 'bond' getm for better autocompletion"
30
+ end
31
+ end
32
+
33
+ # If an API key is set in SolveBio.api_key, use that.
34
+ # Otherwise, look for credentials in the local file,
35
+ # Otherwise, ask the user to log in.
36
+
37
+ if SolveBio.api_key or SolveBio::CLI::Credentials.get_credentials
38
+ email, SolveBio.api_key = SolveBio::CLI::Auth::whoami
39
+ else
40
+ SolveBio::CLI::Auth.login
41
+ end
42
+
43
+ if not SolveBio.api_key
44
+ puts("SolveBio requires a valid account. To sign up, visit: https://www.solvebio.com/signup")
45
+ exit 1
46
+ end
47
+
48
+ include SolveBio
@@ -0,0 +1,12 @@
1
+ module SolveBio
2
+ module CLI
3
+ module Tutorial
4
+ def tutorial
5
+ puts <<-TUTORIAL
6
+ # Tutorial coming soon!
7
+ TUTORIAL
8
+ end
9
+ module_function :tutorial
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,149 @@
1
+ module SolveBio
2
+ class Client
3
+ attr_reader :headers, :api_host
4
+ attr_accessor :api_key
5
+
6
+ # Add our own kind of Authorization tokens. This has to be
7
+ # done this way, late, because the rest-client gem looks for
8
+ # .netrc and will set basic authentication if it finds a match.
9
+ RestClient.add_before_execution_proc do | req, args |
10
+ if args[:authorization]
11
+ req.instance_variable_get('@header')['authorization'] = [args[:authorization]]
12
+ end
13
+ end
14
+
15
+ def initialize(api_key=nil, api_host=nil)
16
+ @api_key = api_key || SolveBio.api_key
17
+ @api_host = api_host || SolveBio.api_host
18
+
19
+ # Mirroring comments from:
20
+ # http://ruby-doc.org/stdlib-2.1.2/libdoc/net/http/rdoc/Net/HTTP.html
21
+ # gzip compression is used in preference to deflate
22
+ # compression, which is used in preference to no compression.
23
+ @headers = {
24
+ :content_type => :json,
25
+ :accept => :json,
26
+ 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
27
+ 'User-Agent' => 'SolveBio Ruby Client %s' % [
28
+ SolveBio::VERSION,
29
+ ]
30
+ }
31
+ end
32
+
33
+ DEFAULT_REQUEST_OPTS = {
34
+ :raw => false,
35
+ :default_headers => true
36
+ }
37
+
38
+ # Issues an HTTP GET across the wire via the Ruby 'rest-client'
39
+ # library. See *request()* for information on opts.
40
+ def get(url, opts={})
41
+ request('get', url, opts)
42
+ end
43
+
44
+ # Issues an HTTP POST across the wire via the Ruby 'rest-client'
45
+ # library. See *request* for information on opts.
46
+ def post(url, data, opts={})
47
+ opts[:payload] =
48
+ if opts.member?(:no_json)
49
+ data
50
+ else
51
+ data.to_json
52
+ end
53
+ request('post', url, opts)
54
+ end
55
+
56
+ # Issues an HTTP Request across the wire via the Ruby 'rest-client'
57
+ # library.
58
+ def request(method, url, opts={})
59
+ opts = DEFAULT_REQUEST_OPTS.merge(opts)
60
+
61
+ # Expand URL with API host if none was given
62
+ api_host = @api_host or SolveBio.api_host
63
+
64
+ if not api_host
65
+ raise SolveError.new('No SolveBio API host is set')
66
+ elsif not url.start_with?(api_host)
67
+ url = Addressable::URI.join(api_host, url).to_s
68
+ end
69
+
70
+ # Handle some default options and add authorization header
71
+ if opts[:default_headers] and @api_key
72
+ headers = @headers.merge(opts[:headers]||{})
73
+ authorization = "Token #{@api_key}"
74
+ else
75
+ headers = nil
76
+ authorization = nil
77
+ end
78
+
79
+ SolveBio::logger.debug('API %s Request: %s' % [method.upcase, url])
80
+
81
+ response = nil
82
+ begin
83
+ RestClient::Request.
84
+ execute(:method => method,
85
+ :url => url,
86
+ :headers => headers,
87
+ :authorization => authorization,
88
+ :timeout => opts[:timeout] || 80,
89
+ :payload => opts[:payload]) do
90
+ |resp, request, result, &block|
91
+ response = resp
92
+ if 429 == response.code
93
+ delay = Integer(response.headers[:retry_after])
94
+ SolveBio::logger.warn("Too many requests, sleeping for #{delay}s.")
95
+ sleep(delay)
96
+ return request(method, url, opts)
97
+ elsif response.code < 200 or response.code >= 400
98
+ handle_api_error(response)
99
+ end
100
+ end
101
+ rescue RestClient::Exception => e
102
+ handle_request_error(e)
103
+ end
104
+
105
+ if opts[:raw]
106
+ return response
107
+ end
108
+
109
+ begin
110
+ response = JSON.parse(response)
111
+ rescue JSON::ParserError => e
112
+ handle_request_error(e)
113
+ end
114
+
115
+ Util.symbolize_names(response)
116
+ end
117
+
118
+ def handle_request_error(e)
119
+ err = e.inspect
120
+ SolveBio::logger.error("API Error: #{err}")
121
+ msg = "Unexpected error communicating with SolveBio.\n" +
122
+ "If this problem persists, let us " +
123
+ "know at contact@solvebio.com."
124
+ raise SolveError.new(nil, msg)
125
+ end
126
+
127
+ def handle_api_error(response)
128
+ SolveBio::logger.error("API Error: #{response}") unless
129
+ [400, 401, 403, 404].member?(response.code.to_i)
130
+ raise SolveError.new(response)
131
+ end
132
+
133
+ def self.client
134
+ @@client ||= SolveBio::Client.new()
135
+ end
136
+
137
+ def self.get(url, opts={})
138
+ client.get(url, opts)
139
+ end
140
+
141
+ def self.post(url, data, opts={})
142
+ client.post(url, data, opts)
143
+ end
144
+
145
+ def self.request(method, url, opts={})
146
+ client.request(method, url, opts)
147
+ end
148
+ end
149
+ end