has_face 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use @has_face --create
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,133 @@
1
+ # Has face
2
+ An Active model validator to validate that images contain faces by
3
+ using the face.com API. The validator works by uploading images to
4
+ face.com and then checking to see if any faces were tagged in the photo.
5
+ It has been tested with carrierwave attachments but will work with any
6
+ attachment type that correctly responds to a `path` method.
7
+
8
+ ## Requirements
9
+ - Rails 3.0 or greater
10
+ - An account for accessing the face.com API (They are free at the moment)
11
+
12
+ ## Installation
13
+ Add has_face to your Gemfile and then bundle
14
+
15
+ ``` ruby
16
+ gem 'has_face'
17
+ ```
18
+
19
+ Once installed run the generator to create an initializer
20
+
21
+ ``` ruby
22
+ rails g has_face:install
23
+ ```
24
+
25
+ Then open up `config/initializers/has_face.rb` and enter your face.com
26
+ API details.
27
+
28
+ ``` ruby
29
+ # config/initializers/has_face.rb
30
+ HasFace.configure do |config|
31
+ config.api_key = 'your face.com API key'
32
+ config.api_secret = 'your face.com API secret'
33
+ config.skip_validation_on_error = false
34
+ end
35
+ ```
36
+
37
+ ## Usage
38
+
39
+ Simply add a validation to the image you want to ensure has faces:
40
+
41
+ ``` ruby
42
+ class User < ActiveRecord::Base
43
+ validates :avatar, :has_face => true
44
+ end
45
+ ```
46
+
47
+ The `allow_nil` and `allow_blank` options are supported:
48
+
49
+ ``` ruby
50
+ class User < ActiveRecord::Base
51
+ validates :avatar, :has_face => true, :allow_blank => true
52
+ end
53
+ ```
54
+
55
+ ## i18n
56
+
57
+ Error messages generated are i18n safe. To alter the error message shown
58
+ add this to your `config/locale/en.yml`
59
+
60
+ ``` ruby
61
+ en:
62
+ activerecord:
63
+ errors:
64
+ messages:
65
+ no_face: "We couldn't see a face in your photo, try taking another one."
66
+ ```
67
+
68
+
69
+ ## Error Handling
70
+
71
+ By default has_face will raise either a `HasFace::FaceAPIError` or
72
+ `HasFace::HTTPRequestError` on failure. You will need to catch these
73
+ errors and then take the appropriate action in your application like so:
74
+
75
+ ``` ruby
76
+ begin
77
+ @user = User.create(params[:user])
78
+ rescue HasFace::FaceAPIError, HasFace::HTTPRequestError => e
79
+ # Perform some sort of action.
80
+ end
81
+ ```
82
+
83
+ If you would like to skip valdiation when a HTTP or API error occurs
84
+ then simply turn on the `skip_validation_on_error` configuration option:
85
+
86
+ ``` ruby
87
+ HasFace.configure do |config|
88
+ config.skip_validation_on_error = true
89
+ end
90
+ ```
91
+
92
+ If an error does occur then it will be logged as a warning in the log
93
+ for your applications current environment.
94
+
95
+ ## Testing has_face
96
+
97
+ To speed up your test suite, you can disable face validations by setting the
98
+ `enable_validation` config value to false, this is usally best done in
99
+ your test config.
100
+
101
+ ``` ruby
102
+ HasFace.enable_validation = false
103
+ ```
104
+
105
+ Has Face supplies a matcher which you can use in your tests, to
106
+ enable it, include the matchers module in your rspec config.
107
+
108
+ ``` ruby
109
+ RSpec.configure do |config|
110
+ config.include HasFace::Test::Matchers
111
+ end
112
+ ```
113
+
114
+
115
+ Once included he matcher can be used:
116
+
117
+ ``` ruby
118
+ context 'validations' do
119
+ it { should validate_has_face_for :avatar }
120
+ end
121
+ ```
122
+
123
+ The options `allow_blank` and `allow_nil` can also be passed to the matcher:
124
+
125
+ ``` ruby
126
+ it { should validate_has_face_for :avatar, :allow_blank => true }
127
+ ```
128
+
129
+ ### Contributing
130
+
131
+ Fork on GitHub and after you’ve committed tested patches, send a pull request.
132
+
133
+ To get tests running simply run `bundle install` and then `rspec spec`
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
data/has_face.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "has_face/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "has_face"
7
+ s.version = HasFace::VERSION
8
+ s.authors = ["Mario Visic"]
9
+ s.email = ["mario@mariovisic.com"]
10
+ s.homepage = "https://github.com/mariovisic/has_face"
11
+ s.summary = "Easily validate if an image contains faces"
12
+ s.description = "An Active Model validator that uses the face.com API to ensures an image contains a face"
13
+
14
+ s.rubyforge_project = "has_face"
15
+
16
+ s.add_dependency 'rails', '>= 3.0'
17
+ s.add_dependency 'rest-client'
18
+
19
+ s.add_development_dependency 'rspec', '>= 2.0'
20
+ s.add_development_dependency 'rr', '>= 1.0'
21
+ s.add_development_dependency 'vcr', '>= 1.0'
22
+ s.add_development_dependency 'fakeweb', '>= 1.0'
23
+
24
+ s.files = `git ls-files`.split("\n")
25
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
26
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
27
+ s.require_paths = ["lib"]
28
+ end
@@ -0,0 +1,15 @@
1
+ module HasFace
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ desc "Copy HasFace default files"
5
+ source_root File.expand_path('../templates', __FILE__)
6
+ class_option :template_engine
7
+
8
+ def copy_initializers
9
+ copy_file 'has_face.rb', 'config/initializers/has_face.rb'
10
+ end
11
+
12
+ end
13
+ end
14
+ end
15
+
@@ -0,0 +1,5 @@
1
+ HasFace.configure do |config|
2
+ config.api_key = 'your face.com API key'
3
+ config.api_secret = 'your face.com API secret'
4
+ config.skip_validation_on_error = false
5
+ end
@@ -0,0 +1,17 @@
1
+ module HasFace
2
+ class Configuration
3
+
4
+ @enable_validation = true
5
+ @skip_validation_on_error = false
6
+ @detect_url = "http://api.face.com/faces/detect.json"
7
+
8
+ class << self
9
+ attr_accessor :enable_validation
10
+ attr_accessor :detect_url
11
+ attr_accessor :api_key
12
+ attr_accessor :api_secret
13
+ attr_accessor :skip_validation_on_error
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,5 @@
1
+ en:
2
+ activerecord:
3
+ errors:
4
+ messages:
5
+ no_face: "We couldn't see a face in your photo, try taking another one."
@@ -0,0 +1,56 @@
1
+ module HasFace
2
+ module Test
3
+ module Matchers
4
+
5
+ class ValidateHasFaceFor
6
+
7
+ def initialize(expected, options)
8
+ @expected = expected
9
+ @options = options
10
+ end
11
+
12
+ def matches?(actual)
13
+ @actual = actual
14
+
15
+ @actual.class.validators.any? do |validator|
16
+ validator.is_a?(HasFace::Validator) && \
17
+ validator.attributes.include?(@expected) && \
18
+ has_correct_allow_blank(validator) && \
19
+ has_correct_allow_nil(validator)
20
+ end
21
+
22
+ end
23
+
24
+ def failure_message
25
+ "expected #{@actual.class.to_s} to validate has face for #{@expected.inspect} #{with_options_message}"
26
+ end
27
+
28
+ def negative_failure_message
29
+ "expected #{@actual.class.to_s} not to validate has face for #{@expected.inspect} #{with_options_message}"
30
+ end
31
+
32
+ def has_correct_allow_blank(validator)
33
+ return true if @options[:allow_blank].nil?
34
+ validator.instance_variable_get('@allow_blank') == @options[:allow_blank]
35
+ end
36
+
37
+ def has_correct_allow_nil(validator)
38
+ return true if @options[:allow_nil].nil?
39
+ validator.instance_variable_get('@allow_nil') == @options[:allow_nil]
40
+ end
41
+
42
+ def with_options_message
43
+ if @options.present?
44
+ "with options #{@options.inspect}"
45
+ end
46
+ end
47
+
48
+ end
49
+
50
+ def validate_has_face_for(expected, options = {})
51
+ ValidateHasFaceFor.new(expected, options)
52
+ end
53
+
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,74 @@
1
+ require 'rest_client'
2
+
3
+ module HasFace
4
+ class Validator < ActiveModel::EachValidator
5
+
6
+ def initialize(options)
7
+ @allow_nil, @allow_blank = options.delete(:allow_nil), options.delete(:allow_blank)
8
+ super
9
+ end
10
+
11
+ def validate_each(record, attr_name, value)
12
+
13
+ # Skip validation if globally turned off
14
+ return if HasFace.enable_validation == false
15
+
16
+ image = record.send(attr_name)
17
+ image_path = image.try(:path) if image.respond_to?(:path)
18
+
19
+ # Skip validation if our image is nil/blank and allow nil/blank is on
20
+ return if (@allow_nil && image.nil?) || (@allow_blank && image.blank?)
21
+
22
+ # Add an error if the url is blank
23
+ return record.errors.add(attr_name, :no_face) if image_path.blank?
24
+
25
+ # Get an parse the JSON response
26
+ params = { :api_key => HasFace.api_key, :api_secret => HasFace.api_secret, :image => File.new(image_path, 'rb') }
27
+
28
+ begin
29
+ response = RestClient.post(HasFace.detect_url, params)
30
+ rescue RestClient::Exception => error
31
+ return handle_request_error(error)
32
+ end
33
+
34
+ json_response = JSON.parse(response.body)
35
+
36
+ # Error handling for failed responses
37
+ return handle_api_error(json_response) unless json_response['status'] == 'success'
38
+
39
+
40
+ # Add errors if no tags are present
41
+ tags = json_response.try(:[], 'photos').try(:first).try(:[], 'tags') || []
42
+ record.errors.add(attr_name, :no_face) if tags.blank?
43
+
44
+ end
45
+
46
+ protected
47
+
48
+ def handle_api_error(response)
49
+ error_message = "face.com API Error: \"#{response['error_message']}\" Code: #{response['error_code']}"
50
+
51
+ if HasFace.skip_validation_on_error
52
+ Rails.logger.warn error_message if Rails.logger.present?
53
+ true
54
+ else
55
+ raise FaceAPIError.new error_message
56
+ end
57
+ end
58
+
59
+ def handle_request_error(error)
60
+ error_message = "has_face HTTP Request Error: \"#{error.message}\""
61
+
62
+ if HasFace.skip_validation_on_error
63
+ Rails.logger.warn error_message if Rails.logger.present?
64
+ true
65
+ else
66
+ raise HTTPRequestError.new error_message
67
+ end
68
+ end
69
+
70
+ end
71
+ end
72
+
73
+ # Compatibility with ActiveModel validates method which matches option keys to their validator class
74
+ ActiveModel::Validations::HasFaceValidator = HasFace::Validator
@@ -0,0 +1,3 @@
1
+ module HasFace
2
+ VERSION = "0.0.1"
3
+ end
data/lib/has_face.rb ADDED
@@ -0,0 +1,30 @@
1
+ require 'has_face/configuration'
2
+ require 'has_face/validator'
3
+ require 'has_face/version'
4
+ require 'has_face/test/matchers'
5
+
6
+ module HasFace
7
+
8
+ # Error classes
9
+ class FaceAPIError < StandardError; end
10
+ class HTTPRequestError < StandardError; end
11
+
12
+ # Add load paths straight to I18n, so engines and application can overwrite it.
13
+ require 'i18n'
14
+ I18n.load_path << File.expand_path('../has_face/locales/en.yml', __FILE__)
15
+
16
+ class << self
17
+
18
+ configs = [ :api_key, :api_secret, :enable_validation, :detect_url, :skip_validation_on_error ]
19
+
20
+ configs.each do |config|
21
+ delegate config, "#{config}=", :to => HasFace::Configuration
22
+ end
23
+
24
+ def configure
25
+ yield self
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ describe HasFace::Test::Matchers do
4
+
5
+ context 'a user without face valdiation' do
6
+
7
+ subject { UserWithoutFaceValidation.new }
8
+
9
+ it { should_not validate_has_face_for :avatar }
10
+
11
+ end
12
+
13
+ context 'a user with face validation' do
14
+
15
+ subject { UserWithFaceValidation.new }
16
+
17
+ it { should validate_has_face_for :avatar }
18
+
19
+ end
20
+
21
+ context 'a user with face validation and allow blank' do
22
+
23
+ subject { UserWithAllowBlank.new }
24
+
25
+ it { should validate_has_face_for :avatar, :allow_blank => true }
26
+ it { should_not validate_has_face_for :avatar, :allow_blank => false }
27
+
28
+ end
29
+
30
+ context 'a user with face validation and allow nil' do
31
+
32
+ subject { UserWithAllowNil.new }
33
+
34
+ it { should validate_has_face_for :avatar, :allow_nil => true }
35
+ it { should_not validate_has_face_for :avatar, :allow_nil => false }
36
+
37
+ end
38
+
39
+ end
@@ -0,0 +1,211 @@
1
+ require 'spec_helper'
2
+
3
+ describe HasFace::Validator do
4
+
5
+ let(:user) { User.new(:avatar => avatar) }
6
+ let(:avatar) { Avatar.new }
7
+
8
+ context 'when validation is globally turned on' do
9
+
10
+ context 'when the image is a valid face' do
11
+
12
+ before :each do
13
+ avatar.path = VALID_IMAGE_PATH
14
+ end
15
+
16
+ it 'should be valid' do
17
+ VCR.use_cassette('valid image') do
18
+ user.should be_valid
19
+ end
20
+ end
21
+
22
+ end
23
+
24
+ context 'when the image is not a valid face' do
25
+
26
+ before :each do
27
+ avatar.path = INVALID_IMAGE_PATH
28
+ end
29
+
30
+ it 'should not be valid' do
31
+ VCR.use_cassette('invalid image') do
32
+ user.should_not be_valid
33
+ end
34
+ end
35
+
36
+ it 'should have an error on the image field' do
37
+ VCR.use_cassette('invalid image') do
38
+ user.valid?
39
+ user.errors[:avatar].should == [ "We couldn't see a face in your photo, try taking another one." ]
40
+ end
41
+ end
42
+
43
+ end
44
+
45
+ end
46
+
47
+ context 'when face validation is globally turned off' do
48
+
49
+ before :each do
50
+ stub(HasFace).enable_validation { false }
51
+ avatar.path = INVALID_IMAGE_PATH
52
+ end
53
+
54
+ it 'should be valid' do
55
+ user.should be_valid
56
+ end
57
+
58
+ end
59
+
60
+ context 'handling a failure response from the face.com API' do
61
+
62
+ let(:logger) { Logger.new(nil) }
63
+
64
+ before :each do
65
+ stub(HasFace).api_key { 'invalid api key' }
66
+ stub(Rails).logger { logger }
67
+ avatar.path = INVALID_IMAGE_PATH
68
+ end
69
+
70
+ context 'when skipping validation on errors is enabled' do
71
+
72
+ before :each do
73
+ stub(HasFace).skip_validation_on_error { true }
74
+ avatar.path = INVALID_IMAGE_PATH
75
+ end
76
+
77
+ it 'should skip validation' do
78
+ VCR.use_cassette('invalid api key') do
79
+ user.should be_valid
80
+ end
81
+ end
82
+
83
+ it 'should log a warning' do
84
+ VCR.use_cassette('invalid api key') do
85
+ mock(logger).warn 'face.com API Error: "API_KEY_DOES_NOT_EXIST - invalid api key" Code: 201'
86
+ user.valid?
87
+ end
88
+ end
89
+
90
+ end
91
+
92
+ context 'when skipping validation on errors is disabled' do
93
+
94
+ it 'should raise an api error' do
95
+ VCR.use_cassette('invalid api key') do
96
+ expect { user.valid? }.to raise_error HasFace::FaceAPIError, 'face.com API Error: "API_KEY_DOES_NOT_EXIST - invalid api key" Code: 201'
97
+ end
98
+ end
99
+
100
+ end
101
+
102
+ end
103
+
104
+ context 'handling http request errors' do
105
+
106
+ let(:logger) { Logger.new(nil) }
107
+
108
+ before :each do
109
+ stub(HasFace).detect_url { 'http://face.com/invalid/lookup/url' }
110
+ stub(Rails).logger { logger }
111
+ avatar.path = INVALID_IMAGE_PATH
112
+ end
113
+
114
+ context 'when skipping validation on errors is enabled' do
115
+
116
+ before :each do
117
+ stub(HasFace).skip_validation_on_error { true }
118
+ end
119
+
120
+ it 'should skip validation' do
121
+ VCR.use_cassette('invalid detect url') do
122
+ user.should be_valid
123
+ end
124
+ end
125
+
126
+ it 'should log a warning' do
127
+ VCR.use_cassette('invalid detect url') do
128
+ mock(logger).warn 'has_face HTTP Request Error: "404 Resource Not Found"'
129
+ user.valid?
130
+ end
131
+ end
132
+
133
+ end
134
+
135
+ context 'when skipping validation on errors is disabled' do
136
+
137
+ it 'should raise an api error' do
138
+ VCR.use_cassette('invalid detect url') do
139
+ expect { user.valid? }.to raise_error HasFace::HTTPRequestError, 'has_face HTTP Request Error: "404 Resource Not Found"'
140
+ end
141
+ end
142
+
143
+ end
144
+
145
+ end
146
+
147
+ context 'allowing blank' do
148
+
149
+ context 'when allow blank is true' do
150
+
151
+ let(:user) { UserWithAllowBlank.new }
152
+
153
+ before :each do
154
+ stub(user).avatar { "" }
155
+ end
156
+
157
+ it 'should be valid with a blank avatar' do
158
+ user.should be_valid
159
+ end
160
+
161
+ end
162
+
163
+ context 'when allow blank is not true' do
164
+
165
+ let(:user) { UserWithoutAllowBlank.new }
166
+
167
+ before :each do
168
+ stub(user).avatar { "" }
169
+ end
170
+
171
+ it 'should not be valid' do
172
+ user.should_not be_valid
173
+ end
174
+
175
+ end
176
+
177
+ end
178
+
179
+ context 'allowing nil' do
180
+
181
+ context 'when allow nil is true' do
182
+
183
+ let(:user) { UserWithAllowNil.new }
184
+
185
+ before :each do
186
+ stub(user).avatar { nil }
187
+ end
188
+
189
+ it 'should be valid with a nil avatar' do
190
+ user.should be_valid
191
+ end
192
+
193
+ end
194
+
195
+ context 'when allow nil is not true' do
196
+
197
+ let(:user) { UserWithoutAllowNil.new }
198
+
199
+ before :each do
200
+ stub(user).avatar { nil }
201
+ end
202
+
203
+ it 'should not be valid' do
204
+ user.should_not be_valid
205
+ end
206
+
207
+ end
208
+
209
+ end
210
+
211
+ end
@@ -0,0 +1,32 @@
1
+ require 'rspec'
2
+ require 'vcr'
3
+
4
+ require 'rails'
5
+ require 'active_model'
6
+ require 'active_model/validations'
7
+ require 'active_record'
8
+ require 'lib/has_face'
9
+
10
+ Dir["spec/support/**/*.rb"].each {|f| require f}
11
+
12
+ VALID_IMAGE_PATH = 'spec/support/assets/hit.jpg'
13
+ INVALID_IMAGE_PATH = 'spec/support/assets/miss.jpg'
14
+
15
+ VCR.config do |c|
16
+ c.cassette_library_dir = 'spec/support/vcr_cassettes'
17
+ c.stub_with :fakeweb
18
+ c.default_cassette_options = { :record => :once }
19
+ end
20
+
21
+ RSpec.configure do |config|
22
+
23
+ config.mock_with :rr
24
+ config.include HasFace::Test::Matchers
25
+
26
+ config.before :all do
27
+ # Put your api details here for tesing. This is only required if VCR is not being used
28
+ # HasFace.api_key = 'api_key' if HasFace.api_key.blank?
29
+ # HasFace.api_secret = 'api_secret' if HasFace.api_secret.blank?
30
+ end
31
+
32
+ end
Binary file
Binary file
@@ -0,0 +1,5 @@
1
+ class Avatar < ActiveRecord::Base
2
+
3
+ belongs_to :user
4
+
5
+ end
@@ -0,0 +1,7 @@
1
+ class BaseUser < ActiveRecord::Base
2
+
3
+ has_one :avatar
4
+
5
+ end
6
+
7
+ class UserWithoutFaceValidation < BaseUser; end
@@ -0,0 +1,9 @@
1
+ class User < BaseUser
2
+
3
+ validates :avatar, :has_face => true
4
+
5
+ end
6
+
7
+ class UserWithFaceValidation < User; end
8
+ class UserWithoutAllowBlank < User; end
9
+ class UserWithoutAllowNil < User; end
@@ -0,0 +1,3 @@
1
+ class UserWithAllowBlank < BaseUser
2
+ validates :avatar, :has_face => true, :allow_blank => true
3
+ end
@@ -0,0 +1,3 @@
1
+ class UserWithAllowNil < BaseUser
2
+ validates :avatar, :has_face => true, :allow_nil => true
3
+ end
@@ -0,0 +1,17 @@
1
+ ActiveRecord::Base.establish_connection({:adapter => 'sqlite3', :database => ':memory:'})
2
+ ActiveRecord::Migration.verbose = false
3
+
4
+ ActiveRecord::Schema.define(:version => 1) do
5
+
6
+ create_table :avatars, :force => true do |t|
7
+ t.string :url
8
+ t.string :path
9
+ t.integer :user_id
10
+ end
11
+
12
+ create_table :base_users, :force => true do |t|
13
+ t.string :full_name
14
+ end
15
+
16
+ end
17
+
@@ -0,0 +1,38 @@
1
+ ---
2
+ - !ruby/struct:VCR::HTTPInteraction
3
+ request: !ruby/struct:VCR::Request
4
+ method: :post
5
+ uri: http://api.face.com:80/faces/detect.json
6
+ body:
7
+ headers:
8
+ accept:
9
+ - "*/*; q=0.5, application/xml"
10
+ content-type:
11
+ - multipart/form-data; boundary=666980
12
+ accept-encoding:
13
+ - gzip, deflate
14
+ content-length:
15
+ - "69159"
16
+ response: !ruby/struct:VCR::Response
17
+ status: !ruby/struct:VCR::ResponseStatus
18
+ code: 200
19
+ message: OK
20
+ headers:
21
+ content-type:
22
+ - "application/json; Charset: utf-8"
23
+ server:
24
+ - Apache/2.2.16 (Debian) mod_ssl/2.2.16 OpenSSL/0.9.8o
25
+ date:
26
+ - Sun, 10 Jul 2011 14:43:22 GMT
27
+ content-length:
28
+ - "115"
29
+ content-encoding:
30
+ - gzip
31
+ vary:
32
+ - Accept-Encoding
33
+ body: !binary |
34
+ H4sIAAAAAAAAA6pWKi5JLCktVrJSSkvMzCktSlXSUUotKsovik/OT0lVsjIy
35
+ MIQJ5KYWFyemA8WUHAM8471dI+Nd/F2D4/38Q+JdIzyDQxR0FTLzyhJzMlMU
36
+ EgsyFbJTK5VqAQAAAP//AwApem9UYAAAAA==
37
+
38
+ http_version: "1.1"
@@ -0,0 +1,41 @@
1
+ ---
2
+ - !ruby/struct:VCR::HTTPInteraction
3
+ request: !ruby/struct:VCR::Request
4
+ method: :post
5
+ uri: http://face.com:80/invalid/lookup/url
6
+ body:
7
+ headers:
8
+ accept:
9
+ - "*/*; q=0.5, application/xml"
10
+ content-type:
11
+ - multipart/form-data; boundary=104496
12
+ accept-encoding:
13
+ - gzip, deflate
14
+ content-length:
15
+ - "69176"
16
+ response: !ruby/struct:VCR::Response
17
+ status: !ruby/struct:VCR::ResponseStatus
18
+ code: 404
19
+ message: Not Found
20
+ headers:
21
+ content-type:
22
+ - text/html; charset=iso-8859-1
23
+ server:
24
+ - Apache/2.2.16 (Debian) mod_ssl/2.2.16 OpenSSL/0.9.8o
25
+ date:
26
+ - Sun, 10 Jul 2011 14:43:50 GMT
27
+ content-length:
28
+ - "261"
29
+ content-encoding:
30
+ - gzip
31
+ vary:
32
+ - Accept-Encoding
33
+ body: !binary |
34
+ H4sIAAAAAAAAA01QwWrDMAy95yu0nrbDrDSU0YExbE3KClkblvSw03BjjYQ5
35
+ cWY7Hfv7OSmFXQTvSe9JT/wmPWyq9yKDl+o1h+L4nO82sLhH3GXVFjGt0ksn
36
+ YTFitl+IiDe+04I3JFUAvvWaxCpewd542JqxVxwvZMRxHuIno34n3VL8mwko
37
+ 4oOoGgJL3yM5TwqObzlg25+lbhVqY77GAUer4Uc66IP2c9KC6cE3rQNH9kyW
38
+ cRwmdxuKVMqSc+JpkHVDmLCELR/gNqVTK/s76Iz6cE5f+cNAfVnmGLNHtjZQ
39
+ znYgwxpZE6tNB4WxHtYxx6txyDSnCfdPX4j+AIuMg79AAQAA
40
+
41
+ http_version: "1.1"
@@ -0,0 +1,42 @@
1
+ ---
2
+ - !ruby/struct:VCR::HTTPInteraction
3
+ request: !ruby/struct:VCR::Request
4
+ method: :post
5
+ uri: http://api.face.com:80/faces/detect.json
6
+ body:
7
+ headers:
8
+ accept:
9
+ - "*/*; q=0.5, application/xml"
10
+ content-type:
11
+ - multipart/form-data; boundary=976464
12
+ accept-encoding:
13
+ - gzip, deflate
14
+ content-length:
15
+ - "69176"
16
+ response: !ruby/struct:VCR::Response
17
+ status: !ruby/struct:VCR::ResponseStatus
18
+ code: 200
19
+ message: OK
20
+ headers:
21
+ content-type:
22
+ - "application/json; Charset: utf-8"
23
+ server:
24
+ - Apache/2.2.16 (Debian) mod_ssl/2.2.16 OpenSSL/0.9.8o
25
+ date:
26
+ - Sun, 10 Jul 2011 14:40:56 GMT
27
+ content-length:
28
+ - "298"
29
+ content-encoding:
30
+ - gzip
31
+ vary:
32
+ - Accept-Encoding
33
+ body: !binary |
34
+ H4sIAAAAAAAAA0yPMW/DIBCF/4p1a93A4YANU6cOXTuWKML4sIns2ApYrRTl
35
+ v5cMlbrdO733vbs7bNOa1wTm6w77bQYDU86bscyy4Dwd/LpY5rb4um/z6oZk
36
+ WWeZsMxbporlbzgWk2Xasl5ybEPrnRCkO98o2ZLjh8tGI9SwxaE0vL+pTkgc
37
+ nHKofB8a0QsZjtj2FDoiFZqzQKU8celJD10I3MtGe8fdQEEWqi6s7zjkCcxR
38
+ 8homiuOUwSheRHbj85/T41RDyi7vRUHavaeUSm5PbiQw5d1E5Rqs4UaLi9d4
39
+ HQtNa13DHJdYaJI/cTdKlM85LnTO9FPW8Llf6wp59bHPleCIFUrTcINt9VIS
40
+ HP5nSkGDvEHssH08fgEAAP//AwD3K/WfbwEAAA==
41
+
42
+ http_version: "1.1"
@@ -0,0 +1,49 @@
1
+ ---
2
+ - !ruby/struct:VCR::HTTPInteraction
3
+ request: !ruby/struct:VCR::Request
4
+ method: :post
5
+ uri: http://api.face.com:80/faces/detect.json
6
+ body:
7
+ headers:
8
+ accept:
9
+ - "*/*; q=0.5, application/xml"
10
+ content-type:
11
+ - multipart/form-data; boundary=591191
12
+ accept-encoding:
13
+ - gzip, deflate
14
+ content-length:
15
+ - "78189"
16
+ response: !ruby/struct:VCR::Response
17
+ status: !ruby/struct:VCR::ResponseStatus
18
+ code: 200
19
+ message: OK
20
+ headers:
21
+ content-type:
22
+ - "application/json; Charset: utf-8"
23
+ server:
24
+ - Apache/2.2.16 (Debian) mod_ssl/2.2.16 OpenSSL/0.9.8o
25
+ date:
26
+ - Sun, 10 Jul 2011 14:40:50 GMT
27
+ content-length:
28
+ - "608"
29
+ content-encoding:
30
+ - gzip
31
+ vary:
32
+ - Accept-Encoding
33
+ body: !binary |
34
+ H4sIAAAAAAAAA6xTPY/cIBD9Kyva7GEwBn9UaZIiUqRISRefLBZjmwjbKwO5
35
+ u5z2v2ew13vaa9KkMsy8eW/eMH5F52H2s0PVz1cUFosqNHh/ruqkTjqpNFbz
36
+ WCfybB7C2c6ydXVS1EleJ7ROGCTqJFsjYj9nJG1FrrJSdCpvVSuEYqLEv866
37
+ R0d0Ni0ofP5Y8EyddNnmqcxPMqPtSVJxSqmCIi151qQUCjXhCkBF1xHFWakk
38
+ ka3ueKFYCVxPpvUDqkRGjmjQph88qrICLl72mx+/qv349PVb8x8kG15gnjZZ
39
+ holoSEOghUWruZ/MH3myGlV+CRrUh0W7YbYgPQVrjyiYNrbzeES9uQWtPOk4
40
+ bCBR89SZZdSQ66R1QDHKKUh7u4KfXi/NW/HVeSowKd+8swzncFV68npB1St6
41
+ RtXa8hG9wGRi25cj0i+6sbrzOwCKihXABKb8Clg2xhUhOOZiQ3CcpYAY5+CH
42
+ OxKB2UbCU5zfEHediBSzdMdwfgPdSYldilNcMsBMs9N7kmHBNisMF7FRuVyb
43
+ 2KYS71e2LaAGM+3nF/kEBnIMDMtsYbYPKS7KuJBewSiL1YD0fjGn4LWLmnH9
44
+ 4/e3tAEOKD7v/lytnmKyiH30emo3mztylPY9Mo+Oeyud28h36PrE77CcANaN
45
+ xpqp/xc2o5fL5fECy+W89AG4kQtKaecAGJzsVwvBxe2icWFHaaaVNytL8G/N
46
+ aHyUJCRmnfaNN6NuvH6GMPoepuOBksOXYA8pofRAecVIRfPDB6jYfoG9BgQY
47
+ JYzSguaXy18AAAD//wMArlodeVkEAAA=
48
+
49
+ http_version: "1.1"
metadata ADDED
@@ -0,0 +1,198 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: has_face
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Mario Visic
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-07-11 00:00:00 +08:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rails
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 7
30
+ segments:
31
+ - 3
32
+ - 0
33
+ version: "3.0"
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: rest-client
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 3
45
+ segments:
46
+ - 0
47
+ version: "0"
48
+ type: :runtime
49
+ version_requirements: *id002
50
+ - !ruby/object:Gem::Dependency
51
+ name: rspec
52
+ prerelease: false
53
+ requirement: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ hash: 3
59
+ segments:
60
+ - 2
61
+ - 0
62
+ version: "2.0"
63
+ type: :development
64
+ version_requirements: *id003
65
+ - !ruby/object:Gem::Dependency
66
+ name: rr
67
+ prerelease: false
68
+ requirement: &id004 !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ hash: 15
74
+ segments:
75
+ - 1
76
+ - 0
77
+ version: "1.0"
78
+ type: :development
79
+ version_requirements: *id004
80
+ - !ruby/object:Gem::Dependency
81
+ name: vcr
82
+ prerelease: false
83
+ requirement: &id005 !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ hash: 15
89
+ segments:
90
+ - 1
91
+ - 0
92
+ version: "1.0"
93
+ type: :development
94
+ version_requirements: *id005
95
+ - !ruby/object:Gem::Dependency
96
+ name: fakeweb
97
+ prerelease: false
98
+ requirement: &id006 !ruby/object:Gem::Requirement
99
+ none: false
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ hash: 15
104
+ segments:
105
+ - 1
106
+ - 0
107
+ version: "1.0"
108
+ type: :development
109
+ version_requirements: *id006
110
+ description: An Active Model validator that uses the face.com API to ensures an image contains a face
111
+ email:
112
+ - mario@mariovisic.com
113
+ executables: []
114
+
115
+ extensions: []
116
+
117
+ extra_rdoc_files: []
118
+
119
+ files:
120
+ - .gitignore
121
+ - .rvmrc
122
+ - Gemfile
123
+ - README.md
124
+ - Rakefile
125
+ - has_face.gemspec
126
+ - lib/generators/has_face/install_generator.rb
127
+ - lib/generators/has_face/templates/has_face.rb
128
+ - lib/has_face.rb
129
+ - lib/has_face/configuration.rb
130
+ - lib/has_face/locales/en.yml
131
+ - lib/has_face/test/matchers.rb
132
+ - lib/has_face/validator.rb
133
+ - lib/has_face/version.rb
134
+ - spec/has_face/test/matchers_spec.rb
135
+ - spec/has_face/validator_spec.rb
136
+ - spec/spec_helper.rb
137
+ - spec/support/assets/hit.jpg
138
+ - spec/support/assets/miss.jpg
139
+ - spec/support/models/avatar.rb
140
+ - spec/support/models/base_user.rb
141
+ - spec/support/models/user.rb
142
+ - spec/support/models/user_with_allow_blank.rb
143
+ - spec/support/models/user_with_allow_nil.rb
144
+ - spec/support/schema_setup.rb
145
+ - spec/support/vcr_cassettes/invalid_api_key.yml
146
+ - spec/support/vcr_cassettes/invalid_detect_url.yml
147
+ - spec/support/vcr_cassettes/invalid_image.yml
148
+ - spec/support/vcr_cassettes/valid_image.yml
149
+ has_rdoc: true
150
+ homepage: https://github.com/mariovisic/has_face
151
+ licenses: []
152
+
153
+ post_install_message:
154
+ rdoc_options: []
155
+
156
+ require_paths:
157
+ - lib
158
+ required_ruby_version: !ruby/object:Gem::Requirement
159
+ none: false
160
+ requirements:
161
+ - - ">="
162
+ - !ruby/object:Gem::Version
163
+ hash: 3
164
+ segments:
165
+ - 0
166
+ version: "0"
167
+ required_rubygems_version: !ruby/object:Gem::Requirement
168
+ none: false
169
+ requirements:
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ hash: 3
173
+ segments:
174
+ - 0
175
+ version: "0"
176
+ requirements: []
177
+
178
+ rubyforge_project: has_face
179
+ rubygems_version: 1.6.2
180
+ signing_key:
181
+ specification_version: 3
182
+ summary: Easily validate if an image contains faces
183
+ test_files:
184
+ - spec/has_face/test/matchers_spec.rb
185
+ - spec/has_face/validator_spec.rb
186
+ - spec/spec_helper.rb
187
+ - spec/support/assets/hit.jpg
188
+ - spec/support/assets/miss.jpg
189
+ - spec/support/models/avatar.rb
190
+ - spec/support/models/base_user.rb
191
+ - spec/support/models/user.rb
192
+ - spec/support/models/user_with_allow_blank.rb
193
+ - spec/support/models/user_with_allow_nil.rb
194
+ - spec/support/schema_setup.rb
195
+ - spec/support/vcr_cassettes/invalid_api_key.yml
196
+ - spec/support/vcr_cassettes/invalid_detect_url.yml
197
+ - spec/support/vcr_cassettes/invalid_image.yml
198
+ - spec/support/vcr_cassettes/valid_image.yml