Skip to content
Browse files

Add documentation.

  • Loading branch information...
1 parent e8ce1c7 commit 8033b923af8f6240ae818cd4c6021342886ed660 @Manfred Manfred committed Mar 27, 2010
Showing with 107 additions and 4 deletions.
  1. +2 −1 .gitignore
  2. +73 −0 README.rdoc
  3. +26 −3 lib/validation_sets.rb
  4. +6 −0 lib/validation_sets/validation_set.rb
View
3 .gitignore
@@ -1 +1,2 @@
-pkg
+pkg
+rdoc
View
73 README.rdoc
@@ -0,0 +1,73 @@
+= Validation Sets
+
+A Rails plugin to bundle validations in sets. You can turn entire sets of validations on or off on
+an instance. This allows you to use different sets of validations for various user roles in the
+application, or for different stages in the lifetime of the model.
+
+ validation_set_for(:activation) do |set|
+ set.validates_presence_of :fullname, :username, :email
+ set.validate :password_should_fit_requirements
+ end
+
+
+== The Case of the Organization
+
+Let's assume we have an organization in our application. The organization represents a company
+using the application. Administrators add these organizations, but often they don't have all the
+information about the company yet. It's up to the contact at the organization to complete it.
+
+ class Organization < ActiveRecord::Base
+ validates_presence_of :name
+
+ validation_set_for(:contact) do |set|
+ set.validates_presence_of :address, :zipcode, :city
+ end
+ end
+
+Now we can have two controllers, one of the administrator and one for the contact. For the
+administrator we don't run any validations except on name so she can choose to fill out any
+of the field.
+
+ class Administrator::OrganizationsController < ActionController::Base
+ allow_access :administrator
+
+ def create
+ @organization = Organization.new(params[:organization])
+ if @organization.save
+ redirect_to [:administrator, @organization]
+ else
+ render :new
+ end
+ end
+ end
+
+For the contact we turn on the extra set of validations for the contact so all the fields
+need to be filled out.
+
+ class Contact::OrganizationsController < ActionController::Base
+ allow_access(:contact) { @organization = @authenticated.organization }
+
+ def update
+ @organization.attributes = params[:organization].slice(:address, :zipcode, :city)
+ @organization.use_validation_set(:contact)
+ if @organization.save
+ redirect_to [:contact, @organization]
+ else
+ render :edit
+ end
+ end
+ end
+
+== Important Security Note
+
+Validation sets uses an attribute on the model called <tt>_validation_set</tt> to set the active
+validation set. Through mass attribute assignment this attribute can be set by anyone from outside
+the application. YOU have to take care to either protect the attribute with <tt>attr_protected</tt> or
+<tt>attr_accessible</tt>.
+
+ attr_protected :_validation_set
+
+Another way to protect it is by slicing out the accessible params in the controller as done in the
+example above.
+
+ @organization = params[:organization].slice(:address, :zipcode, :city)
View
29 lib/validation_sets.rb
@@ -1,8 +1,11 @@
module ValidationSets
autoload :ValidationSet, 'validation_sets/validation_set'
+ # Used to set the current validation set during definition of the model
attr_accessor :current_validation_set
+ # Returns the name of the validation callback method for a save method and the label of a
+ # validation set.
def validation_set_method(on, label)
case on
when :save then "validate_#{label}_set".to_sym
@@ -11,6 +14,7 @@ def validation_set_method(on, label)
end
end
+ # Returns the name of the validation callback method for a save method.
def validation_method(on)
if current_validation_set
validation_set_method(on, current_validation_set)
@@ -19,6 +23,13 @@ def validation_method(on)
end
end
+ # Add a validation set to the model.
+ #
+ # class Account < ActiveRecord::Base
+ # validation_set_for(:activation) do |set|
+ # set.validates_presence_of :username
+ # end
+ # end
def validation_set_for(label, &block)
[:save, :create, :update].each do |save_method|
callback_chain = validation_method(save_method)
@@ -39,15 +50,27 @@ def validation_set_for(label, &block)
end
module InstanceMethods
+ # Note that you want to protect this attribute to make sure people can't change the validation
+ # set from the form
attr_reader :_validation_set
+ # Set the active validation set on the model. After calling this both the global as well as
+ # the validations defined in the set will run.
+ #
+ # Example:
+ #
+ # account = Account.new
+ # account.use_validation_set(:admin) # Turns on the :admin validation set
+ # account.use_validation_set(nil) # Turns off all validation sets
def use_validation_set(set)
@_validation_set = set
end
end
end
-class ActiveRecord::Base
- extend ValidationSets
- include ValidationSets::InstanceMethods
+module ActiveRecord #:nodoc:
+ class Base #:nodoc:
+ extend ValidationSets
+ include ValidationSets::InstanceMethods
+ end
end
View
6 lib/validation_sets/validation_set.rb
@@ -1,22 +1,28 @@
module ValidationSets
+ # A ValidationSet instance is used to redirect the original validation methods (validate,
+ # validate_on_create, and validate_on_update) to the callback chain for the set.
class ValidationSet
def initialize(model, label)
@model = model
@label = label
end
+ # Adds a validation method or proc that always runs
def validate(*params, &block)
send(validation_set_method(:save, @label), *params, &block)
end
+ # Adds a validation method or proc that runs on create
def validate_on_create(*params, &block)
send(validation_set_method(:create, @label), *params, &block)
end
+ # Adds a validation method or proc that runs on update
def validate_on_update(*params, &block)
send(validation_set_method(:update, @label), *params, &block)
end
+ # Forwards all other methods (ie. validates_presence_of) to the model class.
def method_missing(method, *attributes, &block)
@model.send(method, *attributes, &block)
end

0 comments on commit 8033b92

Please sign in to comment.
Something went wrong with that request. Please try again.