Skip to content

Commit

Permalink
Middleware
Browse files Browse the repository at this point in the history
  • Loading branch information
bcardarella committed Mar 5, 2011
1 parent b73ba75 commit 8e16657
Show file tree
Hide file tree
Showing 10 changed files with 223 additions and 7 deletions.
4 changes: 1 addition & 3 deletions client_side_validations.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@ Gem::Specification.new do |s|

s.add_dependency 'activesupport', '~> 3.0.0'

s.add_development_dependency 'activemodel', '~> 3.0.3'
s.add_development_dependency 'activerecord', '~> 3.0.3'
s.add_development_dependency 'actionpack', '~> 3.0.3'
s.add_development_dependency 'rails', '~> 3.0.0'
s.add_development_dependency 'sqlite3'
s.add_development_dependency 'bson_ext'
s.add_development_dependency 'mongoid', '~> 2.0.0.rc.6'
Expand Down
6 changes: 3 additions & 3 deletions lib/client_side_validations.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module ClientSideValidations
end

require 'client_side_validations/active_record' if defined?(ActiveRecord)
require 'client_side_validations/mongoid' if defined?(Mongoid)
require 'client_side_validations/active_record' if defined?(::ActiveRecord)
require 'client_side_validations/mongoid' if defined?(::Mongoid)
require 'client_side_validations/action_view'

require 'client_side_validations/middleware' if defined?(::Rails)
1 change: 1 addition & 0 deletions lib/client_side_validations/active_record.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'client_side_validations/active_model'
require 'client_side_validations/active_record/middleware'

%w{uniqueness}.each do |validator|
require "client_side_validations/active_record/#{validator}"
Expand Down
23 changes: 23 additions & 0 deletions lib/client_side_validations/active_record/middleware.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module ClientSideValidations::ActiveRecord
class Middleware
def self.is_unique?(klass, resource, params)
attribute = params[resource].keys.first
value = params[resource][attribute]
t = klass.arel_table

if params[:case_sensitive] == 'true'
relation = t[attribute].eq(value)
else
relation = t[attribute].matches(value)
end

relation = relation.and(t[:id].not_eq(params[:id])) if params[:id]

(params[:scope] || {}).each do |key, value|
relation = relation.and(t[key].eq(value))
end

(!klass.where(relation).exists?).to_s
end
end
end
38 changes: 38 additions & 0 deletions lib/client_side_validations/middleware.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
module ClientSideValidations

class Middleware
def initialize(app)
@app = app
end

def call(env)
case env['PATH_INFO']
when '/validators/uniqueness.json'
request = ActionDispatch::Request.new(env)
unique = is_unique?(request.params)
[200, {'Content-Type' => 'application/json', 'Content-Length' => "#{unique.length}"}, [unique]]
else
@app.call(env)
end
end

private

def is_unique?(params)
resource = params.keys.first
klass = resource.classify.constantize

if (defined?(::ActiveRecord::Base) && klass.superclass == ::ActiveRecord::Base)
ClientSideValidations::ActiveRecord::Middleware.is_unique?(klass, resource, params)
elsif (defined?(::Mongoid::Document) && klass.included_modules.include?(::Mongoid::Document))
ClientSideValidations::Mongoid::Middleware.is_unique?(klass, resource, params)
end
end
end

class Engine < ::Rails::Engine
config.app_middleware.use ClientSideValidations::Middleware
end

end

1 change: 1 addition & 0 deletions lib/client_side_validations/mongoid.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'client_side_validations/active_model'
require 'client_side_validations/mongoid/middleware'

%w{uniqueness}.each do |validator|
require "client_side_validations/mongoid/#{validator}"
Expand Down
23 changes: 23 additions & 0 deletions lib/client_side_validations/mongoid/middleware.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module ClientSideValidations::Mongoid
class Middleware

# Still need to handle embedded documents
def self.is_unique?(klass, resource, params)
attribute = params[resource].keys.first
value = params[resource][attribute]

if params[:case_sensitive] == 'false'
value = Regexp.new("^#{Regexp.escape(value.to_s)}$", Regexp::IGNORECASE)
end

criteria = klass.where(attribute => value)
criteria = criteria.where(:_id => {'$ne' => params[:id]}) if params[:id]

(params[:scope] || {}).each do |key, value|
criteria = criteria.where(key => value)
end

(!criteria.exists?).to_s
end
end
end
2 changes: 1 addition & 1 deletion test/active_record/models/user.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
users_table = %{CREATE TABLE users (id INTEGER PRIMARY KEY, age INTEGER, name TEXT);}
users_table = %{CREATE TABLE users (id INTEGER PRIMARY KEY, age INTEGER, name TEXT, email TEXT);}
ActiveRecord::Base.connection.execute(users_table)

