bazil_client 1.0.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/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ Gemfile.lock
6
+ /coverage/
7
+ /vendor/bundle/
8
+ /pkg/
9
+ /rdoc/
data/ChangeLog.md ADDED
@@ -0,0 +1,3 @@
1
+ ## Version 1.0.0 (2013-02-14) ##
2
+
3
+ Released!
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+
5
+ gem "simplecov", :require => false
6
+ gem 'rspec', :require => false
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 Preferred Infrastructure,Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
File without changes
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rubygems'
5
+ require 'rake'
6
+
7
+ require 'rspec/core'
8
+ require 'rspec/core/rake_task'
9
+
10
+ RSpec::Core::RakeTask.new(:spec) do |spec|
11
+ spec.pattern = 'spec/**/*_spec.rb'
12
+ spec.rspec_opts = ['--color --backtrace']
13
+ end
14
+
15
+ task :default => [:spec]
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.name = "bazil_client"
6
+ gem.description = "Ruby client of Bazil"
7
+ gem.homepage = "https://asp-bazil.preferred.jp/"
8
+ gem.summary = gem.description
9
+ gem.version = File.read("VERSION").strip
10
+ gem.authors = ["Nobuyuki Kubota"]
11
+ gem.email = "bazil-info@preferred.jp"
12
+ gem.has_rdoc = false
13
+ gem.platform = Gem::Platform::RUBY
14
+ gem.files = `git ls-files`.split("\n")
15
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ gem.require_paths = ['lib']
18
+ gem.license = "MIT"
19
+
20
+ gem.required_ruby_version = '>= 1.9.0'
21
+ gem.add_development_dependency "rake", ">= 0.9.2"
22
+ gem.add_development_dependency "simplecov", ">= 0.5.4"
23
+ gem.add_development_dependency "rspec", ">= 1.0.0"
24
+ end
@@ -0,0 +1,159 @@
1
+ require 'forwardable'
2
+
3
+ require 'rubygems'
4
+ require 'json'
5
+ require 'net/http'
6
+ require 'net/https'
7
+ require 'bazil/model'
8
+ require 'bazil/rest'
9
+ require 'bazil/error'
10
+
11
+ module Bazil
12
+ class Client
13
+ extend Forwardable
14
+
15
+ class Options
16
+ attr_reader :host, :port, :scheme, :ca_file, :ssl_version, :verify_mode
17
+
18
+ def initialize(options)
19
+ if options.kind_of? String
20
+ options = {CA_FILE_KEY => options}
21
+ end
22
+ options = symbolize_keys(options)
23
+
24
+ url = URI::parse(options[URL_KEY] || DEFAULT_URL)
25
+ @host = url.host or raise "Failed to obtain host name from given url: url = #{url.to_s}"
26
+ @port = url.port or raise "Failed to obtain port number from given url: url = #{url.to_s}"
27
+ @scheme = url.scheme or raise "Failed to obtain scheme from given url: url = #{url.to_s}"
28
+ raise "Unsupported scheme '#{@scheme}'" unless AVAILABLE_SCHEMA.include? @scheme
29
+
30
+ @ca_file = options[CA_FILE_KEY] || DEFAULT_CA_FILE
31
+ if @ca_file
32
+ raise "ca_file option must be string value" unless @ca_file.is_a? String
33
+ raise "ca_file option must be absolute path" unless @ca_file[0] == '/'
34
+ raise "ca_file '#{@ca_file}' doesn't exist" unless File::exists? @ca_file
35
+ end
36
+
37
+ ssl_version = options[SSL_VERSION_KEY] || DEFAULT_SSL_VERSION
38
+ raise "Unsupported SSL version '#{ssl_version}'" unless AVAILABLE_SSL_VERSIONS.has_key? ssl_version
39
+ @ssl_version = AVAILABLE_SSL_VERSIONS[ssl_version]
40
+
41
+ skip_verify = options[SKIP_VERIFY_KEY] || DEFAULT_SKIP_VERIFY
42
+ raise "skip_verify option must be boolean value" unless skip_verify.is_a?(TrueClass) || skip_verify.is_a?(FalseClass)
43
+ @verify_mode = skip_verify ? OpenSSL::SSL::VERIFY_NONE : OpenSSL::SSL::VERIFY_PEER
44
+ end
45
+
46
+ private
47
+
48
+ def symbolize_keys(hash)
49
+ {}.tap{|new_hash|
50
+ hash.each{|k,v|
51
+ new_hash[k.to_s.to_sym] = v
52
+ }
53
+ }
54
+ end
55
+
56
+ URL_KEY = :url
57
+ DEFAULT_URL = 'https://asp-bazil.preferred.jp/'
58
+ AVAILABLE_SCHEMA = ['http', 'https']
59
+
60
+ CA_FILE_KEY = :ca_file
61
+ DEFAULT_CA_FILE = nil
62
+
63
+ SSL_VERSION_KEY = :ssl_version
64
+ AVAILABLE_SSL_VERSIONS = {SSLv3: 'SSLv3', TLSv1: 'TLSv1'}
65
+ DEFAULT_SSL_VERSION = :TLSv1
66
+
67
+ SKIP_VERIFY_KEY = :skip_verify
68
+ DEFAULT_SKIP_VERIFY = false
69
+ end
70
+
71
+ def set_ssl_options(http, options)
72
+ http.use_ssl = options.scheme == 'https'
73
+ http.ca_file = options.ca_file
74
+ http.ssl_version = options.ssl_version
75
+ http.verify_mode = options.verify_mode
76
+ end
77
+
78
+ def initialize(options={})
79
+ opt = Options.new(options)
80
+ http = Net::HTTP.new(opt.host, opt.port)
81
+ set_ssl_options(http,opt)
82
+ @http_cli = REST.new(http)
83
+ end
84
+
85
+ def_delegators :@http_cli, :read_timeout, :read_timeout=, :set_api_keys
86
+
87
+ def models(options = {})
88
+ queries = {}
89
+ queries[:tag_id] = options[:tag_id].to_i if options.has_key? :tag_id
90
+ queries[:page] = options[:page].to_i if options.has_key? :page
91
+ queries[:per_page] = options[:per_page].to_i if options.has_key? :per_page
92
+
93
+ res, body = @http_cli.get(gen_uri("models",queries))
94
+ raise_error("Failed to get models", res) unless res.code =~ /2[0-9][0-9]/
95
+ JSON.parse(res.body)["models"].map{|model|
96
+ model["config_ids"].map{|config_id|
97
+ Model.new(self, model["id"].to_i, config_id.to_i)
98
+ }
99
+ }.flatten
100
+ end
101
+
102
+ def create_model(config)
103
+ data = config.to_json
104
+ res, body = @http_cli.post(gen_uri('models'), data, {'Content-Type' => 'application/json; charset=UTF-8', 'Content-Length' => data.length.to_s})
105
+ raise_error("Failed to create model", res) unless res.code =~ /2[0-9][0-9]/ # TODO: return detailed error information
106
+ js = JSON.parse(res.body)
107
+ Model.new(self, js['model_id'].to_i, js['config_id'].to_i)
108
+ end
109
+
110
+ def delete_model(model_id)
111
+ res, body = @http_cli.delete(gen_uri("models/#{model_id}"))
112
+ raise_error("Failed to delete model", res) unless res.code =~ /2[0-9][0-9]/ # TODO: return detailed error information
113
+ JSON.parse(res.body)
114
+ end
115
+
116
+ def create_config(model_id, config)
117
+ data = config.to_json
118
+ res, body = @http_cli.post(gen_uri("models/#{model_id}/configs"), data, {'Content-Type' => 'application/json; charset=UTF-8', 'Content-Length' => data.length.to_s})
119
+ raise_error("Failed to create new configuration", res) unless res.code =~ /2[0-9][0-9]/ # TODO: return detailed error information
120
+ js = JSON.parse(res.body)
121
+ Model.new(self, model_id, js['config_id'].to_i)
122
+ end
123
+
124
+ def delete_config(model_id, config_id)
125
+ res, body = @http_cli.delete(gen_uri("models/#{model_id}/configs/#{config_id}"))
126
+ raise_error("Failed to delete configuration", res) unless res.code =~ /2[0-9][0-9]/ # TODO: return detailed error information
127
+ JSON.parse(res.body)
128
+ end
129
+
130
+ def model(model_id, config_id)
131
+ model = Model.new(self, model_id, config_id)
132
+ model.status
133
+ model
134
+ end
135
+
136
+ def http_client
137
+ @http_cli
138
+ end
139
+
140
+ # TODO: make this changable
141
+ def api_version
142
+ 'v2'
143
+ end
144
+
145
+ private
146
+
147
+ def gen_uri(path, queries = {})
148
+ if queries.empty?
149
+ "/#{api_version}/#{path}"
150
+ else
151
+ "/#{api_version}/#{path}?#{queries.map{|k,v| "#{k}=#{v}"}.join('&')}"
152
+ end
153
+ end
154
+
155
+ def raise_error(message, res)
156
+ raise APIError.new(message, res.code, JSON.parse(res.body))
157
+ end
158
+ end # class Client
159
+ end # module Bazil
@@ -0,0 +1,37 @@
1
+ module Bazil
2
+ class BazilError < RuntimeError
3
+ def inspect
4
+ to_s
5
+ end
6
+ end
7
+
8
+ class ConnectionError < BazilError
9
+ attr_reader :method, :address, :port
10
+
11
+ def initialize(method, address, port)
12
+ @method = method
13
+ @address = address
14
+ @port = port
15
+ end
16
+
17
+ def to_s
18
+ "Failed to connect to the server at #{@method} method: server = #{@address}:#{@port}"
19
+ end
20
+ end
21
+
22
+ class APIError < BazilError
23
+ attr_reader :errors
24
+
25
+ def initialize(message, code, response)
26
+ @message = message
27
+ @code = code
28
+ @errors = response['errors']
29
+ end
30
+
31
+ def to_s
32
+ result = [@message]
33
+ result += @errors.map { |error| "\t#{error['file']}(#{error['line']}): #{error['ecode']}: #{error['message']}" }
34
+ result.join("\n")
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,180 @@
1
+ require 'rubygems'
2
+ require 'json'
3
+ require 'bazil/error'
4
+
5
+ module Bazil
6
+ class Model
7
+ attr_reader :model_id, :config_id
8
+
9
+ def initialize(client, model_id, config_id)
10
+ @client = client
11
+ @http_cli = client.http_client
12
+ @model_id = model_id
13
+ @config_id = config_id
14
+ end
15
+
16
+ def status
17
+ res = @http_cli.get(gen_uri(target_path(@config_id, "status")))
18
+ raise_error("Failed to get status of the model: #{error_suffix}", res) unless res.code =~ /2[0-9][0-9]/
19
+ JSON.parse(res.body)
20
+ end
21
+
22
+ def model_config
23
+ res = @http_cli.get(gen_uri())
24
+ raise_error("Failed to get model config: #{error_suffix}", res) unless res.code =~ /2[0-9][0-9]/
25
+ JSON.parse(res.body)
26
+ end
27
+
28
+ def update_model_config(conf)
29
+ res = @http_cli.put(gen_uri(), conf.to_json, {'Content-Type' => 'application/json; charset=UTF-8', 'Content-Length' => conf.to_json.length.to_s})
30
+ JSON.parse(res.body)
31
+ end
32
+
33
+ def config
34
+ res = @http_cli.get(gen_uri("configs/#{@config_id}"))
35
+ raise_error("Failed to get config of the model: #{error_suffix}", res) unless res.code =~ /2[0-9][0-9]/
36
+ JSON.parse(res.body)
37
+ end
38
+
39
+ def update_config(config)
40
+ res = send(:put, "configs/#{@config_id}", config.to_json, "Failed to updated config")
41
+ {}
42
+ end
43
+
44
+ def train(train_data)
45
+ train_data = symbolize_keys(train_data)
46
+ raise ArgumentError, 'Annotation must be not nil' unless train_data.has_key? :annotation
47
+ raise ArgumentError, 'Data must be not nil' unless train_data.has_key? :data
48
+
49
+ body = post("training_data", train_data.to_json, "Failed to post training data")
50
+ JSON.parse(body)
51
+ end
52
+
53
+ def retrain(option = {})
54
+ body = post(target_path(@config_id, 'retrain'), option.to_json, "Failed to retrain the model")
55
+ JSON.parse(body)
56
+ end
57
+
58
+ def trace(method, data, config = nil)
59
+ new_data = {}
60
+ new_data['method'] = method if method
61
+ new_data['data'] = data if data
62
+ new_data['config'] = config if config
63
+ body = post(target_path(@config_id, "trace"), new_data.to_json, "Failed to execute trace")
64
+ JSON.parse(body)
65
+ end
66
+
67
+ def evaluate(method, config = nil)
68
+ new_data = {}
69
+ new_data['method'] = method if method
70
+ new_data['config'] = config if config
71
+ body = post(target_path(@config_id, "evaluate"), new_data.to_json, "Failed to execute evaluate")
72
+ JSON.parse(body)
73
+ end
74
+
75
+ def labels
76
+ res = @http_cli.get(gen_uri(target_path(@config_id, "labels")))
77
+ raise_error("Failed to get labels the model has: #{error_suffix}", res) unless res.code =~ /2[0-9][0-9]/
78
+ JSON.parse(res.body)['labels']
79
+ end
80
+
81
+ def training_data(id)
82
+ raise ArgumentError, 'Id must be Integer' unless id.kind_of? Integer
83
+ res = @http_cli.get(gen_uri("training_data/#{id}"))
84
+ raise_error("Failed to get training data of the model: id = #{id}, #{error_suffix}", res) unless res.code =~ /2[0-9][0-9]/
85
+ JSON.parse(res.body)
86
+ end
87
+
88
+ def list_training_data(condition = {})
89
+ condition = condition.dup
90
+ condition[:page] ||= 1
91
+ condition[:per_page] ||= 10
92
+
93
+ res = @http_cli.get(gen_uri("training_data?page=#{condition[:page]}&per_page=#{condition[:per_page]}"))
94
+ raise_error("Failed to query training data of the model", res) unless res.code =~ /2[0-9][0-9]/
95
+ JSON.parse(res.body)
96
+ end
97
+
98
+ def delete_all_training_data
99
+ res = @http_cli.delete(gen_uri("training_data"))
100
+ raise_error("Failed to delete all training_data of the model: #{error_suffix}", res) unless res.code =~ /2[0-9][0-9]/
101
+ {}
102
+ end
103
+
104
+ def put_training_data(new_data = {})
105
+ new_data = symbolize_keys(new_data)
106
+ raise ArgumentError, 'Data must be not nil' unless new_data.has_key? :data
107
+ body = post('training_data', new_data.to_json, "Failed to post training data")
108
+ JSON.parse(body)
109
+ end
110
+
111
+ def update_training_data(id, new_data = {})
112
+ new_data = symbolize_keys(new_data)
113
+ raise ArgumentError, 'Id must be Integer' unless id.kind_of? Integer
114
+ raise ArgumentError, 'Data must be not nil' unless new_data.has_key? :data
115
+ send(:put, "training_data/#{id}", new_data.to_json, "Failed to update training data")
116
+ {}
117
+ end
118
+
119
+ def delete_training_data(id)
120
+ raise ArgumentError, 'Id must be Integer' unless id.kind_of? Integer
121
+ res = @http_cli.delete(gen_uri("training_data/#{id}"))
122
+ raise_error("Failed to delete a training data: id = #{id}, #{error_suffix}", res) unless res.code =~ /2[0-9][0-9]/
123
+ {}
124
+ end
125
+
126
+ def query(data)
127
+ data = {'data' => data}.to_json
128
+ res = post(target_path(@config_id, 'query'), data, "Failed to post data for query")
129
+ JSON.parse(res)
130
+ end
131
+
132
+ private
133
+
134
+ def symbolize_keys(data)
135
+ {}.tap{|d|
136
+ data.each{|k,v|
137
+ d[k.to_sym] = v
138
+ }
139
+ }
140
+ end
141
+
142
+ def stringify_keys(data)
143
+ {}.tap{|d|
144
+ data.each{|k,v|
145
+ d[k.to_s] = v
146
+ }
147
+ }
148
+ end
149
+
150
+ def post(path, data, error_message)
151
+ send(:post, path, data, error_message)
152
+ end
153
+
154
+ def send(method, path, data, error_message)
155
+ res = @http_cli.method(method).call(gen_uri(path), data, {'Content-Type' => 'application/json; charset=UTF-8', 'Content-Length' => data.length.to_s})
156
+ raise_error("#{error_message}: #{error_suffix}", res) unless res.code =~ /2[0-9][0-9]/ # TODO: enhance error information
157
+ res.body
158
+ end
159
+
160
+ def target_path(id, path)
161
+ "configs/#{id}/#{path}"
162
+ end
163
+
164
+ def gen_uri(path = nil)
165
+ if path
166
+ "/#{@client.api_version}/models/#{@model_id}/#{path}"
167
+ else
168
+ "/#{@client.api_version}/models/#{@model_id}"
169
+ end
170
+ end
171
+
172
+ def error_suffix
173
+ "model = #{@model_id}"
174
+ end
175
+
176
+ def raise_error(message, res)
177
+ raise APIError.new(message, res.code, JSON.parse(res.body))
178
+ end
179
+ end # module Model
180
+ end # module Bazil
data/lib/bazil/rest.rb ADDED
@@ -0,0 +1,78 @@
1
+ require 'forwardable'
2
+
3
+ require 'rubygems'
4
+ require 'json'
5
+ require 'time'
6
+ require 'net/http'
7
+ require 'digest/md5'
8
+
9
+ module Bazil
10
+ class REST
11
+ extend Forwardable
12
+
13
+ def initialize(http)
14
+ @http = http
15
+ end
16
+
17
+ def_delegators :@http, :read_timeout, :read_timeout=
18
+
19
+ def set_api_keys(key, secret)
20
+ @api_key = key
21
+ @secret_key = secret
22
+ true
23
+ end
24
+
25
+ def get(uri)
26
+ uri, header = add_api_signature(uri, nil)
27
+ @http.get(uri, header)
28
+ rescue Errno::ECONNREFUSED => e
29
+ raise_error('GET')
30
+ end
31
+
32
+ def put(uri, data, header = {})
33
+ uri, header = add_api_signature(uri, data, header)
34
+ @http.put(uri, data, header)
35
+ rescue Errno::ECONNREFUSED => e
36
+ raise_error('PUT')
37
+ end
38
+
39
+ def post(uri, data, header = {})
40
+ uri, header = add_api_signature(uri, data, header)
41
+ @http.post(uri, data, header)
42
+ rescue Errno::ECONNREFUSED => e
43
+ raise_error('POST')
44
+ end
45
+
46
+ def delete(uri)
47
+ uri, header = add_api_signature(uri, nil)
48
+ @http.delete(uri, header)
49
+ rescue Errno::ECONNREFUSED => e
50
+ raise_error('DELETE')
51
+ end
52
+
53
+ private
54
+ def add_api_signature(uri, data, header = {})
55
+ return uri, header unless @api_key and @secret_key
56
+
57
+ base,param = uri.split('?',2)
58
+ current_time = Time.now.httpdate
59
+
60
+ signature = ''
61
+ signature = data.gsub(/\s/, '') if data
62
+ parameters = (param || "").split('&')
63
+ parameters << "api_key=#{@api_key}"
64
+ signature << parameters.sort.join()
65
+ signature << current_time
66
+ signature << @secret_key
67
+ parameters << "api_signature=#{Digest::MD5.hexdigest(signature)}"
68
+ base << '?'
69
+ base << parameters.join('&')
70
+
71
+ return base, header.merge({'Date' => current_time})
72
+ end
73
+
74
+ def raise_error(method)
75
+ raise ConnectionError.new(method, @http.address, @http.port)
76
+ end
77
+ end # class Rest
78
+ end # module Bazil
data/lib/bazil.rb ADDED
@@ -0,0 +1 @@
1
+ require 'bazil/client'
@@ -0,0 +1,150 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bazil::Client do
4
+ describe Bazil::Client::Options, "with empty option provide default" do
5
+ let(:option) { Bazil::Client::Options.new({}) }
6
+
7
+ it "host name" do
8
+ expect(option.host).to eq('asp-bazil.preferred.jp')
9
+ end
10
+
11
+ it "port name" do
12
+ expect(option.port).to eq(443)
13
+ end
14
+
15
+ it "scheme" do
16
+ expect(option.scheme).to eq('https')
17
+ end
18
+
19
+ it "ca_file" do
20
+ expect(option.ca_file).to eq(nil)
21
+ end
22
+
23
+ it "ssl_version" do
24
+ expect(option.ssl_version).to eq('TLSv1')
25
+ end
26
+
27
+ it "verify_mode" do
28
+ expect(option.verify_mode).to eq(OpenSSL::SSL::VERIFY_PEER)
29
+ end
30
+ end
31
+
32
+ describe Bazil::Client::Options, "with all options can parse" do
33
+ let(:option) {
34
+ Bazil::Client::Options.new({
35
+ url: 'http://localhost:8080/',
36
+ ca_file: __FILE__, # always exists and absolute path
37
+ ssl_version: :SSLv3,
38
+ skip_verify: true
39
+ })
40
+ }
41
+
42
+ it "host name" do
43
+ expect(option.host).to eq('localhost')
44
+ end
45
+
46
+ it "port name" do
47
+ expect(option.port).to eq(8080)
48
+ end
49
+
50
+ it "scheme" do
51
+ expect(option.scheme).to eq('http')
52
+ end
53
+
54
+ it "ca_file" do
55
+ expect(option.ca_file).to eq(__FILE__)
56
+ end
57
+
58
+ it "ssl_version" do
59
+ expect(option.ssl_version).to eq('SSLv3')
60
+ end
61
+
62
+ it "skip_verify" do
63
+ expect(option.verify_mode).to eq(OpenSSL::SSL::VERIFY_NONE)
64
+ end
65
+ end
66
+
67
+ describe Bazil::Client::Options, "'s url" do
68
+ it "can derivate HTTP port number from http schema" do
69
+ option = Bazil::Client::Options.new(url: 'http://localhost/')
70
+ expect(option.port).to eq(80)
71
+ end
72
+
73
+ it "can derivate HTTPS port number from https schem" do
74
+ option = Bazil::Client::Options.new(url: 'https://localhost/')
75
+ expect(option.port).to eq(443)
76
+ end
77
+
78
+ it "can overwrite port number for http schema" do
79
+ option = Bazil::Client::Options.new(url: 'http://localhost:443/')
80
+ expect(option.port).to eq(443)
81
+ end
82
+
83
+ it "can overwrite port number for https schem" do
84
+ option = Bazil::Client::Options.new(url: 'https://localhost:80/')
85
+ expect(option.port).to eq(80)
86
+ end
87
+ end
88
+
89
+ describe Bazil::Client::Options, "will raise error for" do
90
+ it "invalid url" do
91
+ proc {
92
+ Bazil::Client::Options.new(url: 42)
93
+ }.should raise_error
94
+ end
95
+
96
+ it "empty url" do
97
+ proc {
98
+ Bazil::Client::Options.new(url: '')
99
+ }.should raise_error
100
+ end
101
+
102
+ it "invalid port" do
103
+ proc {
104
+ Bazil::Client::Options.new(url: 'http://localhost:ssl_port_please/')
105
+ }.should raise_error
106
+ end
107
+
108
+ it "unsupported scheme" do
109
+ proc {
110
+ Bazil::Client::Options.new(url: 'saitama://localhost:80/')
111
+ }.should raise_error
112
+ end
113
+
114
+ it "non string ca_file" do
115
+ proc {
116
+ Bazil::Client::Options.new(ca_file: 42)
117
+ }.should raise_error
118
+ end
119
+
120
+ it "relative ca_file" do
121
+ proc {
122
+ Bazil::Client::Options.new(ca_file: './' + File::basename(__FILE__))
123
+ }.should raise_error
124
+ end
125
+
126
+ it "non exists ca_file" do
127
+ proc {
128
+ Bazil::Client::Options.new(ca_file: '/:never:/:exist:/:file:/:path:')
129
+ }.should raise_error
130
+ end
131
+
132
+ it "invalid ssl_version" do
133
+ proc {
134
+ Bazil::Client::Options.new(ssl_version: 3.14)
135
+ }.should raise_error
136
+ end
137
+
138
+ it "unsupported ssl_version" do
139
+ proc {
140
+ Bazil::Client::Options.new(ssl_version: :SSL_version_saitama)
141
+ }.should raise_error
142
+ end
143
+
144
+ it "non-boolean skip_verify" do
145
+ proc {
146
+ Bazil::Client::Options.new(skip_verify: "YES")
147
+ }.should raise_error
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,166 @@
1
+ require 'spec_helper'
2
+
3
+ class FakeResponse
4
+ attr_reader :code, :message, :body
5
+ def initialize(code,message,body)
6
+ @code = code
7
+ @message = message
8
+ @body = body
9
+ end
10
+ end
11
+
12
+ class FakeClient
13
+ attr_accessor :http_client
14
+ def initialize
15
+ @http_client = Object.new
16
+ end
17
+ def api_version
18
+ 'v2'
19
+ end
20
+ end
21
+
22
+ describe Bazil::Client do
23
+ let(:client) { FakeClient.new }
24
+ let(:model_id) { 42 }
25
+ let(:config_id) { 184 }
26
+
27
+ def gen_model_path(path = "")
28
+ "/#{client.api_version}/models/#{model_id}#{path}"
29
+ end
30
+
31
+ def gen_model_config_path(path = "")
32
+ "/#{client.api_version}/models/#{model_id}/configs/#{config_id}#{path}"
33
+ end
34
+
35
+ def header_for_json(js)
36
+ {
37
+ "Content-Type" => "application/json; charset=UTF-8",
38
+ "Content-Length" => js.to_json.length.to_s
39
+ }
40
+ end
41
+
42
+ let(:model) {
43
+ Bazil::Model.new(client, model_id, config_id)
44
+ }
45
+
46
+ describe "model" do
47
+ it "model_config send GET /models/:model_id" do
48
+ result = {}
49
+ client.http_client.should_receive(:get).with(gen_model_path("")).and_return(FakeResponse.new("200", "OK", result.to_json))
50
+ res = model.model_config
51
+ expect(res).to eq(result)
52
+ end
53
+
54
+ it "update_model_config send PUT /models/:model_id" do
55
+ arg = {id: model_id, name: 'test'}
56
+ result = {}
57
+ client.http_client.should_receive(:put).with(gen_model_path(""), arg.to_json, header_for_json(arg)).and_return(FakeResponse.new("200", "OK", result.to_json))
58
+ res = model.update_model_config(arg)
59
+ expect(res).to eq(result)
60
+ end
61
+
62
+ it "config send GET /models/:model_id/configs/:config_id" do
63
+ result = {}
64
+ client.http_client.should_receive(:get).with(gen_model_config_path("")).and_return(FakeResponse.new("200", "OK", result.to_json))
65
+ res = model.config
66
+ expect(res).to eq(result)
67
+ end
68
+
69
+ it "update_config send PUT /models/:model_id/configs/:config_id" do
70
+ arg = {methd: 'arow', id: config_id, model_id: model_id}
71
+ result = {}
72
+ client.http_client.should_receive(:put).with(gen_model_config_path(""), arg.to_json, header_for_json(arg)).and_return(FakeResponse.new("200", "OK", result.to_json))
73
+ res = model.update_config(arg)
74
+ expect(res).to eq(result)
75
+ end
76
+
77
+ it "status request GET /models/:model_id/configs/:config_id/status" do
78
+ result = { "num_features" => 0, "num_train_queries" => 0, "num_labels" => 0, "num_queries" => 0, }
79
+ client.http_client.should_receive(:get).with(gen_model_config_path("/status")).and_return(FakeResponse.new("200", "OK", result.to_json))
80
+ expect(model.status).to eq(result)
81
+ end
82
+
83
+ it "retrain with empty option request POST /models/:model_id/configs/:config_id/retrain" do
84
+ arg = {}
85
+ result = { "total" => 1000, "elapsed_time" => 0.5 }
86
+ client.http_client.should_receive(:post).with(gen_model_config_path("/retrain"), arg.to_json, header_for_json(arg)).and_return(FakeResponse.new("200", "OK", result.to_json))
87
+ expect(model.retrain).to eq(result)
88
+ end
89
+
90
+ it "retrain with option request POST /models/:model_id/configs/:config_id/retrain" do
91
+ arg = {times: 10}
92
+ result = { "total" => 1000, "elapsed_time" => 0.5 }
93
+ client.http_client.should_receive(:post).with(gen_model_config_path("/retrain"), arg.to_json, header_for_json(arg)).and_return(FakeResponse.new("200", "OK", result.to_json))
94
+ expect(model.retrain(arg)).to eq(result)
95
+ end
96
+
97
+ it "query request POST /models/:model_id/configs/:config_id/query" do
98
+ arg = {data: {key: "value"}}
99
+ result = { "score" => { "Label1" => 0.5, "Label2" => -0.5, }, "classifier_result" => "Label1", }
100
+ client.http_client.should_receive(:post).with(gen_model_config_path("/query"), arg.to_json, header_for_json(arg)).and_return(FakeResponse.new("200", "OK", result.to_json))
101
+ expect(model.query(arg[:data])).to eq(result)
102
+ end
103
+
104
+ it "trace request POST /models/:model_id/configs/:config_id/trace" do
105
+ arg = {method: "feature_weights", data: {key: "value"}}
106
+ result = { "result" => { "Label1" => 0.5, "Label2" => -0.5, "feature_weights" => { } }, "data" => { "key" => "value" } }
107
+ client.http_client.should_receive(:post).with(gen_model_config_path("/trace"), arg.to_json, header_for_json(arg)).and_return(FakeResponse.new("200", "OK", result.to_json))
108
+ expect(model.trace(arg[:method], arg[:data])).to eq(result)
109
+ end
110
+
111
+ it "evaluate request POST /models/:model_id/configs/:config_id/evaluate" do
112
+ arg = {method: "cross_validation", config: {num_folds: 2}}
113
+ result = { "folds" => [{},{}], "result" => {} }
114
+ client.http_client.should_receive(:post).with(gen_model_config_path("/evaluate"), arg.to_json, header_for_json(arg)).and_return(FakeResponse.new("200", "OK", result.to_json))
115
+ expect(model.evaluate(arg[:method], arg[:config])).to eq(result)
116
+ end
117
+
118
+ it "put_training_data request POST /models/:model_id/training_data" do
119
+ arg = {data: {name: "P"}, annotation: "saitama"}
120
+ result = {"training_data_id" => 42}
121
+ client.http_client.should_receive(:post).with(gen_model_path("/training_data"), arg.to_json, header_for_json(arg)).and_return(FakeResponse.new("200", "OK", result.to_json))
122
+ expect(model.put_training_data(arg)).to eq(result)
123
+ end
124
+
125
+ it "list_training_data request GET /models/:model_id/training_data and set default page information" do
126
+ result = { "num_training_data" => 1000, "page" => 1, "per_page" => 10, "training_data" => [] }
127
+ client.http_client.should_receive(:get).with(gen_model_path("/training_data?page=1&per_page=10")).and_return(FakeResponse.new("200", "OK", result.to_json))
128
+ expect(model.list_training_data).to eq(result)
129
+ end
130
+
131
+ it "list_training_data with page information request GET /models/:model_id/training_data" do
132
+ arg = {page: 100, per_page: 450}
133
+ result = { "num_training_data" => 1000, "page" => 1, "per_page" => 10, "training_data" => [] }
134
+ client.http_client.should_receive(:get).with(gen_model_path("/training_data?page=#{arg[:page]}&per_page=#{arg[:per_page]}")).and_return(FakeResponse.new("200", "OK", result.to_json))
135
+ expect(model.list_training_data(arg)).to eq(result)
136
+ end
137
+
138
+ it "delete_all_training_data request DELETE /models/:model_id/training_data" do
139
+ result = {}
140
+ client.http_client.should_receive(:delete).with(gen_model_path("/training_data")).and_return(FakeResponse.new("200", "OK", result.to_json))
141
+ expect(model.delete_all_training_data).to eq(result)
142
+ end
143
+
144
+ it "training_data request GET /models/:model_id/training_data/:training_data_id" do
145
+ training_data_id = 123
146
+ result = {"data" => {"name" => "P"}, "annotation" => "saitama"}
147
+ client.http_client.should_receive(:get).with(gen_model_path("/training_data/#{training_data_id}")).and_return(FakeResponse.new("200", "OK", result.to_json))
148
+ expect(model.training_data(training_data_id)).to eq(result)
149
+ end
150
+
151
+ it "update_training_data request PUT /models/:model_id/training_data/:training_data_id" do
152
+ training_data_id = 123
153
+ arg = {data: {name: "P"}, annotation: "gunnma"}
154
+ result = {}
155
+ client.http_client.should_receive(:put).with(gen_model_path("/training_data/#{training_data_id}"), arg.to_json, header_for_json(arg)).and_return(FakeResponse.new("200", "OK", result.to_json))
156
+ expect(model.update_training_data(training_data_id, arg)).to eq(result)
157
+ end
158
+
159
+ it "delete_all_training_data request DELETE /models/:model_id/training_data/:training_data_id" do
160
+ training_data_id = 123
161
+ result = {}
162
+ client.http_client.should_receive(:delete).with(gen_model_path("/training_data/#{training_data_id}")).and_return(FakeResponse.new("200", "OK", result.to_json))
163
+ expect(model.delete_training_data(training_data_id)).to eq(result)
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,11 @@
1
+ require 'simplecov'
2
+ SimpleCov.start do
3
+ add_filter 'spec'
4
+ add_filter 'vendor/bundle'
5
+ end
6
+
7
+ require 'rubygems'
8
+ require 'bazil'
9
+ require 'net/http'
10
+ require 'rspec'
11
+ require 'rspec/mocks'
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bazil_client
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Nobuyuki Kubota
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-14 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.9.2
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 0.9.2
30
+ - !ruby/object:Gem::Dependency
31
+ name: simplecov
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 0.5.4
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 0.5.4
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: 1.0.0
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: 1.0.0
62
+ description: Ruby client of Bazil
63
+ email: bazil-info@preferred.jp
64
+ executables: []
65
+ extensions: []
66
+ extra_rdoc_files: []
67
+ files:
68
+ - .gitignore
69
+ - ChangeLog.md
70
+ - Gemfile
71
+ - LICENSE
72
+ - README
73
+ - Rakefile
74
+ - VERSION
75
+ - bazil_client.gemspec
76
+ - lib/bazil.rb
77
+ - lib/bazil/client.rb
78
+ - lib/bazil/error.rb
79
+ - lib/bazil/model.rb
80
+ - lib/bazil/rest.rb
81
+ - spec/bazil/client_spec.rb
82
+ - spec/bazil/model_spec.rb
83
+ - spec/spec_helper.rb
84
+ homepage: https://asp-bazil.preferred.jp/
85
+ licenses:
86
+ - MIT
87
+ post_install_message:
88
+ rdoc_options: []
89
+ require_paths:
90
+ - lib
91
+ required_ruby_version: !ruby/object:Gem::Requirement
92
+ none: false
93
+ requirements:
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: 1.9.0
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ! '>='
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ segments:
104
+ - 0
105
+ hash: -4262472802805615291
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 1.8.23
109
+ signing_key:
110
+ specification_version: 3
111
+ summary: Ruby client of Bazil
112
+ test_files:
113
+ - spec/bazil/client_spec.rb
114
+ - spec/bazil/model_spec.rb
115
+ - spec/spec_helper.rb