Permalink
Browse files

Initial import

  • Loading branch information...
0 parents commit a27179542e82b46b2faa5da709932e3e304e567c Iain Hecker committed Jan 30, 2009
Showing with 242 additions and 0 deletions.
  1. +2 −0 .gitignore
  2. +13 −0 README
  3. +11 −0 Rakefile
  4. +1 −0 init.rb
  5. +117 −0 lib/validate_database.rb
  6. +3 −0 spec/db/database.yml
  7. +10 −0 spec/db/schema.rb
  8. +42 −0 spec/spec_helper.rb
  9. +39 −0 spec/validate_database_spec.rb
  10. +4 −0 tasks/validate_database_tasks.rake
2 .gitignore
@@ -0,0 +1,2 @@
+spec/*.log
+spec/db/*.db
13 README
@@ -0,0 +1,13 @@
+ValidateDatabase
+================
+
+Introduction goes here.
+
+
+Example
+=======
+
+Example goes here.
+
+
+Copyright (c) 2009 [name of plugin creator], released under the MIT license
11 Rakefile
@@ -0,0 +1,11 @@
+require 'rake'
+require 'spec/rake/spectask'
+
+desc 'Default: run specs.'
+task :default => :spec
+
+desc 'Run the specs'
+Spec::Rake::SpecTask.new(:spec) do |t|
+ t.spec_opts = ['--colour --format specdoc --loadby mtime --reverse']
+ t.spec_files = FileList['spec/**/*_spec.rb']
+end
1 init.rb
@@ -0,0 +1 @@
+ActiveRecord::Base.send(:include, ValidateDatabase)
117 lib/validate_database.rb
@@ -0,0 +1,117 @@
+module ValidateDatabase
+
+ @@options = {
+ :exclude => [:id, :created_at, :updated_at]
+ }
+ mattr_reader :options
+
+ def self.included(base)
+ base.extend ValidateDatabase::ClassMethods
+ base.send(:include, ValidateDatabase::InstanceMethods)
+ end
+
+ module ClassMethods
+
+ # Add validations according to your database, checking for type, limit and null.
+ # Specify specific columns to validate only them, or use the :all, :except way
+ # to exclude just some of them.
+ #
+ # class Post < ActiveRecord::Base
+ # validates_according_to_database :all, :except => :person_id
+ # end
+ #
+ # class User < ActiveRecord::Base
+ # validates_according_to_database :birthday
+ # end
+ #
+ # Note: id, created_at and updated_at are automatically excluded from any
+ # validation, because that will break Rails.
+ def validates_according_to_database(*args)
+ send(:validate, :database_validation) unless registered_database_validations
+ register_database_validations(args)
+ end
+
+ def register_database_validations(args)
+ @registered_database_validations = args
+ end
+
+ def registered_database_validations
+ @registered_database_validations
+ end
+
+ end
+
+ module InstanceMethods
+
+ def database_validation
+ database_validation_columns.each do |column|
+ validate_presence(column) unless column.null
+ may_allow_blank(column) do |value|
+ validate_limit(column, value) if column.limit
+ validate_numericality(column, value) if column.number?
+ validate_date(column, value) if column.type == :date
+ validate_datetime(column, value) if column.type == :datetime
+ validate_time(column, value) if column.type == :time
+ end
+ end
+ end
+
+ def database_validation_columns
+ columns = self.class.registered_database_validations
+ config = {}
+ config = columns.last if columns.last.is_a?(Hash)
+ if columns.first == :all
+ method = :reject
+ source = [*config[:except]].map(&:to_s)
+ else
+ method = :select
+ source = columns.map(&:to_s)
+ end
+ default_excludes = ValidateDatabase.options[:exclude].map(&:to_s)
+ self.class.columns.send(method) do |column|
+ source.include?(column.name)
+ end.reject do |column|
+ default_excludes.include?(column.name)
+ end
+ end
+
+ def validate_presence(column)
+ errors.add(column.name, :blank) if send(column.name.to_sym).blank?
+ end
+
+ def validate_limit(column, value)
+ errors.add(column.name, :too_long, :count => column.limit, :value => value) if value.to_s.size > column.limit
+ end
+
+ def validate_numericality(column, value)
+ if column.type == :integer
+ errors.add(column.name, :not_a_number, :value => value) unless value.to_s =~ /\A[+-]?\d+\Z/
+ else
+ begin
+ Kernel.Float(value)
+ rescue ArgumentError, TypeError
+ errors.add(column.name, :not_a_number, :value => value)
+ end
+ end
+ end
+
+ def validate_time(column, value)
+ value.to_time rescue errors.add(column.name, :not_a_time, :value => value)
+ end
+
+ def validate_date(column, value)
+ value.to_date rescue errors.add(column.name, :not_a_date, :value => value)
+ end
+
+ def validate_datetime(column, value)
+ value.to_datetime rescue errors.add(column.name, :not_a_datetime, :value => value)
+ end
+
+ def may_allow_blank(column)
+ value = send(column.name.to_sym)
+ yield(value) unless value.blank? and column.null
+ end
+
+ end
+
+end
3 spec/db/database.yml
@@ -0,0 +1,3 @@
+sqlite3:
+ :adapter: sqlite3
+ :dbfile: vendor/plugins/validate_database/spec/db/validate_database.sqlite3.db
10 spec/db/schema.rb
@@ -0,0 +1,10 @@
+ActiveRecord::Schema.define(:version => 0) do
+ create_table :models, :force => true do |t|
+ t.column :limited_string, :string, :limit => 5
+ [:integer, :float, :date, :datetime, :time].each do |type|
+ [:some, :required].each do |null|
+ t.column :"#{null}_#{type}", type, :null => (null == :some)
+ end
+ end
+ end
+end
42 spec/spec_helper.rb
@@ -0,0 +1,42 @@
+begin
+ require File.dirname(__FILE__) + '/../../../../spec/spec_helper'
+rescue LoadError
+ puts "You need to install rspec in your base app"
+ exit
+end
+
+plugin_spec_dir = File.dirname(__FILE__)
+ActiveRecord::Base.logger = Logger.new(plugin_spec_dir + "/debug.log")
+
+databases = YAML::load(IO.read(plugin_spec_dir + "/db/database.yml"))
+ActiveRecord::Base.establish_connection(databases[ENV["DB"] || "sqlite3"])
+load(File.join(plugin_spec_dir, "db", "schema.rb"))
+
+class Model < ActiveRecord::Base
+ validates_according_to_database :all
+end
+
+Types = [ :integer, :float, :date, :time, :datetime ]
+Nulls = [ :required, :some ]
+Value = {
+ :integer => 3213,
+ :float => 323.12,
+ :date => Date.today,
+ :time => Time.now.to_time,
+ :datetime => Time.now
+}
+ValueValid = lambda do |type, null|
+ hash = { Value[type] => :valid, "foo" => :invalid }
+ hash.merge!(nil => :invalid) if null == :required
+ hash.merge!(nil => :valid) if null == :some
+ hash
+end
+Might = { :valid => "should", :invalid => "should_not" }
+HaveError = { :valid => [:have_at_most,0], :invalid => [:have_at_least, 1] }
+R = { :required => "a required", :some => "some" }
+C = lambda { |type, null| :"#{null}_#{type}" }
+It = lambda do |attrs|
+ defaults = {:limited_string => "aaa"}
+ Types.each { |t| Nulls.each { |n| defaults.merge!(C[t, n] => Value[t]) } }
+ Model.new(defaults.merge(attrs))
+end
39 spec/validate_database_spec.rb
@@ -0,0 +1,39 @@
+require File.dirname(__FILE__) + '/spec_helper'
+
+class ValidateDatabaseSpec < Spec::ExampleGroup
+
+ describe "A model" do
+
+ it "should" do
+ a = It[{}]
+ a.valid?
+ puts a.errors.full_messages.inspect
+ puts a.attributes.inspect
+ a.should be_valid
+ end
+
+ Types.each do |type|
+
+ Nulls.each do |null|
+
+ ValueValid[type, null].each do |value_typed, valid|
+
+ [ value_typed, (value_typed.is_a?(String) ? nil : value_typed.to_s) ].flatten.each do |value|
+
+ it "#{Might[valid].humanize.downcase} allow #{R[null]} #{type} to be #{value.inspect}" do
+
+ It[C[type,null] => value].should send(*HaveError[valid]).error_on(C[type,null])
+
+ end
+
+ end
+
+ end
+
+ end
+
+ end
+
+ end
+
+end
4 tasks/validate_database_tasks.rake
@@ -0,0 +1,4 @@
+# desc "Explaining what the task does"
+# task :validate_database do
+# # Task goes here
+# end

0 comments on commit a271795

Please sign in to comment.