public
Rubygem
Description: Extras for DataMapper, including bridges to DataObjects::Migrations and Merb::DataMapper
Homepage: http://datamapper.org
Clone URL: git://github.com/sam/dm-more.git
Search Repo:
myabc (author)
Thu May 08 15:04:41 -0700 2008
commit  1569d96fa9c302ce85b327c5d9fd6fc9c20b1756
tree    71280dcbcc2f3feb7f07a0816ee4af18b6fc457d
parent  f331f4c3fc6c52f69790feecfee0bb804a66296c
dm-more / dm-validations / lib / dm-validations.rb
100644 205 lines (176 sloc) 7.268 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
require 'rubygems'
require 'pathname'
 
gem 'dm-core', '=0.9.0'
require 'data_mapper'
 
 
dir = Pathname(__FILE__).dirname.expand_path / 'dm-validations'
 
require dir / 'validation_errors'
require dir / 'contextual_validators'
require dir / 'auto_validate'
 
require dir / 'generic_validator'
require dir / 'required_field_validator'
require dir / 'absent_field_validator'
require dir / 'confirmation_validator'
require dir / 'format_validator'
require dir / 'length_validator'
require dir / 'within_validator'
require dir / 'numeric_validator'
require dir / 'method_validator'
require dir / 'uniqueness_validator'
require dir / 'acceptance_validator'
 
require dir / 'support' / 'object'
 
#require File.join(File.dirname(__FILE__),'dm-validations','validation_errors')
#require File.join(File.dirname(__FILE__),'dm-validations','contextual_validators')
#require File.join(File.dirname(__FILE__),'dm-validations','auto_validate')
 
#require File.join(File.dirname(__FILE__),'dm-validations','generic_validator')
#require File.join(File.dirname(__FILE__),'dm-validations','required_field_validator')
#require File.join(File.dirname(__FILE__),'dm-validations','absent_field_validator')
#require File.join(File.dirname(__FILE__),'dm-validations','confirmation_validator')
#require File.join(File.dirname(__FILE__),'dm-validations','format_validator')
#require File.join(File.dirname(__FILE__),'dm-validations','length_validator')
#require File.join(File.dirname(__FILE__),'dm-validations','within_validator')
#require File.join(File.dirname(__FILE__),'dm-validations','numeric_validator')
#require File.join(File.dirname(__FILE__),'dm-validations','method_validator')
#require File.join(File.dirname(__FILE__),'dm-validations','uniqueness_validator')
#require File.join(File.dirname(__FILE__),'dm-validations','acceptance_validator')
 
#require File.join(File.dirname(__FILE__),'dm-validations','support','object')
 
 
 
module DataMapper
  module Validate
      
      def self.included(base)
        base.extend(ClassMethods)
        base.class_eval do
         include DataMapper::Validate::ValidatesPresent
         include DataMapper::Validate::ValidatesAbsent
         include DataMapper::Validate::ValidatesIsConfirmed
         include DataMapper::Validate::ValidatesIsAccepted
         include DataMapper::Validate::ValidatesFormat
         include DataMapper::Validate::ValidatesLength
         include DataMapper::Validate::ValidatesWithin
         include DataMapper::Validate::ValidatesIsNumber
         include DataMapper::Validate::ValidatesWithMethod
         include DataMapper::Validate::ValidatesIsUnique
         include DataMapper::Validate::AutoValidate
        end
      end
      
      # Return the ValidationErrors
      #
      def errors
        @errors ||= ValidationErrors.new
      end
      
      # Mark this resource as validatable. When we validate associations of a
      # resource we can check if they respond to validatable? before trying to
      # recursivly validate them
      #
      def validatable?()
        true
      end
      
      # Alias for valid?(:default)
      #
      def valid_for_default?
        valid?(:default)
      end
      
      # Check if a resource is valid in a given context
      #
      def valid?(context = :default)
        self.class.validators.execute(context,self)
      end
      
      # Begin a recursive walk of the model checking validity
      #
      def all_valid?(context = :default)
        recursive_valid?(self,context,true)
      end
      
      # Do recursive validity checking
      #
      def recursive_valid?(target, context, state)
        valid = state
        target.instance_variables.each do |ivar|
          ivar_value = target.instance_variable_get(ivar)
          if ivar_value.validatable?
            valid = valid && recursive_valid?(ivar_value,context,valid)
          elsif ivar_value.respond_to?(:each)
            ivar_value.each do |item|
              if item.validatable?
                valid = valid && recursive_valid?(item,context,valid)
              end
            end
          end
        end
        return valid && target.valid?
      end
      
      
      def validation_property_value(name)
        return self.instance_variable_get("@#{name}") if self.instance_variables.include?(name)
        return self.send(name) if self.respond_to?(name)
        nil
      end
      
      def validation_association_keys(name)
        if self.class.relationships.has_key?(name)
          result = []
          relation = self.class.relationships[name]
          relation.child_key.each do |key|
            result << key.name
          end
          return result
        end
        nil
      end
      
      module ClassMethods
        
        # Return the set of contextual validators or create a new one
        #
        def validators
          @validations ||= ContextualValidators.new
        end
        
        # Clean up the argument list and return a opts hash, including the
        # merging of any default opts. Set the context to default if none is
        # provided. Also allow :context to be aliased to :on, :when & group
        #
        def opts_from_validator_args(args, defaults = nil)
          opts = args.last.kind_of?(Hash) ? args.pop : {}
          context = :default
          context = opts[:context] if opts.has_key?(:context)
          context = opts.delete(:on) if opts.has_key?(:on)
          context = opts.delete(:when) if opts.has_key?(:when)
          context = opts.delete(:group) if opts.has_key?(:group)
          opts[:context] = context
          opts.mergs!(defaults) unless defaults.nil?
          opts
        end
        
        # Given a new context create an instance method of
        # valid_for_<context>? which simply calls valid?(context)
        # if it does not already exist
        #
        def create_context_instance_methods(context)
          name = "valid_for_#{context.to_s}?"
          if !self.instance_methods.include?(name)
            class_eval <<-EOS
def #{name}
valid?('#{context.to_s}'.to_sym)
end
EOS
          end
          
          all = "all_valid_for_#{context.to_s}?"
          if !self.instance_methods.include?(all)
            class_eval <<-EOS
def #{all}
all_valid?('#{context.to_s}'.to_sym)
end
EOS
          end
        end
        
        # Create a new validator of the given klazz and push it onto the
        # requested context for each of the fields in the fields list
        #
        def add_validator_to_context(opts, fields, klazz)
          fields.each do |field|
            if opts[:context].is_a?(Symbol)
              validators.context(opts[:context]) << klazz.new(field, opts)
              create_context_instance_methods(opts[:context])
            elsif opts[:context].is_a?(Array)
              opts[:context].each do |c|
                validators.context(c) << klazz.new(field, opts)
                create_context_instance_methods(c)
              end
            end
          end
        end
              
      end #module ClassMethods
  end
end