has_face 0.0.1

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,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