Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Added ability to override class name for uniqueness validator #520

Merged
merged 1 commit into from

4 participants

@arr-ee

Closes #519

Added :client_validations option to the validate_uniqueness_of,
where one can specify the real class name for the remote validation.

For example, we have a wrapper class for the User model, UserForm, and
it uses remote uniqueness validation for the email field.

class UserForm
  include ActiveRecord::Validations
  attr_accessor :email
  validates_uniqueness_of :email
end

However, this won't work since middleware will try to perform validation
against UserForm, and it's not persisted.

Now one can state which class should be used for validation:

class UserForm
  include ActiveRecord::Validations
  attr_accessor :email
  validates_uniqueness_of :email, :client_validations => { :class =>
  User }
end
@coveralls

Coverage remained the same when pulling 9bf286d on arr-ee:override_class_for_uniqueness_validator into b674e02 on bcardarella:3-2-stable.

View Details

README.md
((13 lines not shown))
+end
+...
+<% form_for(UserForm.new) do %>
+...
+```
+
+However, this won't work since middleware will try to perform validation against UserForm, and it's not persisted.
+
+This is solved by passing `client_validations` options hash to the validator, that currently supports one key — `:class`, and setting correct name to the form object:
+
+```ruby
+class UserForm
+ include ActiveRecord::Validations
+ attr_accessor :email
+ validates_uniqueness_of :email, :client_validations => { :class =>
+ User }
@bcardarella Owner

We should suggest the developer use a string version of the class. So 'User' instead of User. Because the validations occur on the class level we are dealing with load order issue for the code evaluation. For example, if the form class was AccountForm and it was referencing a User class the app will throw an error on production as the lazy loading of classes is only in development.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@bcardarella
Owner

This looks good! Merge away :+1:

@arr-ee arr-ee Added ability to override class name for uniqueness validator
Added `:client_validations` option to the `validate_uniqueness_of`,
where one can specify the real class name for the remote validation.

For example, we have a wrapper class for the User model, UserForm, and
it uses remote uniqueness validation for the email field.

```
class UserForm
  include ActiveRecord::Validations
  attr_accessor :email
  validates_uniqueness_of :email
end
```

However, this won't work since middleware will try to perform validation
against UserForm, and it's not persisted.

Now one can state which class should be used for validation:

```
class UserForm
  include ActiveRecord::Validations
  attr_accessor :email
  validates_uniqueness_of :email, :client_validations => { :class =>
  User }
end
```
b045df6
@coveralls

Coverage remained the same when pulling 5310341 on arr-ee:override_class_for_uniqueness_validator into b674e02 on bcardarella:3-2-stable.

View Details

@arr-ee arr-ee merged commit c84462f into DavyJonesLocker:3-2-stable
@coveralls

Coverage remained the same when pulling b045df6 on arr-ee:override_class_for_uniqueness_validator into b674e02 on bcardarella:3-2-stable.

View Details

@midu

Hi, I work with ilya (the guy who submitted the issue) and I think you guys deserve some hearts: :heart: :heart: :heart: :heart: :heart: :heart: :heart: :heart: :heart: :heart: :heart: :heart: :heart: :heart:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 2, 2013
  1. @arr-ee

    Added ability to override class name for uniqueness validator

    arr-ee authored
    Added `:client_validations` option to the `validate_uniqueness_of`,
    where one can specify the real class name for the remote validation.
    
    For example, we have a wrapper class for the User model, UserForm, and
    it uses remote uniqueness validation for the email field.
    
    ```
    class UserForm
      include ActiveRecord::Validations
      attr_accessor :email
      validates_uniqueness_of :email
    end
    ```
    
    However, this won't work since middleware will try to perform validation
    against UserForm, and it's not persisted.
    
    Now one can state which class should be used for validation:
    
    ```
    class UserForm
      include ActiveRecord::Validations
      attr_accessor :email
      validates_uniqueness_of :email, :client_validations => { :class =>
      User }
    end
    ```
