From 890b60cae4939117d21d3031c90e8682679b78be Mon Sep 17 00:00:00 2001 From: Matt Aimonetti Date: Thu, 5 Feb 2009 17:06:12 -0800 Subject: [PATCH] added autovalidation (auto_validate! in your ExtendedDocument) and extracted some extlib stuff so we will soon be able to remove the dependency. --- lib/couchrest/core/document.rb | 18 +- lib/couchrest/mixins/callbacks.rb | 2 + lib/couchrest/mixins/properties.rb | 22 ++- lib/couchrest/mixins/validation.rb | 13 +- lib/couchrest/mixins/views.rb | 1 - lib/couchrest/monkeypatches.rb | 3 + lib/couchrest/more/extended_document.rb | 12 +- lib/couchrest/more/property.rb | 16 +- lib/couchrest/support/blank.rb | 42 +++++ lib/couchrest/support/class.rb | 175 ++++++++++++++++++ lib/couchrest/validation/auto_validate.rb | 167 +++++++++++++++++ lib/couchrest/validation/validation_errors.rb | 2 +- .../validators/required_field_validator.rb | 2 +- spec/couchrest/core/server_spec.rb | 21 ++- spec/couchrest/more/property_spec.rb | 34 +++- spec/fixtures/more/card.rb | 2 + spec/fixtures/more/service.rb | 14 ++ spec/spec_helper.rb | 3 +- 18 files changed, 497 insertions(+), 52 deletions(-) create mode 100644 lib/couchrest/support/blank.rb create mode 100644 lib/couchrest/support/class.rb create mode 100644 lib/couchrest/validation/auto_validate.rb create mode 100644 spec/fixtures/more/service.rb diff --git a/lib/couchrest/core/document.rb b/lib/couchrest/core/document.rb index abd2df7..115331f 100644 --- a/lib/couchrest/core/document.rb +++ b/lib/couchrest/core/document.rb @@ -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 diff --git a/lib/couchrest/mixins/callbacks.rb b/lib/couchrest/mixins/callbacks.rb index 8165422..3299d0a 100644 --- a/lib/couchrest/mixins/callbacks.rb +++ b/lib/couchrest/mixins/callbacks.rb @@ -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 diff --git a/lib/couchrest/mixins/properties.rb b/lib/couchrest/mixins/properties.rb index 8b92ada..d56e9f6 100644 --- a/lib/couchrest/mixins/properties.rb +++ b/lib/couchrest/mixins/properties.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 diff --git a/lib/couchrest/mixins/validation.rb b/lib/couchrest/mixins/validation.rb index 6dbb656..3cdb279 100644 --- a/lib/couchrest/mixins/validation.rb +++ b/lib/couchrest/mixins/validation.rb @@ -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 # diff --git a/lib/couchrest/mixins/views.rb b/lib/couchrest/mixins/views.rb index 9de7a14..005cb27 100644 --- a/lib/couchrest/mixins/views.rb +++ b/lib/couchrest/mixins/views.rb @@ -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) diff --git a/lib/couchrest/monkeypatches.rb b/lib/couchrest/monkeypatches.rb index bad327d..25acfa3 100644 --- a/lib/couchrest/monkeypatches.rb +++ b/lib/couchrest/monkeypatches.rb @@ -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 diff --git a/lib/couchrest/more/extended_document.rb b/lib/couchrest/more/extended_document.rb index e4615b6..4cbd94d 100644 --- a/lib/couchrest/more/extended_document.rb +++ b/lib/couchrest/more/extended_document.rb @@ -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 diff --git a/lib/couchrest/more/property.rb b/lib/couchrest/more/property.rb index 7b993ff..bbe398d 100644 --- a/lib/couchrest/more/property.rb +++ b/lib/couchrest/more/property.rb @@ -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 diff --git a/lib/couchrest/support/blank.rb b/lib/couchrest/support/blank.rb new file mode 100644 index 0000000..bb65e5f --- /dev/null +++ b/lib/couchrest/support/blank.rb @@ -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 diff --git a/lib/couchrest/support/class.rb b/lib/couchrest/support/class.rb new file mode 100644 index 0000000..3b5f370 --- /dev/null +++ b/lib/couchrest/support/class.rb @@ -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 of attributes to define reader for. + # @return 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 Boolean}]> Array of attributes to define writer for. + # @option syms :instance_writer if true, instance-level attribute writer is defined. + # @return 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 Boolean}]> Array of attributes to define accessor for. + # @option syms :instance_writer if true, instance-level attribute writer is defined. + # @return 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 of attributes to define inheritable reader for. + # @return 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 Boolean}]> Array of attributes to + # define inheritable writer for. + # @option syms :instance_writer if true, instance-level inheritable attribute writer is defined. + # @return 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 Boolean}]> Array of attributes to + # define inheritable accessor for. + # @option syms :instance_writer if true, instance-level inheritable attribute writer is defined. + # @return 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 \ No newline at end of file diff --git a/lib/couchrest/validation/auto_validate.rb b/lib/couchrest/validation/auto_validate.rb new file mode 100644 index 0000000..d5a96aa --- /dev/null +++ b/lib/couchrest/validation/auto_validate.rb @@ -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 diff --git a/lib/couchrest/validation/validation_errors.rb b/lib/couchrest/validation/validation_errors.rb index 671348e..0852244 100644 --- a/lib/couchrest/validation/validation_errors.rb +++ b/lib/couchrest/validation/validation_errors.rb @@ -75,7 +75,7 @@ module CouchRest # @param field_name the name of the field that caused the error # @param 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. diff --git a/lib/couchrest/validation/validators/required_field_validator.rb b/lib/couchrest/validation/validators/required_field_validator.rb index e30fa83..17e3132 100644 --- a/lib/couchrest/validation/validators/required_field_validator.rb +++ b/lib/couchrest/validation/validators/required_field_validator.rb @@ -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 diff --git a/spec/couchrest/core/server_spec.rb b/spec/couchrest/core/server_spec.rb index 8a23e10..5e0f1f7 100644 --- a/spec/couchrest/core/server_spec.rb +++ b/spec/couchrest/core/server_spec.rb @@ -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 diff --git a/spec/couchrest/more/property_spec.rb b/spec/couchrest/more/property_spec.rb index 0046bb5..9a341ed 100644 --- a/spec/couchrest/more/property_spec.rb +++ b/spec/couchrest/more/property_spec.rb @@ -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 \ No newline at end of file diff --git a/spec/fixtures/more/card.rb b/spec/fixtures/more/card.rb index 700e357..b57bb82 100644 --- a/spec/fixtures/more/card.rb +++ b/spec/fixtures/more/card.rb @@ -15,4 +15,6 @@ class Card < CouchRest::ExtendedDocument # Validation validates_present :first_name + auto_validate! + end \ No newline at end of file diff --git a/spec/fixtures/more/service.rb b/spec/fixtures/more/service.rb new file mode 100644 index 0000000..f77a31e --- /dev/null +++ b/spec/fixtures/more/service.rb @@ -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 \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 06c7979..8aea0d3 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -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')