mongomatic 0.1.4 → 0.1.31

Sign up to get free protection for your applications and to get access to all the features.
Files changed (28) hide show
  1. data/README.rdoc +26 -90
  2. data/lib/mongomatic.rb +2 -1
  3. data/lib/mongomatic/base.rb +2 -14
  4. data/lib/mongomatic/validatable.rb +31 -0
  5. data/lib/mongomatic/validatable/child_validation.rb +17 -0
  6. data/lib/mongomatic/validatable/errors.rb +108 -0
  7. data/lib/mongomatic/validatable/included_validation.rb +11 -0
  8. data/lib/mongomatic/validatable/macros.rb +316 -0
  9. data/lib/mongomatic/validatable/object_extension.rb +21 -0
  10. data/lib/mongomatic/validatable/requireable.rb +28 -0
  11. data/lib/mongomatic/validatable/understandable.rb +33 -0
  12. data/lib/mongomatic/validatable/validatable_class_methods.rb +89 -0
  13. data/lib/mongomatic/validatable/validatable_instance_methods.rb +111 -0
  14. data/lib/mongomatic/validatable/validations/validates_acceptance_of.rb +16 -0
  15. data/lib/mongomatic/validatable/validations/validates_associated.rb +15 -0
  16. data/lib/mongomatic/validatable/validations/validates_confirmation_of.rb +25 -0
  17. data/lib/mongomatic/validatable/validations/validates_each.rb +16 -0
  18. data/lib/mongomatic/validatable/validations/validates_exclusion_of.rb +19 -0
  19. data/lib/mongomatic/validatable/validations/validates_format_of.rb +18 -0
  20. data/lib/mongomatic/validatable/validations/validates_inclusion_of.rb +19 -0
  21. data/lib/mongomatic/validatable/validations/validates_length_of.rb +32 -0
  22. data/lib/mongomatic/validatable/validations/validates_numericality_of.rb +28 -0
  23. data/lib/mongomatic/validatable/validations/validates_presence_of.rb +18 -0
  24. data/lib/mongomatic/validatable/validations/validates_true_for.rb +15 -0
  25. data/lib/mongomatic/validatable/validations/validation_base.rb +93 -0
  26. data/test/helper.rb +1 -4
  27. metadata +24 -3
  28. data/lib/mongomatic/errors.rb +0 -7
