indico 0.2.5 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -24,7 +24,7 @@ For API key registration and setup, checkout our [quickstart guide](http://docs.
24
24
 
25
25
  Full Documentation
26
26
  ------------
27
- Detailed documentation and further code examples are available at [indico.reame.io](http://indico.readme.io/v2.0/docs/ruby)
27
+ Detailed documentation and further code examples are available at [indico.readme.io](http://indico.readme.io/v2.0/docs/ruby)
28
28
 
29
29
  Examples
30
30
  ---------
@@ -72,6 +72,46 @@ Each `Indico` method has a corresponding batch method for analyzing many example
72
72
  => [0.9899001220871786, 0.005709885173415242]
73
73
  ```
74
74
 
75
+
76
+ Calling multiple APIs with a single function
77
+ ---------
78
+ There are two multiple API functions `predict_text` and `predict_image` (and their batch counterparts). These functions are similar to the existing api functions, but take in an additional `apis` argument as an array of strings of API names (defaults to all existing apis). `predict_text` accepts a list of existing text APIs and vice versa for `predict_image`.
79
+
80
+ Accepted text API names: `text_tags, political, sentiment, language`
81
+
82
+ Accepted image API names: `fer, facial_features, image_features`
83
+
84
+ ```ruby
85
+ > require 'indico'
86
+
87
+ => true
88
+
89
+ > Indico.api_key = "YOUR_API_KEY"
90
+
91
+ => "YOUR_API_KEY"
92
+
93
+ > Indico.predict_text("Best day ever", ["sentiment", "language"])
94
+
95
+ => {"sentiment"=>0.9899001220871786, "language"=>{"Swedish"=>0.0022464881013042294, "Vietnamese"=>9.887170914498351e-05, ...}}
96
+
97
+ > Indico.batch_predict_text(["Best day ever", "Worst day ever"], ["sentiment", "language"])
98
+
99
+ => {"sentiment"=>[0.9899001220871786, 0.005709885173415242], "language"=>[{"Swedish"=>0.0022464881013042294, "Vietnamese"=>9.887170914498351e-05, "Romanian"=>0.00010661175919993216, ...}, {"Swedish"=>0.4924352805804646, "Vietnamese"=>0.028574824174911372, "Romanian"=>0.004185623723173551, "Dutch"=>0.000717033819689362, "Korean"=>0.0030093489153785826, ...}]}
100
+
101
+ > test_face = Array.new(48){Array.new(48){Array.new(3){rand(100)/100.0}}}
102
+
103
+ => [[[0.66, 0.99, 0.03], [0.42, 0.72, 0.86], [0.95, 0.44, 0.61], [0.39, 0.57, 0.4], [0.06, 0.52, 0.43], [0.11, 0.09, 0.78], [0.35, 0.69, 0.32], [0.44, 0.5, 0.26], [0.71, 0.75, 0.64], [0.91, 0.92, 0.14], [0.71, 0.98, 0.02], ..]]
104
+
105
+ > Indico.predict_image(test_face, ["fer", "facial_features"])
106
+
107
+ => {"facial_features"=>[0.0, -0.026176479280200796, 0.20707644777495776, ...], "fer"=>{"Angry"=>0.08877494466353497, "Sad"=>0.3933999409104264, "Neutral"=>0.1910612654566151, "Surprise"=>0.0346146405941845, "Fear"=>0.17682159820518667, "Happy"=>0.11532761017005204}}
108
+
109
+ > Indico.batch_predict_image([test_face, test_face], ["fer", "facial_features"])
110
+
111
+ => {"facial_features"=>[[0.0, -0.026176479280200796, 0.20707644777495776, ...], [0.0, -0.026176479280200796, 0.20707644777495776, ...]], "fer"=>[{"Angry"=>0.08877494466353497, "Sad"=>0.3933999409104264, "Neutral"=>0.1910612654566151, "Surprise"=>0.0346146405941845, "Fear"=>0.17682159820518667, "Happy"=>0.11532761017005204}, {"Angry"=>0.08877494466353497, "Sad"=>0.3933999409104264, "Neutral"=>0.1910612654566151, "Surprise"=>0.0346146405941845, "Fear"=>0.17682159820518667, "Happy"=>0.11532761017005204}]}
112
+ ```
113
+
114
+
75
115
  ## Contributing
76
116
 
77
117
  1. Fork it ( https://github.com/[my-github-username]/indico/fork )
data/indico.gemspec CHANGED
@@ -19,6 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ['lib']
20
20
 
21
21
  spec.add_runtime_dependency 'inifile', '~> 3.0.0'
22
+ spec.add_runtime_dependency 'oily_png', '~> 1.2.0'
22
23
  spec.add_development_dependency 'bundler', '~> 1.6'
23
24
  spec.add_development_dependency 'rake'
24
25
  spec.add_development_dependency 'rspec'
@@ -0,0 +1,3 @@
1
+ class IndicoError < StandardError
2
+
3
+ end
data/lib/indico/helper.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'base64'
1
+ require_relative './errors'
2
2
 
3
3
  module Indico
4
4
  private
@@ -14,24 +14,22 @@ module Indico
14
14
  def self.api_handler(data, api, config)
15
15
  server = nil
16
16
  api_key = nil
17
- unless config.nil?
18
- server = config[:cloud]
19
- api_key = config[:api_key]
20
- end
17
+
21
18
  d = {}
22
19
  d['data'] = data
23
- data_dict = JSON.dump(d)
24
20
 
25
- if api_key.nil?
26
- api_key = Indico.config['auth']
21
+ unless config.nil?
22
+ server = config[:cloud]
23
+ api_key = config[:api_key]
24
+ apis = config["apis"]
25
+ d = d.merge(config)
27
26
  end
28
27
 
29
- if server.nil?
30
- server = Indico.config['cloud']
31
- end
28
+ url = url_join((server or Indico.config['cloud']), api) +
29
+ (apis ? "?apis=" + apis.join(",") : "")
32
30
 
33
- response = make_request(url_join(server, api), data_dict,
34
- add_api_key_to_header(api_key))
31
+ response = make_request(url, JSON.dump(d),
32
+ add_api_key_to_header((api_key or Indico.config['auth'])))
35
33
 
36
34
 
37
35
  results = JSON.parse(response.body)
@@ -65,7 +63,7 @@ module Indico
65
63
  if api_key.nil?
66
64
  raise ArgumentError, 'api key is required'
67
65
  end
68
- headers = { 'Content-Type' => 'application/json', 'Accept' => 'text/plain' }
66
+ headers = HEADERS
69
67
  headers['X-ApiKey'] = api_key
70
68
  headers
71
69
  end
@@ -0,0 +1,109 @@
1
+ require 'oily_png'
2
+ require 'base64'
3
+
4
+ module Indico
5
+ def self.preprocess(image, size, batch)
6
+ # image is [[f,f,f,f, ...], ..] or [[[f,f,f],[f,f,f],[f,f,f]], ...]
7
+ if batch
8
+ # Batch Request
9
+ im_array = Array.new
10
+
11
+ # process each image
12
+ image.each do |_image|
13
+ im_array.push(preprocess(_image, size, false))
14
+ end
15
+
16
+ return im_array
17
+ end
18
+
19
+ if image.class == String
20
+ decoded_image = handle_string_input(image)
21
+ elsif image.class == Array
22
+ decoded_image = handle_array_input(image)
23
+ else
24
+ raise Exception.new("Image input must be nested array of pixels, filename, or base64 string")
25
+ end
26
+
27
+ # Resize and export base64 encoded string
28
+ return decoded_image.resize(size, size).to_data_url.gsub("data:image/png;base64," ,"")
29
+ end
30
+
31
+ def self.handle_string_input(str)
32
+ # Handles string input
33
+ if File.file?(str)
34
+ # Handling File Inputs
35
+ return ChunkyPNG::Image.from_file(str)
36
+ end
37
+
38
+ regex = %r{^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$}.match(str)
39
+ # Base 64 handling
40
+ if regex == nil
41
+ raise Exception.new("String is not a valid absolute filepath or base64 string")
42
+ else
43
+ return ChunkyPNG::Image.from_data_url("data:image/png;base64," + str.gsub("data:image/png;base64," ,""))
44
+ end
45
+ end
46
+
47
+ def self.handle_array_input(image)
48
+ # Handles properly formatting and loading array of pixels
49
+ # Single Request
50
+ warn "Warning! Array input as image will be deprecated in the next major release.\n Consider using filepaths or base64 encoded strings"
51
+
52
+ dimens = get_dimension(image)
53
+ isFloat = array_contains_float(image, dimens)
54
+
55
+ if dimens.size > 3
56
+ raise Exception.new("Nested array of image must be [[p, p, ...]] or [[[r,g,b], [r,g,b], ...]]")
57
+ end
58
+
59
+ decoded_image = ChunkyPNG::Image.new(dimens[0], dimens[1])
60
+
61
+ # Manually loading the pixels into the ChunkyImage object
62
+ (0..dimens[0] - 1).each do |x|
63
+ (0..dimens[1] - 1).each do |y|
64
+ pixel = image[x][y]
65
+ if pixel.is_a?(Array)
66
+ if isFloat
67
+ pixel.map! {|a| (255 * a).to_i}
68
+ end
69
+ decoded_image.set_pixel(x,y, ChunkyPNG::Color.rgb(pixel[0], pixel[1], pixel[2]))
70
+ else
71
+ if isFloat
72
+ pixel = (pixel * 255).to_i
73
+ end
74
+ decoded_image.set_pixel(x,y, ChunkyPNG::Color.rgb(pixel, pixel, pixel))
75
+ end
76
+ end
77
+ end
78
+
79
+ return decoded_image
80
+ end
81
+
82
+
83
+ def self.get_dimension(array)
84
+ return [] unless array.is_a?(Array)
85
+ return [array.size] + get_dimension(array[0])
86
+ end
87
+
88
+ def self.array_contains_float(array, dimens)
89
+ # Determines whether or not the array contains floats
90
+ if not array.is_a?(Array)
91
+ return array.class == Float
92
+ end
93
+ elem = array[0]
94
+ (0..dimens.size - 2).each do |i|
95
+ elem = elem[0]
96
+ end
97
+
98
+ return elem.class == Float
99
+ end
100
+
101
+ def self.get_rgb(value)
102
+ # Returns Integer encoding of RGB value used by ChunkyPNG
103
+ return [
104
+ ChunkyPNG::Color.r(value),
105
+ ChunkyPNG::Color.g(value),
106
+ ChunkyPNG::Color.b(value)
107
+ ]
108
+ end
109
+ end
@@ -0,0 +1,47 @@
1
+ require_relative './errors'
2
+
3
+ module Indico
4
+ CLIENT_TO_SERVER = {
5
+ "political" => "political",
6
+ "sentiment" => "sentiment",
7
+ "language" => "language",
8
+ "text_tags" => "texttags",
9
+ "fer" => "fer",
10
+ "facial_features" => "facialfeatures",
11
+ "image_features" => "imagefeatures"
12
+ }
13
+
14
+ SERVER_TO_CLIENT = CLIENT_TO_SERVER.invert
15
+ def self.multi(data, type, apis, allowed, batch = false, config)
16
+ converted_apis = Array.new
17
+ apis.each { |api|
18
+ if not allowed.include? api
19
+ fail api + " is not a valid api for " + type + " requests. Please use: " + allowed.join(", ")
20
+ else
21
+ converted_apis.push(CLIENT_TO_SERVER[api])
22
+ end
23
+ }
24
+
25
+ if config.nil?
26
+ config = {}
27
+ end
28
+
29
+ config["apis"] = converted_apis
30
+ response = api_handler(data, batch ? "apis/batch" : "apis", config)
31
+ results = handle_multi(response)
32
+
33
+ results
34
+ end
35
+
36
+ def self.handle_multi(results)
37
+ converted_results = Hash.new
38
+ results.each do |key, value|
39
+ if value.is_a?(Hash) && value.has_key?("results")
40
+ converted_results[SERVER_TO_CLIENT[key]] = value["results"]
41
+ else
42
+ raise IndicoError, 'unexpected result from ' + key + '\n\t' + value.fetch("error", "")
43
+ end
44
+ end
45
+ converted_results
46
+ end
47
+ end
@@ -1,4 +1,13 @@
1
1
  require 'inifile'
2
+ require_relative 'version'
3
+
4
+ HEADERS = { 'Content-Type' => 'application/json',
5
+ 'Accept' => 'application/json',
6
+ 'client-lib' => 'ruby',
7
+ 'version-number' => Indico::VERSION }
8
+ # APIS
9
+ TEXT_APIS = ["political", "sentiment", "language", "text_tags"]
10
+ IMAGE_APIS = ["fer", "facial_features", "image_features"]
2
11
 
3
12
  module Indico
4
13
  private
@@ -1,3 +1,3 @@
1
1
  module Indico
2
- VERSION = '0.2.5'
2
+ VERSION = '0.3.0'
3
3
  end
data/lib/indico.rb CHANGED
@@ -1,16 +1,13 @@
1
1
  require 'indico/version'
2
2
  require 'indico/helper'
3
+ require 'indico/image'
4
+ require 'indico/multi'
3
5
  require 'indico/settings'
4
6
  require 'uri'
5
7
  require 'json'
6
8
  require 'net/https'
7
9
 
8
10
  module Indico
9
- HEADERS = { 'Content-Type' => 'application/json',
10
- 'Accept' => 'application/json',
11
- 'client-lib' => 'ruby',
12
- 'version-number' => '0.2.5' }
13
-
14
11
  def self.api_key
15
12
  config['auth']
16
13
  end
@@ -67,27 +64,47 @@ module Indico
67
64
  api_handler(test_text, 'texttags/batch', config)
68
65
  end
69
66
 
70
- def self.fer(face, config = nil)
71
- api_handler(face, 'fer', config)
67
+ def self.fer(test_image, config = nil)
68
+ api_handler(preprocess(test_image, 48, false), 'fer', config)
69
+ end
70
+
71
+ def self.batch_fer(test_image, config = nil)
72
+ api_handler(preprocess(test_image, 48, true), 'fer/batch', config)
73
+ end
74
+
75
+ def self.facial_features(test_image, config = nil)
76
+ api_handler(preprocess(test_image, 48, false), 'facialfeatures', config)
77
+ end
78
+
79
+ def self.batch_facial_features(test_image, config = nil)
80
+ api_handler(preprocess(test_image, 48, true), 'facialfeatures/batch', config)
81
+ end
82
+
83
+ def self.image_features(test_image, config = nil)
84
+ api_handler(preprocess(test_image, 64, false), 'imagefeatures', config)
72
85
  end
73
86
 
74
- def self.batch_fer(test_text, config = nil)
75
- api_handler(test_text, 'fer/batch', config)
87
+ def self.batch_image_features(test_image, config = nil)
88
+ api_handler(preprocess(test_image, 64, true), 'imagefeatures/batch', config)
76
89
  end
77
90
 
78
- def self.facial_features(face, config = nil)
79
- api_handler(face, 'facialfeatures', config)
91
+ def self.predict_image(face, apis = IMAGE_APIS, config = nil)
92
+ api_hash = {"apis" => apis}
93
+ multi(preprocess(face, 48, false), "image", apis, IMAGE_APIS, config ? config.update(api_hash) : api_hash)
80
94
  end
81
95
 
82
- def self.batch_facial_features(test_text, config = nil)
83
- api_handler(test_text, 'facialfeatures/batch', config)
96
+ def self.predict_text(test_text, apis = TEXT_APIS, config = nil)
97
+ api_hash = {"apis" => apis}
98
+ multi(test_text, "text", apis, TEXT_APIS, config ? config.update(api_hash) : api_hash)
84
99
  end
85
100
 
86
- def self.image_features(face, config = nil)
87
- api_handler(face, 'imagefeatures', config)
101
+ def self.batch_predict_image(face, apis, config = nil)
102
+ api_hash = {"apis" => apis}
103
+ multi(preprocess(face, 48, true), "image", apis, IMAGE_APIS, true, config ? config.update(api_hash) : api_hash)
88
104
  end
89
105
 
90
- def self.batch_image_features(test_text, config = nil)
91
- api_handler(test_text, 'imagefeatures/batch', config)
106
+ def self.batch_predict_text(test_text, apis, config = nil)
107
+ api_hash = {"apis" => apis}
108
+ multi(test_text, "text", apis, TEXT_APIS, true, config ? config.update(api_hash) : api_hash)
92
109
  end
93
110
  end
Binary file
@@ -0,0 +1 @@
1
+ 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
@@ -21,7 +21,7 @@ describe Indico do
21
21
  it 'should access a private cloud' do
22
22
  expected_keys = Set.new(%w(Conservative Green Liberal Libertarian))
23
23
  data = ['Guns don\'t kill people.', ' People kill people.']
24
-
24
+
25
25
  # for mocking: use http instead of https to route requests to our public cloud
26
26
  Indico.cloud_protocol = 'http://'
27
27
  response = Indico.batch_political(data, @config_private_cloud)
@@ -104,26 +104,63 @@ describe Indico do
104
104
  it 'should tag face with correct facial expression' do
105
105
  expected_keys = Set.new(%w(Angry Sad Neutral Surprise Fear Happy))
106
106
  test_face = Array.new(48) { Array.new(48) { rand(100) / 100.0 } }
107
- response = Indico.batch_fer([test_face, test_face], @config)
107
+ silent_warnings do
108
+ response = Indico.batch_fer([test_face, test_face], @config)
108
109
 
109
- expect(Set.new(response[0].keys)).to eql(expected_keys)
110
- expect(Set.new(response[1].keys)).to eql(expected_keys)
110
+ expect(Set.new(response[0].keys)).to eql(expected_keys)
111
+ expect(Set.new(response[1].keys)).to eql(expected_keys)
112
+ end
111
113
  end
112
114
 
113
115
  it 'should tag face with correct facial features' do
114
116
  test_face = Array.new(48) { Array.new(48) { rand(100) / 100.0 } }
115
- response = Indico.batch_facial_features([test_face, test_face], @config)
116
- expect(response[0].length).to eql(48)
117
- expect(response[1].length).to eql(48)
117
+ silent_warnings do
118
+ response = Indico.batch_facial_features([test_face, test_face], @config)
119
+ expect(response[0].length).to eql(48)
120
+ expect(response[1].length).to eql(48)
121
+ end
118
122
  end
119
123
 
120
124
  it 'should tag image with correct image features' do
121
125
  test_image = Array.new(48) { Array.new(48) { rand(100) / 100.0 } }
122
- response = Indico.batch_image_features([test_image, test_image], @config)
123
- expect(response[0].length).to eql(2048)
124
- expect(response[1].length).to eql(2048)
126
+ silent_warnings do
127
+ response = Indico.batch_image_features([test_image, test_image], @config)
128
+ expect(response[0].length).to eql(2048)
129
+ expect(response[1].length).to eql(2048)
130
+ end
125
131
  end
126
132
 
133
+ it "should respond with all text apis called in batch" do
134
+ expected_keys = Set.new(TEXT_APIS)
135
+ response = Indico.batch_predict_text(["Worst movie ever."], TEXT_APIS)
136
+
137
+ expect(response.class).to eql(Hash)
138
+ expect(Set.new(response.keys)).to eql(expected_keys)
139
+ end
140
+
141
+ it "should respond with all image apis called in batch" do
142
+ test_image = Array.new(48){Array.new(48){Array.new(3){rand(100)/100.0}}}
143
+ expected_keys = Set.new(IMAGE_APIS)
144
+ silent_warnings do
145
+ response = Indico.batch_predict_image([test_image], IMAGE_APIS)
146
+
147
+ expect(response.class).to eql(Hash)
148
+ expect(Set.new(response.keys)).to eql(expected_keys)
149
+ end
150
+ end
151
+
152
+ it "should respond with all image apis called on int array in batch" do
153
+ test_image = Array.new(48){Array.new(48){Array.new(3){rand(100)}}}
154
+ expected_keys = Set.new(IMAGE_APIS)
155
+ silent_warnings do
156
+ response = Indico.batch_predict_image([test_image], IMAGE_APIS)
157
+
158
+ expect(response.class).to eql(Hash)
159
+ expect(Set.new(response.keys)).to eql(expected_keys)
160
+ end
161
+ end
162
+
163
+
127
164
  # Uncomment when frontend updated to accept image urls
128
165
  # it "should accept image urls" do
129
166
  # test_image = 'http://icons.iconarchive.com/icons/oxygen-icons.org/' +
data/spec/indico_spec.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'spec_helper'
2
2
  require 'set'
3
+ require 'oily_png'
3
4
 
4
5
  describe Indico do
5
6
  before do
@@ -18,7 +19,7 @@ describe Indico do
18
19
  it 'should tag text with correct political tags' do
19
20
  expected_keys = Set.new(%w(Conservative Green Liberal Libertarian))
20
21
  data = 'Guns don\'t kill people. People kill people.'
21
-
22
+
22
23
  # for mocking: use http to redirect requests to our public cloud endpoint
23
24
  Indico.cloud_protocol = 'http://'
24
25
  response = Indico.political(data, @config)
@@ -94,33 +95,91 @@ describe Indico do
94
95
  it 'should tag face with correct facial expression' do
95
96
  expected_keys = Set.new(%w(Angry Sad Neutral Surprise Fear Happy))
96
97
  test_face = Array.new(48) { Array.new(48) { rand(100) / 100.0 } }
97
-
98
- response = Indico.fer(test_face)
99
-
100
- expect(Set.new(response.keys)).to eql(expected_keys)
98
+ silent_warnings do
99
+ response = Indico.fer(test_face)
100
+ expect(Set.new(response.keys)).to eql(expected_keys)
101
+ end
101
102
  end
102
103
 
103
104
  it 'should tag face with correct facial features' do
104
105
  test_face = Array.new(48) { Array.new(48) { rand(100) / 100.0 } }
105
- response = Indico.facial_features(test_face)
106
-
107
- expect(response.length).to eql(48)
106
+ silent_warnings do
107
+ response = Indico.facial_features(test_face)
108
+ expect(response.length).to eql(48)
109
+ end
108
110
  end
109
111
 
110
112
  it 'should tag image with correct image features' do
111
113
  test_image = Array.new(48) { Array.new(48) { rand(100) / 100.0 } }
112
- response = Indico.image_features(test_image)
113
-
114
- expect(response.length).to eql(2048)
114
+ silent_warnings do
115
+ response = Indico.image_features(test_image)
116
+ expect(response.length).to eql(2048)
117
+ end
115
118
  end
116
119
 
117
120
  it "should tag rgb image with correct image features" do
118
121
  test_image = Array.new(48){Array.new(48){Array.new(3){rand(100)/100.0}}}
119
- response = Indico.image_features(test_image)
122
+ silent_warnings do
123
+ response = Indico.image_features(test_image)
124
+ expect(response.length).to eql(2048)
125
+ end
126
+ end
127
+
128
+ it "should be able to load image from path" do
129
+ expected_keys = Set.new(%w(Angry Sad Neutral Surprise Fear Happy))
130
+ response = Indico.fer(File.dirname(__FILE__) + "/data/happy.png")
131
+
132
+ expect(Set.new(response.keys)).to eql(expected_keys)
133
+ expect(response["Happy"]).to be > 0.5
134
+ end
120
135
 
121
- expect(response.length).to eql(2048)
136
+ it "should be able to load image from b64" do
137
+ expected_keys = Set.new(%w(Angry Sad Neutral Surprise Fear Happy))
138
+ response = Indico.fer(File.open(File.dirname(__FILE__) + "/data/happy64.txt", 'rb') { |f| f.read })
139
+
140
+ expect(Set.new(response.keys)).to eql(expected_keys)
141
+ expect(response["Happy"]).to be > 0.5
122
142
  end
123
143
 
144
+ it "should respond with all text apis called" do
145
+ expected_keys = Set.new(TEXT_APIS)
146
+ response = Indico.predict_text("Worst movie ever.", TEXT_APIS)
147
+
148
+ expect(response.class).to eql(Hash)
149
+ expect(Set.new(response.keys)).to eql(expected_keys)
150
+ end
151
+
152
+ it "should respond with all text apis called by default" do
153
+ expected_keys = Set.new(TEXT_APIS)
154
+ response = Indico.predict_text("Worst movie ever.")
155
+
156
+ expect(response.class).to eql(Hash)
157
+ expect(Set.new(response.keys)).to eql(expected_keys)
158
+ end
159
+
160
+ it "should respond with all image apis called" do
161
+ test_image = Array.new(48){Array.new(48){Array.new(3){rand(100)/100.0}}}
162
+ expected_keys = Set.new(IMAGE_APIS)
163
+ silent_warnings do
164
+ response = Indico.predict_image(test_image, IMAGE_APIS)
165
+
166
+ expect(response.class).to eql(Hash)
167
+ expect(Set.new(response.keys)).to eql(expected_keys)
168
+ end
169
+ end
170
+
171
+ it "should respond with all image apis called on int array" do
172
+ test_image = Array.new(48){Array.new(48){Array.new(3){rand(100)}}}
173
+ expected_keys = Set.new(IMAGE_APIS)
174
+ silent_warnings do
175
+ response = Indico.predict_image(test_image, IMAGE_APIS)
176
+
177
+ expect(response.class).to eql(Hash)
178
+ expect(Set.new(response.keys)).to eql(expected_keys)
179
+ end
180
+ end
181
+
182
+
124
183
  # Uncomment when frontend updated to accept image urls
125
184
  # it 'should accept image urls' do
126
185
  # response = Indico.image_features('http://icons.iconarchive.com/icons/' +
data/spec/spec_helper.rb CHANGED
@@ -1,4 +1,13 @@
1
1
  require 'indico'
2
+ require 'stringio'
3
+
4
+ def silent_warnings
5
+ old_stderr = $stderr
6
+ $stderr = StringIO.new
7
+ yield
8
+ ensure
9
+ $stderr = old_stderr
10
+ end
2
11
 
3
12
  # This file was generated by the `rspec --init` command. Conventionally, all
4
13
  # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: indico
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2015-05-28 00:00:00.000000000 Z
15
+ date: 2015-06-11 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: inifile
@@ -30,6 +30,22 @@ dependencies:
30
30
  - - ~>
31
31
  - !ruby/object:Gem::Version
32
32
  version: 3.0.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: oily_png
35
+ requirement: !ruby/object:Gem::Requirement
36
+ none: false
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 1.2.0
41
+ type: :runtime
42
+ prerelease: false
43
+ version_requirements: !ruby/object:Gem::Requirement
44
+ none: false
45
+ requirements:
46
+ - - ~>
47
+ - !ruby/object:Gem::Version
48
+ version: 1.2.0
33
49
  - !ruby/object:Gem::Dependency
34
50
  name: bundler
35
51
  requirement: !ruby/object:Gem::Requirement
@@ -96,11 +112,16 @@ files:
96
112
  - Rakefile
97
113
  - indico.gemspec
98
114
  - lib/indico.rb
115
+ - lib/indico/errors.rb
99
116
  - lib/indico/helper.rb
117
+ - lib/indico/image.rb
118
+ - lib/indico/multi.rb
100
119
  - lib/indico/settings.rb
101
120
  - lib/indico/version.rb
102
121
  - spec/config/.indicorc.test
103
122
  - spec/config/.indicorc.test.2
123
+ - spec/data/happy.png
124
+ - spec/data/happy64.txt
104
125
  - spec/indico_batch_spec.rb
105
126
  - spec/indico_spec.rb
106
127
  - spec/settings_spec.rb