bazil_client 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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