@@ -0,0 +1,21 @@
1
+ class Object #:nodoc:
2
+ module InstanceExecHelper #:nodoc:
3
+ end
4
+ include InstanceExecHelper
5
+ def instance_eval_with_params(*args, &block)
6
+ begin
7
+ old_critical, Thread.critical = Thread.critical, true
8
+ n = 0
9
+ n += 1 while respond_to?(mname="__instance_exec#{n}")
10
+ InstanceExecHelper.module_eval{ define_method(mname, &block) }
11
+ ensure
12
+ Thread.critical = old_critical
13
+ end
14
+ begin
15
+ ret = send(mname, *args)
16
+ ensure
17
+ InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil
18
+ end
19
+ ret
20
+ end
21
+ end
@@ -0,0 +1,28 @@
1
+ module Mongomatic
2
+ module Validatable
3
+ module Requireable #:nodoc:
4
+ module ClassMethods #:nodoc:
5
+ def requires(*args)
6
+ required_options.concat args
7
+ end
8
+
9
+ def required_options
10
+ @required_options ||= []
11
+ end
12
+ end
13
+
14
+ def self.included(klass)
15
+ klass.extend ClassMethods
16
+ end
17
+
18
+ def requires(options)
19
+ required_options = self.class.required_options.inject([]) do |errors, attribute|
20
+ errors << attribute.to_s unless options.has_key?(attribute)
21
+ errors
22
+ end
23
+ raise ArgumentError.new("#{self.class} requires options: #{required_options.join(', ')}") if required_options.any?
24
+ true
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,33 @@
1
+ module Mongomatic
2
+ module Validatable
3
+ module Understandable #:nodoc:
4
+ module ClassMethods #:nodoc:
5
+ def understands(*args)
6
+ understandings.concat args
7
+ end
8
+
9
+ def understandings
10
+ @understandings ||= []
11
+ end
12
+
13
+ def all_understandings
14
+ return understandings + self.superclass.all_understandings if self.superclass.respond_to? :all_understandings
15
+ understandings
16
+ end
17
+ end
18
+
19
+ def self.included(klass)
20
+ klass.extend ClassMethods
21
+ end
22
+
23
+ def must_understand(hash)
24
+ invalid_options = hash.inject([]) do |errors, (key, value)|
25
+ errors << key.to_s unless self.class.all_understandings.include?(key)
26
+ errors
27
+ end
28
+ raise ArgumentError.new("invalid options: #{invalid_options.join(', ')}") if invalid_options.any?
29
+ true
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,89 @@
1
+ module Mongomatic
2
+ module Validatable
3
+ module ClassMethods #:nodoc:
4
+
5
+ def validate_children(instance, group)
6
+ self.children_to_validate.each do |child_validation|
7
+ next unless child_validation.should_validate?(instance)
8
+ child_or_children = instance.send child_validation.attribute
9
+ [child_or_children].flatten.each do |child|
10
+ if (child.respond_to?(:valid_for_group?))
11
+ child.valid_for_group?(group)
12
+ else
13
+ child.valid?
14
+ end
15
+ child.errors.each do |attribute, messages|
16
+ if messages.is_a?(String)
17
+ add_error(instance, child_validation.map[attribute.to_sym] || attribute, messages)
18
+ else
19
+ messages.each do |message|
20
+ add_error(instance, child_validation.map[attribute.to_sym] || attribute, message)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ def all_before_validations
29
+ if self.superclass.respond_to? :all_before_validations
30
+ return before_validations + self.superclass.all_before_validations
31
+ end
32
+ before_validations
33
+ end
34
+
35
+ def before_validations
36
+ @before_validations ||= []
37
+ end
38
+
39
+ def all_validations
40
+ if self.respond_to?(:superclass) && self.superclass.respond_to?(:all_validations)
41
+ return validations + self.superclass.all_validations
42
+ end
43
+ validations
44
+ end
45
+
46
+ def validations
47
+ @validations ||= []
48
+ end
49
+
50
+ def add_error(instance, attribute, msg)
51
+ instance.errors.add(attribute, msg)
52
+ end
53
+
54
+ def validation_keys_include?(key)
55
+ validations.map { |validation| validation.key }.include?(key)
56
+ end
57
+
58
+ def validations_to_include
59
+ @validations_to_include ||= []
60
+ end
61
+
62
+ protected
63
+
64
+ def add_validations(args, klass)
65
+ options = args.last.is_a?(Hash) ? args.pop : {}
66
+ args.each do |attribute|
67
+ new_validation = klass.new self, attribute, options
68
+ self.validations << new_validation
69
+ self.create_valid_method_for_groups new_validation.groups
70
+ end
71
+ end
72
+
73
+ def create_valid_method_for_groups(groups)
74
+ groups.each do |group|
75
+ self.class_eval do
76
+ define_method "valid_for_#{group}?".to_sym do
77
+ valid_for_group?(group)
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ def children_to_validate
84
+ @children_to_validate ||= []
85
+ end
86
+
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,111 @@
1
+ module Mongomatic
2
+ module Validatable
3
+ def self.included(klass) #:nodoc:
4
+ klass.extend Validatable::ClassMethods
5
+ klass.extend Validatable::Macros
6
+ end
7
+
8
+ # call-seq: valid?
9
+ #
10
+ # Returns true if no errors were added otherwise false. Only executes validations that have no :groups option specified
11
+ def valid?
12
+ self.send(:before_validate) if self.respond_to?(:before_validate)
13
+ r = valid_for_group?(nil)
14
+ self.send(:after_validate) if self.respond_to?(:after_validate)
15
+ r
16
+ end
17
+
18
+ # call-seq: errors
19
+ #
20
+ # Returns the Errors object that holds all information about attribute error messages.
21
+ def errors
22
+ @errors ||= Validatable::Errors.new
23
+ end
24
+
25
+ def valid_for_group?(group) #:nodoc:
26
+ errors.clear
27
+ run_before_validations
28
+ self.class.validate_children(self, group)
29
+ self.validate_group(group)
30
+ errors.empty?
31
+ end
32
+
33
+ def times_validated(key) #:nodoc:
34
+ times_validated_hash[key] || 0
35
+ end
36
+
37
+ def increment_times_validated_for(validation) #:nodoc:
38
+ if validation.key != nil
39
+ if times_validated_hash[validation.key].nil?
40
+ times_validated_hash[validation.key] = 1
41
+ else
42
+ times_validated_hash[validation.key] += 1
43
+ end
44
+ end
45
+ end
46
+
47
+ # call-seq: validate_only(key)
48
+ #
49
+ # Only executes a specified validation. The argument should follow a pattern based on the key of the validation.
50
+ # Examples:
51
+ # * validates_presence_of :name can be run with obj.validate_only("presence_of/name")
52
+ # * validates_presence_of :birthday, :key => "a key" can be run with obj.validate_only("presence_of/a key")
53
+ def validate_only(key)
54
+ validation_name, attribute_name = key.split("/")
55
+ validation_name = validation_name.split("_").collect{|word| word.capitalize}.join
56
+ validation_key = "#{self.class.name}/Validatable::Validates#{validation_name}/#{attribute_name}"
57
+ validation = self.class.all_validations.find { |validation| validation.key == validation_key }
58
+ raise ArgumentError.new("validation with key #{validation_key} could not be found") if validation.nil?
59
+ errors.clear
60
+ run_validation(validation)
61
+ end
62
+
63
+ protected
64
+ def times_validated_hash #:nodoc:
65
+ @times_validated_hash ||= {}
66
+ end
67
+
68
+ def validate_group(group) #:nodoc:
69
+ validation_levels.each do |level|
70
+ validations_for_level_and_group(level, group).each do |validation|
71
+ run_validation(validation) if validation.should_validate?(self)
72
+ end
73
+ return unless self.errors.empty?
74
+ end
75
+ end
76
+
77
+ def run_validation(validation) #:nodoc:
78
+ validation_result = validation.valid?(self)
79
+ add_error(validation.attribute, validation.message(self)) unless validation_result
80
+ increment_times_validated_for(validation)
81
+ validation.run_after_validate(validation_result, self, validation.attribute)
82
+ end
83
+
84
+ def run_before_validations #:nodoc:
85
+ self.class.all_before_validations.each do |block|
86
+ instance_eval &block
87
+ end
88
+ end
89
+
90
+ def add_error(attribute, message) #:nodoc:
91
+ self.class.add_error(self, attribute, message)
92
+ end
93
+
94
+ def validations_for_level_and_group(level, group) #:nodoc:
95
+ validations_for_level = self.all_validations.select { |validation| validation.level == level }
96
+ return validations_for_level.select { |validation| validation.groups.empty? } if group.nil?
97
+ validations_for_level.select { |validation| validation.groups.include?(group) }
98
+ end
99
+
100
+ def all_validations #:nodoc:
101
+ res = self.class.validations_to_include.inject(self.class.all_validations) do |result, included_validation_class|
102
+ result += self.send(included_validation_class.attribute).all_validations
103
+ result
104
+ end
105
+ end
106
+
107
+ def validation_levels #:nodoc:
108
+ self.class.all_validations.inject([1]) { |result, validation| result << validation.level }.uniq.sort
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,16 @@
1
+ module Mongomatic
2
+ module Validatable
3
+ class ValidatesAcceptanceOf < ValidationBase #:nodoc:
4
+ def valid?(instance)
5
+ value = instance[self.attribute.to_s]
6
+ return true if allow_nil && value.nil?
7
+ return true if allow_blank && value.blank?
8
+ %w(1 true t).include?(value)
9
+ end
10
+
11
+ def message(instance)
12
+ super || "must be accepted"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ module Mongomatic
2
+ module Validatable
3
+ class ValidatesAssociated < ValidationBase #:nodoc:
4
+ def valid?(instance)
5
+ Array(instance.send(attribute)).compact.map do |child|
6
+ child.valid?
7
+ end.all?
8
+ end
9
+
10
+ def message(instance)
11
+ super || "is invalid"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,25 @@
1
+ module Mongomatic
2
+ module Validatable
3
+ class ValidatesConfirmationOf < ValidationBase #:nodoc:
4
+ option :case_sensitive
5
+ default :case_sensitive => true
6
+
7
+ def initialize(klass, attribute, options={})
8
+ klass.class_eval { attr_accessor "#{attribute}_confirmation" }
9
+ super
10
+ end
11
+
12
+ def valid?(instance)
13
+ confirmation_value = instance.send("#{self.attribute}_confirmation")
14
+ return true if allow_nil && confirmation_value.nil?
15
+ return true if allow_blank && confirmation_value.blank?
16
+ return instance[self.attribute.to_s] == instance.send("#{self.attribute}_confirmation".to_sym) if case_sensitive
17
+ instance[self.attribute.to_s].to_s.casecmp(instance.send("#{self.attribute}_confirmation".to_sym).to_s) == 0
18
+ end
19
+
20
+ def message(instance)
21
+ super || "doesn't match confirmation"
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,16 @@
1
+ module Mongomatic
2
+ module Validatable
3
+ class ValidatesEach < ValidationBase #:nodoc:
4
+ required_option :logic
5
+
6
+ def valid?(instance)
7
+ instance.instance_eval(&logic)
8
+ true # return true so no error is added. should look in the future at doing this different.
9
+ end
10
+
11
+ def message(instance)
12
+ super || "is invalid"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,19 @@
1
+ module Mongomatic
2
+ module Validatable
3
+ class ValidatesExclusionOf < ValidationBase #:nodoc:
4
+ required_option :within
5
+
6
+ def valid?(instance)
7
+ value = instance.send(attribute)
8
+ return true if allow_nil && value.nil?
9
+ return true if allow_blank && value.blank?
10
+
11
+ !within.include?(value)
12
+ end
13
+
14
+ def message(instance)
15
+ super || "is reserved"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ module Mongomatic
2
+ module Validatable
3
+ class ValidatesFormatOf < ValidationBase #:nodoc:
4
+ required_option :with
5
+
6
+ def valid?(instance)
7
+ value = instance[self.attribute.to_s]
8
+ return true if allow_nil && value.nil?
9
+ return true if allow_blank && value.blank?
10
+ not (value.to_s =~ self.with).nil?
11
+ end
12
+
13
+ def message(instance)
14
+ super || "is invalid"
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ module Mongomatic
2
+ module Validatable
3
+ class ValidatesInclusionOf < ValidationBase
4
+ required_option :within
5
+
6
+ def valid?(instance)
7
+ value = instance.send(attribute)
8
+ return true if allow_nil && value.nil?
9
+ return true if allow_blank && value.blank?
10
+
11
+ within.include?(value)
12
+ end
13
+
14
+ def message(instance)
15
+ super || "is not in the list"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,32 @@
1
+ module Mongomatic
2
+ module Validatable
3
+ class ValidatesLengthOf < ValidationBase #:nodoc:
4
+ option :minimum, :maximum, :is, :within
5
+
6
+ def message(instance)
7
+ super || "is invalid"
8
+ end
9
+
10
+ def valid?(instance)
11
+ valid = true
12
+ value = instance[self.attribute.to_s]
13
+
14
+ if value.nil?
15
+ return true if allow_nil
16
+ value = ''
17
+ end
18
+
19
+ if value.blank?
20
+ return true if allow_blank
21
+ value = ''
22
+ end
23
+
24
+ valid &&= value.length <= maximum unless maximum.nil?
25
+ valid &&= value.length >= minimum unless minimum.nil?
26
+ valid &&= value.length == is unless is.nil?
27
+ valid &&= within.include?(value.length) unless within.nil?
28
+ valid
29
+ end
30
+ end
31
+ end
32
+ end