This page is out of date. Refresh to see the latest.
View
41 README.md
@@ -102,7 +102,7 @@ that `<script>` tag elsewhere you can do this by passing a name to
<%= form_for @user, :validate => :user_validators do |f| %>
```
-The `<script`> tag is added to `content_for()` with the name you passed,
+The `<script`> tag is added to `content_for()` with the name you passed,
so you can simply render that anywhere you like:
```erb
@@ -165,6 +165,37 @@ You can even turn them off per fieldset:
...
```
+## Wrapper objects and remote validations ##
+
+For example, we have a wrapper class for the User model, UserForm, and it uses remote uniqueness validation for the `email` field.
+
+```ruby
+class UserForm
+ include ActiveRecord::Validations
+ attr_accessor :email
+ validates_uniqueness_of :email
+end
+...
+<% form_for(UserForm.new) do %>
+...
+```
+
+However, this won't work since middleware will try to perform validation against UserForm, and it's not persisted.
+
+This is solved by passing `client_validations` options hash to the validator, that currently supports one key — `:class`, and setting correct name to the form object:
+
+```ruby
+class UserForm
+ include ActiveRecord::Validations
+ attr_accessor :email
+ validates_uniqueness_of :email, :client_validations => { :class =>
+ 'User' }
+end
+...
+<% form_for(UserForm.new, :as => :user) do %>
+...
+```
+
## Understanding the embedded `<script>` tag ##
A rendered form with validations will always have a `<script>` appeneded
@@ -407,7 +438,7 @@ You can reset the current state of the validations, clear all error messages, an
```js
$(form).resetClientSideValidations();
-```
+```
## Callbacks ##
@@ -440,7 +471,7 @@ window.ClientSideValidations.callbacks.element.fail = function(element, message,
}
window.ClientSideValidations.callbacks.element.pass = function(element, callback) {
- // Take note how we're passing the callback to the hide()
+ // Take note how we're passing the callback to the hide()
// method so it is run after the animation is complete.
element.parent().find('.message').hide('slide', {direction: "left"}, 500, callback);
}
@@ -471,7 +502,7 @@ ClientSideValidations::Config.disabled_validators = [:uniqueness]
This will completely disable the uniqueness validator. The `FormBuilder`
will automatically skip building validators that are disabled.
-
+
## Authors ##
[Brian Cardarella](http://twitter.com/bcardarella)
@@ -484,7 +515,7 @@ This gem follows [Semantic Versioning](http://semver.org)
Major and minor version numbers will follow `Rails`'s major and
minor version numbers. For example,
-`client_side_validations-3.2.0` will be compatible up to
+`client_side_validations-3.2.0` will be compatible up to
`~> rails-3.2.0`
We will maintain compatibility with one minor version back. So the 3.2.0 version of
View
11 lib/client_side_validations/active_record/uniqueness.rb
@@ -6,16 +6,19 @@ def client_side_hash(model, attribute, force = nil)
hash[:case_sensitive] = options[:case_sensitive]
hash[:id] = model.id unless model.new_record?
hash[:allow_blank] = true if options[:allow_blank]
+
+ if options.key?(:client_validations) && options[:client_validations].key?(:class)
+ hash[:class] = options[:client_validations][:class].underscore
+ elsif model.class.name.demodulize != model.class.name
+ hash[:class] = model.class.name.underscore
+ end
+
if options.key?(:scope) && options[:scope].present?
hash[:scope] = Array.wrap(options[:scope]).inject({}) do |scope_hash, scope_item|
scope_hash.merge!(scope_item => model.send(scope_item))
end
end
- unless model.class.name.demodulize == model.class.name
- hash[:class] = model.class.name.underscore
- end
-
hash
end
View
6 test/active_record/cases/test_uniqueness_validator.rb
@@ -51,5 +51,11 @@ def test_uniqueness_client_side_hash_when_nested_module
expected_hash = { :message => "has already been taken", :case_sensitive => true, :class => 'active_record_test_module/user2' }
assert_equal expected_hash, UniquenessValidator.new(:attributes => [:name]).client_side_hash(@user, :name)
end
+
+ def test_uniqueness_client_side_hash_with_class_from_options
+ @user = UserForm.new
+ expected_hash = { :message => "has already been taken", :case_sensitive => true, :class => 'user' }
+ assert_equal expected_hash, UniquenessValidator.new(:attributes => [:name], :client_validations => { :class => 'User' } ).client_side_hash(@user, :name)
+ end
end
View
16 test/active_record/models/user.rb
@@ -12,3 +12,19 @@ class Thaumaturgist < Conjurer; end
module ActiveRecordTestModule
class User2 < User; end
end
+
+class UserForm
+ include ActiveRecord::Validations
+
+ attr_accessor :name
+
+ validates_uniqueness_of :name, :client_validations => { :class => User }
+
+ def self.i18n_scope
+ :activerecord
+ end
+
+ def new_record?
+ true
+ end
+end
Something went wrong with that request. Please try again.