Guest User

Untitled

a guest
Feb 19th, 2018
67
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.73 KB | None | 0 0
  1. module ActiveModel
  2. module Validations
  3. def self.included(base)
  4. base.class_eval do
  5. @validation_stack = {
  6. :any => [],
  7. :create => [],
  8. :update => []
  9. }.freeze
  10.  
  11. include InstanceMethods
  12. extend ClassMethods
  13. end
  14. end
  15.  
  16. module InstanceMethods
  17. NEW_RECORD_NOT_IMPLEMENTED = "You need to implement a new_record? instance method in your class."
  18. # The list of errors.
  19. def errors
  20. @errors ||= Errors.new
  21. end
  22.  
  23. # Runs all the validations
  24. def validate
  25. errors.clear
  26. self.class.validate(self)
  27. end
  28.  
  29. # Checks if there are any errors
  30. def valid?
  31. errors.empty? && errors.base.empty?
  32. end
  33.  
  34. # Should we run validations for create or update?
  35. def validation_scope
  36. raise NoMethodError, NEW_RECORD_NOT_IMPLEMENTED unless respond_to?(:new_record?)
  37. new_record? ? :create : :update
  38. end
  39. end
  40.  
  41. module ClassMethods
  42. MESSAGES = {
  43. :blank => "can not be blank",
  44. :format => "is of invalid format"
  45. }
  46.  
  47. # Runs the actual validations, one by one.
  48. def validate(instance)
  49. # Runs the validations for the current validation scope (create or update)
  50. @validation_stack[instance.validation_scope].each {|validation| validation.run(instance) }
  51.  
  52. # Runs the validations that runs on both create and update
  53. @validation_stack[:any].each {|validation| validation.run(instance) }
  54. end
  55.  
  56. # Validates that the value of an attribute isn't "blank?".
  57. #
  58. # Post.validates_presence_of :title
  59. def validates_presence_of(*args)
  60. options = args.options :message => MESSAGES[:blank]
  61.  
  62. validate_attributes(args, options) do |record, attribute, value|
  63. record.errors[attribute] << options[:message] if value.blank?
  64. end
  65. end
  66.  
  67. # Regular expression matching.
  68. #
  69. # User.validates_format_of :email, :with => /^.+@.+\..+$/
  70. def validates_format_of(*args)
  71. options = args.options :message => MESSAGES[:format]
  72. options.assert_required_keys :with
  73.  
  74. validate_attributes(args, options) do |record, attribute, value|
  75. record.errors[attribute] << options[:message] unless value =~ options[:with]
  76. end
  77. end
  78.  
  79. def validates_length_of(*args)
  80. # TODO: :minimum and :maximum, to complement :length
  81. options = args.options :message => MESSAGES[:length]
  82. options.assert_required_keys :length
  83.  
  84. validate_attributes(args, options) do |record, attribute, value|
  85. record.errors[attribute] << options[:message] if value < options[:length]
  86. end
  87. end
  88.  
  89. def validates_numericality_of(*args)
  90. # TODO: Use something other than Integer(value) and rescue.
  91. # NOTE: This method is pretty nasty in AR. I think AR should handle the oddities in
  92. # order to make the change backwards compatible.
  93. options = args.options :message => MESSAGES[:numericality]
  94.  
  95. validate_attributes(args, options) do |record, attribute, value|
  96. record.errors[attribute] << options[:message] unless Integer(value) rescue(false)
  97. end
  98. end
  99.  
  100. # Uses Enumerable#in? to determine if the value matches the items in the :in option.
  101. #
  102. # User.validates_inclusion_of :gener, :in => %(male female)
  103. def validates_inclusion_of(*args)
  104. options = args.options :message => MESSAGES[:inclusion]
  105. options.assert_required_keys :in
  106.  
  107. validate_attributes(args, options) do |record, attribute, value|
  108. record.errors[attribute] << options[:message] unless options[:in].include?(value)
  109. end
  110. end
  111.  
  112. # The opposite of validates_inclusion_of.
  113. def validates_exclusion_of(*args)
  114. options = args.options :message => MESSAGES[:exclution]
  115. options.assert_required_keys :in
  116.  
  117. validate_attributes(args, options) do |record, attribute, value|
  118. record.errors[attribute] << options[:message] unless options[:in].include?(value)
  119. end
  120. end
  121.  
  122. # Validate attribute names with the block passed as the actual validation.
  123. #
  124. # validate_attributes(:title, :body) do |record, attribute, value|
  125. # record.errors[atribute] << "must no suck" if record.sucks? and value.is_silly?
  126. # end
  127. def validate_attributes(attributes, options, &proc)
  128. options.reverse_merge!(:on => :any)
  129. @validation_stack[options[:on]] << Validation.new(attributes, proc)
  130. end
  131. end
  132.  
  133. # Instances of this class is stored in the validation stack.
  134. # TODO: Add support for :if and :unless (symbols for method names and procs getting the
  135. # instance yielded to them)
  136. class Validation
  137. def initialize(attributes, proc)
  138. @attributes = attributes
  139. @proc = proc
  140. end
  141.  
  142. def run(instance)
  143. @attributes.each {|attribute| @proc.call(instance, attribute, instance.send(attribute)) }
  144. end
  145. end
  146.  
  147. class Errors < Hash
  148. # These errors are instance-wide errors rather than attribute specific ones.
  149. attr_reader :base
  150.  
  151. # We want errors[:foo] to yield an empty array. See the tests.
  152. def initialize
  153. super {|hash, key| hash[key] = [] }
  154. @base = []
  155. end
  156.  
  157. # As we can potentially have empty arrays from calling errors[:foo], we have to
  158. # check if the values are all empty arrays as well.
  159. def empty?
  160. values.all?(&:empty?)
  161. end
  162. end
  163. end
  164. end
Add Comment
Please, Sign In to add comment