sam / dm-more

Extras for DataMapper, including bridges to DataObjects::Migrations and Merb::DataMapper

This URL has Read+Write access

Dan Kubb (author)
Sat May 24 22:29:10 -0700 2008
commit  69b9df09621058677f80095d365d4c16f9c247bd
tree    f6c9fe8f58fdfe12916409a6285adb39a59ea511
parent  378929eef072c085e3bb81a64d33ade6dff8d530
dm-more / dm-validations / lib / dm-validations.rb
100644 202 lines (175 sloc) 6.011 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
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 / 'custom_validator'
 
require dir / 'support' / 'object'
 
module DataMapper
  module Validate
 
    # Validate the resource before saving
    #
    def save(context = :default)
      return false unless valid?(context)
      super()
    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
      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
 
      # 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 attributes 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 # module Validate
 
  module Resource
    class << self
      included = instance_method(:included)
 
      define_method(:included) do |model|
        included.bind(self).call(model)
        model.send(:include, Validate)
      end
    end
 
    module ClassMethods
      include Validate::ClassMethods
    end # module ClassMethods
  end # module Resource
end # module DataMapper