class User < ActiveRecord::Base
Expand Down
130 changes: 130 additions & 0 deletions test/middleware/cases/test_middleware.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
require 'rails'

# Pulled from railties/test/abstract_unit in Rails 3.1
module TestApp
class Application < Rails::Application
config.root = File.dirname(__FILE__)
config.active_support.deprecation = :log
config.logger = Logger.new(STDOUT)
end
end

require 'active_record/models/user'
require 'client_side_validations/middleware'

TestApp::Application.initialize!

class ClientSideValidationsMiddleWareTest < Test::Unit::TestCase
def test_if_middleware_is_auto_included
assert Rails.configuration.middleware.include?(ClientSideValidations::Middleware)
end
end

class ClientSideValidationsActiveRecordMiddlewareTest < Test::Unit::TestCase
include Rack::Test::Methods

def teardown
User.delete_all
end

def app
app = Proc.new { |env| [200, {}, ['success']] }
ClientSideValidations::Middleware.new(app)
end

def test_uniqueness_when_resource_exists
User.create(:email => 'user@test.com')
get '/validators/uniqueness.json', { 'user[email]' => 'user@test.com', 'case_sensitive' => true }

assert_equal 'false', last_response.body
end

def test_uniqueness_when_resource_does_not_exist
get '/validators/uniqueness.json', { 'user[email]' => 'user@test.com', 'case_sensitive' => true }

assert_equal 'true', last_response.body
end

def test_uniqueness_when_id_is_given
user = User.create(:email => 'user@test.com')
get '/validators/uniqueness.json', { 'user[email]' => 'user@test.com', 'id' => user.id, 'case_sensitive' => true }

assert_equal 'true', last_response.body
end

def test_uniqueness_when_scope_is_given
User.create(:email => 'user@test.com', :age => 25)
get '/validators/uniqueness.json', { 'user[email]' => 'user@test.com', 'scope' => { 'age' => 30 }, 'case_sensitive' => true }

assert_equal 'true', last_response.body
end

def test_uniqueness_when_multiple_scopes_are_given
User.create(:email => 'user@test.com', :age => 30, :name => 'Brian')
get '/validators/uniqueness.json', { 'user[email]' => 'user@test.com', 'scope' => { 'age' => 30, 'name' => 'Robert' }, 'case_sensitive' => true }

assert_equal 'true', last_response.body
end

def test_uniqueness_when_case_insensitive
User.create(:name => 'Brian')
get '/validators/uniqueness.json', { 'user[name]' => 'BRIAN', 'case_sensitive' => false }

assert_equal 'false', last_response.body
end
end

class ClientSideValidationsMongoidMiddlewareTest < Test::Unit::TestCase
include Rack::Test::Methods

def teardown
Book.delete_all
end

def app
app = Proc.new { |env| [200, {}, ['success']] }
ClientSideValidations::Middleware.new(app)
end

def test_uniqueness_when_resource_exists
Book.create(:author_email => 'book@test.com')
get '/validators/uniqueness.json', { 'book[author_email]' => 'book@test.com' }

assert_equal 'false', last_response.body
end

def test_uniqueness_when_resource_does_not_exist
get '/validators/uniqueness.json', { 'book[author_email]' => 'book@test.com' }

assert_equal 'true', last_response.body
end

def test_uniqueness_when_id_is_given
book = Book.create(:author_email => 'book@test.com')
get '/validators/uniqueness.json', { 'book[author_email]' => 'book@test.com', 'id' => book.id }

assert_equal 'true', last_response.body
end

def test_uniqueness_when_scope_is_given
Book.create(:author_email => 'book@test.com', :age => 25)
get '/validators/uniqueness.json', { 'book[author_email]' => 'book@test.com', 'scope' => { 'age' => 30 } }

assert_equal 'true', last_response.body
end

def test_uniqueness_when_multiple_scopes_are_given
Book.create(:author_email => 'book@test.com', :age => 30, :author_name => 'Brian')
get '/validators/uniqueness.json', { 'book[author_email]' => 'book@test.com', 'scope' => { 'age' => 30, 'author_name' => 'Robert' } }

assert_equal 'true', last_response.body
end

def test_uniqueness_when_case_insensitive
Book.create(:author_name => 'Brian')
get '/validators/uniqueness.json', { 'book[author_name]' => 'BRIAN', 'case_sensitive' => false }

assert_equal 'false', last_response.body
end
end

2 changes: 2 additions & 0 deletions test/mongoid/models/book.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@ class Book
include Mongoid::Document

field :age, :type => Integer
field :author_name, :type => String
field :author_email, :type => String
end

0 comments on commit 8e16657

Please sign in to comment.