Permalink
Browse files

Initial commit

  • Loading branch information...
0 parents commit 7f7395af653c2fdf629c2d4d76b4ad1699c4172b @stevehodgkiss committed Feb 14, 2011
Showing with 315 additions and 0 deletions.
  1. +3 −0 .gitignore
  2. +1 −0 .rspec
  3. +7 −0 Gemfile
  4. +32 −0 Gemfile.lock
  5. +20 −0 LICENSE
  6. +23 −0 README.md
  7. +7 −0 Rakefile
  8. +1 −0 lib/validation-scopes.rb
  9. +40 −0 lib/validation_scopes.rb
  10. +10 −0 spec/spec_helper.rb
  11. +85 −0 spec/validation_scopes_spec.rb
  12. +64 −0 specs.watchr
  13. +22 −0 validation-scopes.gemspec
@@ -0,0 +1,3 @@
+*.gem
+.bundle
+pkg/*
1 .rspec
@@ -0,0 +1 @@
+--color
@@ -0,0 +1,7 @@
+source "http://rubygems.org"
+
+gemspec
+
+group :test do
+ gem "rspec"
+end
@@ -0,0 +1,32 @@
+PATH
+ remote: .
+ specs:
+ validation-scopes (0.0.1)
+ activemodel
+
+GEM
+ remote: http://rubygems.org/
+ specs:
+ activemodel (3.0.4)
+ activesupport (= 3.0.4)
+ builder (~> 2.1.2)
+ i18n (~> 0.4)
+ activesupport (3.0.4)
+ builder (2.1.2)
+ diff-lcs (1.1.2)
+ i18n (0.5.0)
+ rspec (2.5.0)
+ rspec-core (~> 2.5.0)
+ rspec-expectations (~> 2.5.0)
+ rspec-mocks (~> 2.5.0)
+ rspec-core (2.5.1)
+ rspec-expectations (2.5.0)
+ diff-lcs (~> 1.1.2)
+ rspec-mocks (2.5.0)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ rspec
+ validation-scopes!
20 LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2011 Steve Hodgkiss
+
+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.
@@ -0,0 +1,23 @@
+# Validation Scopes
+
+Validation Scopes allows you to group validations together that share the same conditions. It depends on ActiveModel. Example:
+
+ class Car < ActiveRecord::Base
+ validation_scope :if => Proc.new { |u| u.step == 2 } do
+ # All validations here get their options merged with the options passed in above
+ validates_presence_of :variant
+ validates_presence_of :body
+ end
+
+ validation_scope :if => Proc.new { |u| i.step == 3 } do
+ validates_inclusion_of :outstanding_finance, :in => [true, false], :if => Proc.new { |u| u.finance == true }
+ end
+ end
+
+# Installation
+
+Add the gem to your Gemfile
+
+ gem "validation-scopes"
+
+It will be included into ActiveRecord::Base if it is defined, if not use `include ValidationScopes` on any ActiveModel object.
@@ -0,0 +1,7 @@
+require 'bundler'
+Bundler::GemHelper.install_tasks
+
+require 'rspec/core/rake_task'
+RSpec::Core::RakeTask.new :spec
+
+task :default => :spec
@@ -0,0 +1 @@
+require 'validation_scopes'
@@ -0,0 +1,40 @@
+require 'active_support/concern'
+require 'active_model'
+
+module ValidationScopes
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+
+ def validation_scope(options, &block)
+ @_in_validation_scope = true
+ @_validation_scope_options = options
+ block.call
+ @_validation_scope_options = nil
+ @_in_validation_scope = false
+ end
+
+ def validate(*args, &block)
+ if @_in_validation_scope
+ if args.empty?
+ args = [@_validation_scope_options.dup]
+ elsif args.last.is_a?(Hash) && args.last.extractable_options?
+ options = args.extract_options!
+ options = options.dup
+ @_validation_scope_options.each_key do |key|
+ if options[key].nil?
+ options[key] = @_validation_scope_options[key]
+ else
+ options[key] = Array.wrap(options[key])
+ options[key] << @_validation_scope_options[key]
+ end
+ end
+ args << options
+ end
+ end
+ super(*args, &block)
+ end
+ end
+end
+
+ActiveRecord::Base.send(:include, ValidationScopes) if defined?(ActiveRecord)
@@ -0,0 +1,10 @@
+$LOAD_PATH.unshift(File.dirname(__FILE__))
+$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
+
+require 'validation_scopes'
+require 'bundler/setup'
+require 'rspec'
+
+RSpec.configure do |config|
+
+end
@@ -0,0 +1,85 @@
+require 'spec_helper'
+
+class TestUser
+ include ActiveModel::Validations
+ include ValidationScopes
+
+ attr_accessor :step
+
+ attr_accessor :name, :email, :address
+ attr_accessor :height, :weight
+ attr_accessor :eye_colour, :age
+
+ def step_2?
+ step == 2
+ end
+end
+
+describe ".validation_scope" do
+
+ context "validates_with" do
+ before do
+ User = Class.new(TestUser) do
+ validates_presence_of :address
+ validation_scope :if => :step_2? do
+ validates_presence_of :name
+ end
+ validation_scope :if => Proc.new { |u| u.step == 3 } do
+ validates_inclusion_of :eye_colour, :in => ["blue", "brown"], :if => Proc.new { |u| !u.age.nil? && u.age > 20 }
+ end
+ end
+ @user = User.new
+ @user.errors[:address].should be
+ @user.address = "123 High St"
+ end
+
+ it "should only validate name when on step 2" do
+ @user.name = nil
+ @user.should be_valid
+ @user.step = 2
+ @user.should be_invalid
+ @user.name = "Steve"
+ @user.should be_valid
+ end
+
+ it "only validates eye colour when on step 3 and age is above 20" do
+ @user.eye_colour = nil
+ @user.should be_valid
+ @user.step = 3
+ @user.eye_colour = "red"
+ @user.should be_valid
+ @user.age = 21
+ @user.should be_invalid
+ @user.eye_colour = "blue"
+ @user.should be_valid
+ end
+
+ after { Object.send(:remove_const, :User) }
+ end
+
+ context "validate method" do
+ before do
+ User = Class.new(TestUser) do
+ validation_scope :unless => :step_2? do
+ validate do
+ errors.add(:weight, "Must be greater than 0") unless !@weight.nil? && @weight > 0
+ end
+ end
+ end
+ end
+
+ it "should only validate weight on step 2" do
+ user = User.new
+ user.weight = 0
+ user.should be_invalid
+ user.weight = 1
+ user.should be_valid
+
+ user.step = 2
+ user.weight = 0
+ user.should be_valid
+ end
+
+ after { Object.send(:remove_const, :User) }
+ end
+end
@@ -0,0 +1,64 @@
+ENV["WATCHR"] = "1"
+system 'clear'
+
+def growl(message)
+ growlnotify = `which growlnotify`.chomp
+ title = "Watchr Test Results"
+ image = message.include?('0 failures') ? "~/Dropbox/Scripts/watchr_example/rails_ok.png" : "~/Dropbox/Scripts/watchr_example/rails_fail.png"
+ options = "-w -n Watchr --image '#{File.expand_path(image)}' -m '#{message}' '#{title}'"
+ system %(#{growlnotify} #{options} &)
+end
+
+def run(cmd)
+ puts(cmd)
+ `#{cmd}`
+end
+
+def run_spec_file(file)
+ system('clear')
+ result = run(%Q(rspec #{file}))
+ growl result.split("\n").last rescue nil
+ puts result
+end
+
+def run_all_specs
+ system('clear')
+ result = run "rspec spec"
+ growl result.split("\n").last rescue nil
+ puts result
+end
+
+def related_spec_files(path)
+ puts path
+ Dir['spec/*.rb'].select { |file| file =~ /#{File.basename(path).split(".").first}_spec.rb/ }
+end
+
+def run_suite
+ run_all_specs
+end
+
+watch('spec/spec_helper\.rb') { run_all_specs }
+watch('spec/.*_spec\.rb') { |m| run_spec_file(m[0]) }
+watch('lib/.*\.rb') { |m| related_spec_files(m[0]).map {|tf| run_spec_file(tf) } }
+
+# Ctrl-\
+Signal.trap 'QUIT' do
+ puts " --- Running all tests ---\n\n"
+ run_all_tests
+end
+
+@interrupted = false
+
+# Ctrl-C
+Signal.trap 'INT' do
+ if @interrupted then
+ @wants_to_quit = true
+ abort("\n")
+ else
+ puts "Interrupt a second time to quit"
+ @interrupted = true
+ Kernel.sleep 1.5
+ # raise Interrupt, nil # let the run loop catch it
+ run_suite
+ end
+end
@@ -0,0 +1,22 @@
+# -*- encoding: utf-8 -*-
+$:.push File.expand_path("../lib", __FILE__)
+
+Gem::Specification.new do |s|
+ s.name = "validation-scopes"
+ s.version = "0.0.1"# Validation::Scopes::VERSION
+ s.platform = Gem::Platform::RUBY
+ s.authors = ["Steve Hodgkiss"]
+ s.email = ["steve@hodgkiss.me.uk"]
+ s.homepage = ""
+ s.summary = %q{Scope ActiveModel validations}
+ s.description = %q{Scope ActiveModel validations}
+
+ s.rubyforge_project = "validation-scopes"
+
+ s.files = `git ls-files`.split("\n")
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
+ s.require_paths = ["lib"]
+
+ s.add_dependency("activemodel")
+end

0 comments on commit 7f7395a

Please sign in to comment.