added autovalidation (auto_validate! in your ExtendedDocument) and extracted some extlib stuff so we will soon be able to remove the dependency.
This commit is contained in:
parent
e9930c5a86
commit
890b60cae4
18 changed files with 497 additions and 52 deletions
|
@ -1,18 +1,6 @@
|
|||
module CouchRest
|
||||
class Response < Hash
|
||||
def initialize(keys = {})
|
||||
keys.each do |k,v|
|
||||
self[k.to_s] = v
|
||||
end
|
||||
end
|
||||
def []= key, value
|
||||
super(key.to_s, value)
|
||||
end
|
||||
def [] key
|
||||
super(key.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
require 'delegate'
|
||||
|
||||
module CouchRest
|
||||
class Document < Response
|
||||
include CouchRest::Mixins::Attachments
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
require File.join(File.dirname(__FILE__), '..', 'support', 'class')
|
||||
|
||||
# Extracted from ActiveSupport::Callbacks written by Yehuda Katz
|
||||
# http://github.com/wycats/rails/raw/18b405f154868204a8f332888871041a7bad95e1/activesupport/lib/active_support/callbacks.rb
|
||||
|
||||
|
|
|
@ -7,23 +7,27 @@ module CouchRest
|
|||
end
|
||||
|
||||
module ClassMethods
|
||||
|
||||
# Stores the class properties
|
||||
def properties
|
||||
@@properties ||= []
|
||||
end
|
||||
|
||||
# This is not a thread safe operation, if you have to set new properties at runtime
|
||||
# make sure to use a mutex.
|
||||
|
||||
def property(name, options={})
|
||||
unless properties.map{|p| p.name}.include?(name.to_s)
|
||||
property = CouchRest::Property.new(name, options.delete(:type), options)
|
||||
create_property_getter(property)
|
||||
create_property_setter(property) unless property.read_only == true
|
||||
properties << property
|
||||
end
|
||||
define_property(name, options) unless properties.map{|p| p.name}.include?(name.to_s)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# This is not a thread safe operation, if you have to set new properties at runtime
|
||||
# make sure to use a mutex.
|
||||
def define_property(name, options={})
|
||||
property = CouchRest::Property.new(name, options.delete(:type), options)
|
||||
create_property_getter(property)
|
||||
create_property_setter(property) unless property.read_only == true
|
||||
properties << property
|
||||
end
|
||||
|
||||
# defines the getter for the property
|
||||
def create_property_getter(property)
|
||||
meth = property.name
|
||||
|
|
|
@ -27,10 +27,14 @@ class Object
|
|||
end
|
||||
end
|
||||
|
||||
require 'pathname'
|
||||
require File.join(File.dirname(__FILE__), '..', 'support', 'class')
|
||||
|
||||
dir = File.join(Pathname(__FILE__).dirname.expand_path, '..', 'validation')
|
||||
|
||||
require File.join(dir, 'validation_errors')
|
||||
require File.join(dir, 'contextual_validators')
|
||||
require File.join(dir, 'auto_validate')
|
||||
|
||||
require File.join(dir, 'validators', 'generic_validator')
|
||||
require File.join(dir, 'validators', 'required_field_validator')
|
||||
|
@ -50,6 +54,13 @@ module CouchRest
|
|||
save_callback :before, :check_validations
|
||||
end
|
||||
EOS
|
||||
base.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
||||
def self.define_property(name, options={})
|
||||
super
|
||||
auto_generate_validations(properties.last)
|
||||
autovalidation_check = true
|
||||
end
|
||||
RUBY_EVAL
|
||||
end
|
||||
|
||||
# Ensures the object is valid for the context provided, and otherwise
|
||||
|
@ -136,7 +147,7 @@ module CouchRest
|
|||
include CouchRest::Validation::ValidatesWithMethod
|
||||
# include CouchRest::Validation::ValidatesWithBlock
|
||||
# include CouchRest::Validation::ValidatesIsUnique
|
||||
# include CouchRest::Validation::AutoValidate
|
||||
include CouchRest::Validation::AutoValidate
|
||||
|
||||
# Return the set of contextual validators or create a new one
|
||||
#
|
||||
|
|
|
@ -4,7 +4,6 @@ module CouchRest
|
|||
|
||||
def self.included(base)
|
||||
base.extend(ClassMethods)
|
||||
# extlib is required for the following code
|
||||
base.send(:class_inheritable_accessor, :design_doc)
|
||||
base.send(:class_inheritable_accessor, :design_doc_slug_cache)
|
||||
base.send(:class_inheritable_accessor, :design_doc_fresh)
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
require File.join(File.dirname(__FILE__), 'support', 'class')
|
||||
require File.join(File.dirname(__FILE__), 'support', 'blank')
|
||||
|
||||
# This file must be loaded after the JSON gem and any other library that beats up the Time class.
|
||||
class Time
|
||||
# This date format sorts lexicographically
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
begin
|
||||
require 'extlib'
|
||||
rescue
|
||||
puts "CouchRest::ExtendedDocument still requires extlib (not for much longer). This is left out of the gemspec on purpose."
|
||||
raise
|
||||
end
|
||||
|
||||
require 'mime/types'
|
||||
require File.join(File.dirname(__FILE__), "property")
|
||||
require File.join(File.dirname(__FILE__), '..', 'mixins', 'extended_document_mixins')
|
||||
|
@ -6,11 +13,11 @@ module CouchRest
|
|||
|
||||
# Same as CouchRest::Document but with properties and validations
|
||||
class ExtendedDocument < Document
|
||||
include CouchRest::Mixins::DocumentQueries
|
||||
include CouchRest::Callbacks
|
||||
include CouchRest::Mixins::DocumentProperties
|
||||
include CouchRest::Mixins::DocumentQueries
|
||||
include CouchRest::Mixins::Views
|
||||
include CouchRest::Mixins::DesignDoc
|
||||
include CouchRest::Callbacks
|
||||
|
||||
# Callbacks
|
||||
define_callbacks :save
|
||||
|
@ -98,6 +105,7 @@ module CouchRest
|
|||
# Overridden to set the unique ID.
|
||||
# Returns a boolean value
|
||||
def save_without_callbacks(bulk = false)
|
||||
raise ArgumentError, "a document requires database to be saved to" unless database
|
||||
set_unique_id if new_document? && self.respond_to?(:set_unique_id)
|
||||
result = database.save_doc(self, bulk)
|
||||
result["ok"] == true
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
module CouchRest
|
||||
|
||||
# Basic attribute support adding getter/setter + validation
|
||||
# Basic attribute support for adding getter/setter + validation
|
||||
class Property
|
||||
attr_reader :name, :type, :validation_format, :required, :read_only, :alias
|
||||
attr_reader :name, :type, :read_only, :alias, :options
|
||||
|
||||
# attribute to define
|
||||
def initialize(name, type = String, options = {})
|
||||
def initialize(name, type = nil, options = {})
|
||||
@name = name.to_s
|
||||
@type = type
|
||||
@type = type || String
|
||||
parse_options(options)
|
||||
self
|
||||
end
|
||||
|
@ -16,10 +16,10 @@ module CouchRest
|
|||
private
|
||||
def parse_options(options)
|
||||
return if options.empty?
|
||||
@required = true if (options[:required] && (options[:required] == true))
|
||||
@validation_format = options[:format] if options[:format]
|
||||
@read_only = options[:read_only] if options[:read_only]
|
||||
@alias = options[:alias] if options
|
||||
@validation_format = options.delete(:format) if options[:format]
|
||||
@read_only = options.delete(:read_only) if options[:read_only]
|
||||
@alias = options.delete(:alias) if options[:alias]
|
||||
@options = options
|
||||
end
|
||||
|
||||
end
|
||||
|
|
42
lib/couchrest/support/blank.rb
Normal file
42
lib/couchrest/support/blank.rb
Normal file
|
@ -0,0 +1,42 @@
|
|||
# blank? methods for several different class types
|
||||
class Object
|
||||
# Returns true if the object is nil or empty (if applicable)
|
||||
def blank?
|
||||
nil? || (respond_to?(:empty?) && empty?)
|
||||
end
|
||||
end # class Object
|
||||
|
||||
class Numeric
|
||||
# Numerics can't be blank
|
||||
def blank?
|
||||
false
|
||||
end
|
||||
end # class Numeric
|
||||
|
||||
class NilClass
|
||||
# Nils are always blank
|
||||
def blank?
|
||||
true
|
||||
end
|
||||
end # class NilClass
|
||||
|
||||
class TrueClass
|
||||
# True is not blank.
|
||||
def blank?
|
||||
false
|
||||
end
|
||||
end # class TrueClass
|
||||
|
||||
class FalseClass
|
||||
# False is always blank.
|
||||
def blank?
|
||||
true
|
||||
end
|
||||
end # class FalseClass
|
||||
|
||||
class String
|
||||
# Strips out whitespace then tests if the string is empty.
|
||||
def blank?
|
||||
strip.empty?
|
||||
end
|
||||
end # class String
|
175
lib/couchrest/support/class.rb
Normal file
175
lib/couchrest/support/class.rb
Normal file
|
@ -0,0 +1,175 @@
|
|||
# Copyright (c) 2004-2008 David Heinemeier Hansson
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
# Allows attributes to be shared within an inheritance hierarchy, but where
|
||||
# each descendant gets a copy of their parents' attributes, instead of just a
|
||||
# pointer to the same. This means that the child can add elements to, for
|
||||
# example, an array without those additions being shared with either their
|
||||
# parent, siblings, or children, which is unlike the regular class-level
|
||||
# attributes that are shared across the entire hierarchy.
|
||||
class Class
|
||||
# Defines class-level and instance-level attribute reader.
|
||||
#
|
||||
# @param *syms<Array> Array of attributes to define reader for.
|
||||
# @return <Array[#to_s]> List of attributes that were made into cattr_readers
|
||||
#
|
||||
# @api public
|
||||
#
|
||||
# @todo Is this inconsistent in that it does not allow you to prevent
|
||||
# an instance_reader via :instance_reader => false
|
||||
def cattr_reader(*syms)
|
||||
syms.flatten.each do |sym|
|
||||
next if sym.is_a?(Hash)
|
||||
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
||||
unless defined? @@#{sym}
|
||||
@@#{sym} = nil
|
||||
end
|
||||
|
||||
def self.#{sym}
|
||||
@@#{sym}
|
||||
end
|
||||
|
||||
def #{sym}
|
||||
@@#{sym}
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
|
||||
# Defines class-level (and optionally instance-level) attribute writer.
|
||||
#
|
||||
# @param <Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to define writer for.
|
||||
# @option syms :instance_writer<Boolean> if true, instance-level attribute writer is defined.
|
||||
# @return <Array[#to_s]> List of attributes that were made into cattr_writers
|
||||
#
|
||||
# @api public
|
||||
def cattr_writer(*syms)
|
||||
options = syms.last.is_a?(Hash) ? syms.pop : {}
|
||||
syms.flatten.each do |sym|
|
||||
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
||||
unless defined? @@#{sym}
|
||||
@@#{sym} = nil
|
||||
end
|
||||
|
||||
def self.#{sym}=(obj)
|
||||
@@#{sym} = obj
|
||||
end
|
||||
RUBY
|
||||
|
||||
unless options[:instance_writer] == false
|
||||
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
||||
def #{sym}=(obj)
|
||||
@@#{sym} = obj
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Defines class-level (and optionally instance-level) attribute accessor.
|
||||
#
|
||||
# @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to define accessor for.
|
||||
# @option syms :instance_writer<Boolean> if true, instance-level attribute writer is defined.
|
||||
# @return <Array[#to_s]> List of attributes that were made into accessors
|
||||
#
|
||||
# @api public
|
||||
def cattr_accessor(*syms)
|
||||
cattr_reader(*syms)
|
||||
cattr_writer(*syms)
|
||||
end
|
||||
|
||||
# Defines class-level inheritable attribute reader. Attributes are available to subclasses,
|
||||
# each subclass has a copy of parent's attribute.
|
||||
#
|
||||
# @param *syms<Array[#to_s]> Array of attributes to define inheritable reader for.
|
||||
# @return <Array[#to_s]> Array of attributes converted into inheritable_readers.
|
||||
#
|
||||
# @api public
|
||||
#
|
||||
# @todo Do we want to block instance_reader via :instance_reader => false
|
||||
# @todo It would be preferable that we do something with a Hash passed in
|
||||
# (error out or do the same as other methods above) instead of silently
|
||||
# moving on). In particular, this makes the return value of this function
|
||||
# less useful.
|
||||
def class_inheritable_reader(*ivars)
|
||||
instance_reader = ivars.pop[:reader] if ivars.last.is_a?(Hash)
|
||||
|
||||
ivars.each do |ivar|
|
||||
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
||||
def self.#{ivar}
|
||||
return @#{ivar} if self.object_id == #{self.object_id} || defined?(@#{ivar})
|
||||
ivar = superclass.#{ivar}
|
||||
return nil if ivar.nil? && !#{self}.instance_variable_defined?("@#{ivar}")
|
||||
@#{ivar} = ivar && !ivar.is_a?(Module) && !ivar.is_a?(Numeric) && !ivar.is_a?(TrueClass) && !ivar.is_a?(FalseClass) && !ivar.is_a?(Symbol) ? ivar.dup : ivar
|
||||
end
|
||||
RUBY
|
||||
unless instance_reader == false
|
||||
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
||||
def #{ivar}
|
||||
self.class.#{ivar}
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Defines class-level inheritable attribute writer. Attributes are available to subclasses,
|
||||
# each subclass has a copy of parent's attribute.
|
||||
#
|
||||
# @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to
|
||||
# define inheritable writer for.
|
||||
# @option syms :instance_writer<Boolean> if true, instance-level inheritable attribute writer is defined.
|
||||
# @return <Array[#to_s]> An Array of the attributes that were made into inheritable writers.
|
||||
#
|
||||
# @api public
|
||||
#
|
||||
# @todo We need a style for class_eval <<-HEREDOC. I'd like to make it
|
||||
# class_eval(<<-RUBY, __FILE__, __LINE__), but we should codify it somewhere.
|
||||
def class_inheritable_writer(*ivars)
|
||||
instance_writer = ivars.pop[:instance_writer] if ivars.last.is_a?(Hash)
|
||||
ivars.each do |ivar|
|
||||
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
||||
def self.#{ivar}=(obj)
|
||||
@#{ivar} = obj
|
||||
end
|
||||
RUBY
|
||||
unless instance_writer == false
|
||||
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
||||
def #{ivar}=(obj) self.class.#{ivar} = obj end
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Defines class-level inheritable attribute accessor. Attributes are available to subclasses,
|
||||
# each subclass has a copy of parent's attribute.
|
||||
#
|
||||
# @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to
|
||||
# define inheritable accessor for.
|
||||
# @option syms :instance_writer<Boolean> if true, instance-level inheritable attribute writer is defined.
|
||||
# @return <Array[#to_s]> An Array of attributes turned into inheritable accessors.
|
||||
#
|
||||
# @api public
|
||||
def class_inheritable_accessor(*syms)
|
||||
class_inheritable_reader(*syms)
|
||||
class_inheritable_writer(*syms)
|
||||
end
|
||||
end
|
167
lib/couchrest/validation/auto_validate.rb
Normal file
167
lib/couchrest/validation/auto_validate.rb
Normal file
|
@ -0,0 +1,167 @@
|
|||
# Ported from dm-migrations
|
||||
require File.join(File.dirname(__FILE__), '..', 'support', 'class')
|
||||
|
||||
module CouchRest
|
||||
|
||||
class Property
|
||||
# flag letting us know if we already checked the autovalidation settings
|
||||
attr_accessor :autovalidation_check
|
||||
end
|
||||
|
||||
module Validation
|
||||
module AutoValidate
|
||||
|
||||
# Turn off auto validation by default
|
||||
def auto_validation
|
||||
@@auto_validation ||= false
|
||||
end
|
||||
|
||||
# Force the auto validation for the class properties
|
||||
# This feature is still not fully ported over,
|
||||
# test are lacking, so please use with caution
|
||||
def auto_validate!
|
||||
@@auto_validation = true
|
||||
end
|
||||
|
||||
# adds message for validator
|
||||
def options_with_message(base_options, property, validator_name)
|
||||
options = base_options.clone
|
||||
opts = property.options
|
||||
options[:message] = if opts[:messages]
|
||||
if opts[:messages].is_a?(Hash) and msg = opts[:messages][validator_name]
|
||||
msg
|
||||
else
|
||||
nil
|
||||
end
|
||||
elsif opts[:message]
|
||||
opts[:message]
|
||||
else
|
||||
nil
|
||||
end
|
||||
options
|
||||
end
|
||||
|
||||
|
||||
##
|
||||
# Auto-generate validations for a given property. This will only occur
|
||||
# if the option :auto_validation is either true or left undefined.
|
||||
#
|
||||
# @details [Triggers]
|
||||
# Triggers that generate validator creation
|
||||
#
|
||||
# :nullable => false
|
||||
# Setting the option :nullable to false causes a
|
||||
# validates_presence_of validator to be automatically created on
|
||||
# the property
|
||||
#
|
||||
# :size => 20 or :length => 20
|
||||
# Setting the option :size or :length causes a validates_length_of
|
||||
# validator to be automatically created on the property. If the
|
||||
# value is a Integer the validation will set :maximum => value if
|
||||
# the value is a Range the validation will set :within => value
|
||||
#
|
||||
# :format => :predefined / lambda / Proc
|
||||
# Setting the :format option causes a validates_format_of
|
||||
# validator to be automatically created on the property
|
||||
#
|
||||
# :set => ["foo", "bar", "baz"]
|
||||
# Setting the :set option causes a validates_within
|
||||
# validator to be automatically created on the property
|
||||
#
|
||||
# Integer type
|
||||
# Using a Integer type causes a validates_is_number
|
||||
# validator to be created for the property. integer_only
|
||||
# is set to true
|
||||
#
|
||||
# BigDecimal or Float type
|
||||
# Using a Integer type causes a validates_is_number
|
||||
# validator to be created for the property. integer_only
|
||||
# is set to false, and precision/scale match the property
|
||||
#
|
||||
#
|
||||
# Messages
|
||||
#
|
||||
# :messages => {..}
|
||||
# Setting :messages hash replaces standard error messages
|
||||
# with custom ones. For instance:
|
||||
# :messages => {:presence => "Field is required",
|
||||
# :format => "Field has invalid format"}
|
||||
# Hash keys are: :presence, :format, :length, :is_unique,
|
||||
# :is_number, :is_primitive
|
||||
#
|
||||
# :message => "Some message"
|
||||
# It is just shortcut if only one validation option is set
|
||||
#
|
||||
def auto_generate_validations(property)
|
||||
return unless property.options
|
||||
return unless property.autovalidation_check || auto_validation || (property.options && property.options.has_key?(:auto_validation) && property.options[:auto_validation])
|
||||
|
||||
# value is set by the storage system
|
||||
opts = {}
|
||||
opts[:context] = property.options[:validates] if property.options.has_key?(:validates)
|
||||
|
||||
# presence
|
||||
unless opts[:allow_nil]
|
||||
# validates_present property.name, opts
|
||||
validates_present property.name, options_with_message(opts, property, :presence)
|
||||
end
|
||||
|
||||
# length
|
||||
if property.type == String
|
||||
# XXX: maybe length should always return a Range, with the min defaulting to 1
|
||||
# 52 being the max set
|
||||
len = property.options.fetch(:length, property.options.fetch(:size, 52))
|
||||
if len.is_a?(Range)
|
||||
opts[:within] = len
|
||||
else
|
||||
opts[:maximum] = len
|
||||
end
|
||||
# validates_length property.name, opts
|
||||
p "dude: #{options_with_message(opts, property, :length)}"
|
||||
validates_length property.name, options_with_message(opts, property, :length)
|
||||
end
|
||||
|
||||
# format
|
||||
if property.options.has_key?(:format)
|
||||
opts[:with] = property.options[:format]
|
||||
# validates_format property.name, opts
|
||||
validates_format property.name, options_with_message(opts, property, :format)
|
||||
end
|
||||
|
||||
# uniqueness validator
|
||||
if property.options.has_key?(:unique)
|
||||
value = property.options[:unique]
|
||||
if value.is_a?(Array) || value.is_a?(Symbol)
|
||||
# validates_is_unique property.name, :scope => Array(value)
|
||||
validates_is_unique property.name, options_with_message({:scope => Array(value)}, property, :is_unique)
|
||||
elsif value.is_a?(TrueClass)
|
||||
# validates_is_unique property.name
|
||||
validates_is_unique property.name, options_with_message({}, property, :is_unique)
|
||||
end
|
||||
end
|
||||
|
||||
# within validator
|
||||
if property.options.has_key?(:set)
|
||||
validates_within property.name, options_with_message({:set => property.options[:set]}, property, :within)
|
||||
end
|
||||
|
||||
# numeric validator
|
||||
if Integer == property.type
|
||||
opts[:integer_only] = true
|
||||
# validates_is_number property.name, opts
|
||||
validates_is_number property.name, options_with_message(opts, property, :is_number)
|
||||
elsif Float == property.type
|
||||
opts[:precision] = property.precision
|
||||
opts[:scale] = property.scale
|
||||
# validates_is_number property.name, opts
|
||||
validates_is_number property.name, options_with_message(opts, property, :is_number)
|
||||
end
|
||||
|
||||
# marked the property has checked
|
||||
property.autovalidation_check = true
|
||||
|
||||
end
|
||||
|
||||
end # module AutoValidate
|
||||
end # module Validation
|
||||
end # module CouchRest
|
|
@ -75,7 +75,7 @@ module CouchRest
|
|||
# @param <Symbol> field_name the name of the field that caused the error
|
||||
# @param <String> message the message to add
|
||||
def add(field_name, message)
|
||||
(errors[field_name] ||= []) << message
|
||||
(errors[field_name.to_sym] ||= []) << message
|
||||
end
|
||||
|
||||
# Collect all errors into a single list.
|
||||
|
|
|
@ -66,7 +66,7 @@ module CouchRest
|
|||
# Returns false for other property types.
|
||||
# Returns false for non-properties.
|
||||
def boolean_type?(property)
|
||||
property ? property.primitive == TrueClass : false
|
||||
property ? property.type == TrueClass : false
|
||||
end
|
||||
|
||||
end # class RequiredFieldValidator
|
||||
|
|
|
@ -2,17 +2,17 @@ require File.dirname(__FILE__) + '/../../spec_helper'
|
|||
|
||||
describe CouchRest::Server do
|
||||
|
||||
before(:all) do
|
||||
@couch = CouchRest::Server.new
|
||||
end
|
||||
|
||||
after(:all) do
|
||||
@couch.available_databases.each do |ref, db|
|
||||
db.delete!
|
||||
end
|
||||
end
|
||||
|
||||
describe "available databases" do
|
||||
before(:each) do
|
||||
@couch = CouchRest::Server.new
|
||||
end
|
||||
|
||||
after(:each) do
|
||||
@couch.available_databases.each do |ref, db|
|
||||
db.delete!
|
||||
end
|
||||
end
|
||||
|
||||
it "should let you add more databases" do
|
||||
@couch.available_databases.should be_empty
|
||||
@couch.define_available_database(:default, "cr-server-test-db")
|
||||
|
@ -20,6 +20,7 @@ describe CouchRest::Server do
|
|||
end
|
||||
|
||||
it "should verify that a database is available" do
|
||||
@couch.define_available_database(:default, "cr-server-test-db")
|
||||
@couch.available_database?(:default).should be_true
|
||||
@couch.available_database?("cr-server-test-db").should be_true
|
||||
@couch.available_database?(:matt).should be_false
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper')
|
||||
|
||||
# check the following file to see how to use the spec'd features.
|
||||
require File.join(FIXTURE_PATH, 'more', 'card')
|
||||
require File.join(FIXTURE_PATH, 'more', 'invoice')
|
||||
require File.join(FIXTURE_PATH, 'more', 'service')
|
||||
|
||||
describe "ExtendedDocument properties" do
|
||||
|
||||
|
@ -37,6 +36,8 @@ describe "ExtendedDocument properties" do
|
|||
it "should be auto timestamped" do
|
||||
@card.created_at.should be_nil
|
||||
@card.updated_at.should be_nil
|
||||
# :emo:hack for autospec
|
||||
Card.use_database(TEST_SERVER.default_database) if @card.database.nil?
|
||||
@card.save
|
||||
@card.created_at.should_not be_nil
|
||||
@card.updated_at.should_not be_nil
|
||||
|
@ -49,7 +50,7 @@ describe "ExtendedDocument properties" do
|
|||
end
|
||||
|
||||
it "should be able to be validated" do
|
||||
@card.should be_valid
|
||||
@card.valid?.should == true
|
||||
end
|
||||
|
||||
it "should let you validate the presence of an attribute" do
|
||||
|
@ -82,4 +83,31 @@ describe "ExtendedDocument properties" do
|
|||
|
||||
end
|
||||
|
||||
describe "autovalidation" do
|
||||
before(:each) do
|
||||
@service = Service.new(:name => "Coumpound analysis", :price => 3_000)
|
||||
end
|
||||
|
||||
it "should be valid" do
|
||||
@service.should be_valid
|
||||
end
|
||||
|
||||
describe "property :name, :length => 4...20" do
|
||||
it "should autovalidate the presence when length is set" do
|
||||
@service.name = nil
|
||||
@service.should_not be_valid
|
||||
@service.errors.should_not be_nil
|
||||
@service.errors.on(:name).first.should == "Name must not be blank"
|
||||
end
|
||||
|
||||
it "should autovalidate the correct length" do
|
||||
@service.name = "a"
|
||||
@service.should_not be_valid
|
||||
@service.errors.should_not be_nil
|
||||
@service.errors.on(:name).first.should == "Name must be between 4 and 19 characters long"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
2
spec/fixtures/more/card.rb
vendored
2
spec/fixtures/more/card.rb
vendored
|
@ -15,4 +15,6 @@ class Card < CouchRest::ExtendedDocument
|
|||
# Validation
|
||||
validates_present :first_name
|
||||
|
||||
auto_validate!
|
||||
|
||||
end
|
14
spec/fixtures/more/service.rb
vendored
Normal file
14
spec/fixtures/more/service.rb
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
class Service < CouchRest::ExtendedDocument
|
||||
# Include the validation module to get access to the validation methods
|
||||
include CouchRest::Validation
|
||||
|
||||
# Set the default database to use
|
||||
use_database TEST_SERVER.default_database
|
||||
|
||||
# Official Schema
|
||||
property :name, :length => 4...20
|
||||
property :price, :type => Integer
|
||||
|
||||
auto_validate!
|
||||
|
||||
end
|
|
@ -1,7 +1,8 @@
|
|||
require "rubygems"
|
||||
require "spec" # Satisfies Autotest and anyone else not using the Rake tasks
|
||||
|
||||
require File.join(File.dirname(__FILE__), '/../lib/couchrest')
|
||||
require File.join(File.dirname(__FILE__), '..','lib','couchrest')
|
||||
# check the following file to see how to use the spec'd features.
|
||||
|
||||
unless defined?(FIXTURE_PATH)
|
||||
FIXTURE_PATH = File.join(File.dirname(__FILE__), '/fixtures')
|
||||
|
|
Loading…
Reference in a new issue