| |   |
| 0 | | module StrokeDB |
| 1 | | |
| 2 | | module Associations |
| 3 | | |
| 4 | | module HasManyAssociation |
| 5 | | attr_reader :association_owner, :association_slotname |
| 6 | | def new(slots={}) |
| 7 | | association_meta.constantize.new(association_owner.store, slots.merge({association_reference_slotname => association_owner})) |
| 8 | | end |
| 9 | | alias :build :new |
| 10 | | |
| 11 | | def create!(slots={}) |
| 12 | | new(slots).save! |
| 13 | | end |
| 14 | | |
| 15 | | def find(query={}) |
| 16 | | association_owner._has_many_association(association_slotname,query) |
| 17 | | end |
| 18 | | def <<(doc) |
| 19 | | doc.update_slots! association_reference_slotname => association_owner |
| 20 | | self |
| 21 | | end |
| 22 | | |
| 23 | | private |
| 24 | | |
| 25 | | def association_reference_slotname |
| 26 | | association_owner.meta["has_many_#{association_slotname}"][:reference_slotname] |
| 27 | | end |
| 28 | | |
| 29 | | def association_meta |
| 30 | | association_owner.meta["has_many_#{association_slotname}"][:meta] |
| 31 | | end |
| 32 | | |
| 33 | | end |
| 34 | | |
| 35 | | def has_many(slotname, opts={}, &block) |
| 36 | | opts = opts.stringify_keys |
| 37 | | |
| 38 | | reference_slotname = opts['foreign_reference'] |
| 39 | | through = opts['through'] || [] |
| 40 | | through = [through] unless through.is_a?(Array) |
| 41 | | meta = (through.shift || slotname).to_s.singularize.camelize |
| 42 | | query = opts['conditions'] || {} |
| 43 | | |
| 44 | | extend_with = opts['extend'] || block |
| 45 | | |
| 46 | | @meta_initialization_procs << Proc.new do |
| 47 | | case extend_with |
| 48 | | when Proc |
| 49 | | extend_with_proc = extend_with |
| 50 | | extend_with = "HasMany#{slotname.to_s.camelize}" |
| 51 | | const_set(extend_with, Module.new(&extend_with_proc)) |
| 52 | | extend_with = "#{self.name}::HasMany#{slotname.to_s.camelize}" |
| 53 | | when Module |
| 54 | | extend_with = extend_with.name |
| 55 | | when NilClass |
| 56 | | else |
| 57 | | raise "has_many extension should be either Module or Proc" |
| 58 | | end |
| 59 | | reference_slotname = reference_slotname || name.demodulize.tableize.singularize |
| 60 | | if name.index('::') # we're in namespaced meta |
| 61 | | _t = name.split('::') |
| 62 | | _t.pop |
| 63 | | _t << meta |
| 64 | | meta = _t.join('::') |
| 65 | | end |
| 66 | | @args.last.reverse_merge!({"has_many_#{slotname}" => { :reference_slotname => reference_slotname, :through => through, :meta => meta, :query => query, :extend_with => extend_with } }) |
| 67 | | define_method(slotname) do |
| 68 | | _has_many_association(slotname,{}) |
| 69 | | end |
| 70 | | |
| 71 | | end |
| 72 | | |
| 73 | | end |
| 74 | | |
| 75 | | private |
| 76 | | |
| 77 | | def initialize_associations |
| 78 | | define_method(:_has_many_association) do |slotname, additional_query| |
| 79 | | slot_has_many = meta["has_many_#{slotname}"] |
| 80 | | reference_slotname = slot_has_many[:reference_slotname] |
| 81 | | through = slot_has_many[:through] |
| 82 | | meta = slot_has_many[:meta] |
| 83 | | query = slot_has_many[:query] |
| 84 | | effective_query = query.merge(:meta => meta.constantize.document, reference_slotname => self).merge(additional_query) |
| 85 | | |
| 86 | | result = LazyArray.new.load_with do |lazy_array| |
| 87 | | store.search(effective_query).map do |d| |
| 88 | | begin |
| 89 | | through.each { |t| d = d.send(t) } |
| 90 | | rescue SlotNotFoundError |
| 91 | | d = nil |
| 92 | | end |
| 93 | | d |
| 94 | | end.compact |
| 95 | | end |
| 96 | | if extend_with = slot_has_many[:extend_with] |
| 97 | | result.extend(extend_with.constantize) |
| 98 | | end |
| 99 | | result.instance_variable_set(:@association_owner, self) |
| 100 | | result.instance_variable_set(:@association_slotname, slotname) |
| 101 | | result.extend(HasManyAssociation) |
| 102 | | result |
| 103 | | end |
| 104 | | end |
| 105 | | end |
| 106 | | end |
| toggle raw diff |
--- a/lib/strokedb/document/associations.rb
+++ /dev/null
@@ -1,107 +0,0 @@
-module StrokeDB
-
- module Associations
-
- module HasManyAssociation
- attr_reader :association_owner, :association_slotname
- def new(slots={})
- association_meta.constantize.new(association_owner.store, slots.merge({association_reference_slotname => association_owner}))
- end
- alias :build :new
-
- def create!(slots={})
- new(slots).save!
- end
-
- def find(query={})
- association_owner._has_many_association(association_slotname,query)
- end
- def <<(doc)
- doc.update_slots! association_reference_slotname => association_owner
- self
- end
-
- private
-
- def association_reference_slotname
- association_owner.meta["has_many_#{association_slotname}"][:reference_slotname]
- end
-
- def association_meta
- association_owner.meta["has_many_#{association_slotname}"][:meta]
- end
-
- end
-
- def has_many(slotname, opts={}, &block)
- opts = opts.stringify_keys
-
- reference_slotname = opts['foreign_reference']
- through = opts['through'] || []
- through = [through] unless through.is_a?(Array)
- meta = (through.shift || slotname).to_s.singularize.camelize
- query = opts['conditions'] || {}
-
- extend_with = opts['extend'] || block
-
- @meta_initialization_procs << Proc.new do
- case extend_with
- when Proc
- extend_with_proc = extend_with
- extend_with = "HasMany#{slotname.to_s.camelize}"
- const_set(extend_with, Module.new(&extend_with_proc))
- extend_with = "#{self.name}::HasMany#{slotname.to_s.camelize}"
- when Module
- extend_with = extend_with.name
- when NilClass
- else
- raise "has_many extension should be either Module or Proc"
- end
- reference_slotname = reference_slotname || name.demodulize.tableize.singularize
- if name.index('::') # we're in namespaced meta
- _t = name.split('::')
- _t.pop
- _t << meta
- meta = _t.join('::')
- end
- @args.last.reverse_merge!({"has_many_#{slotname}" => { :reference_slotname => reference_slotname, :through => through, :meta => meta, :query => query, :extend_with => extend_with } })
- define_method(slotname) do
- _has_many_association(slotname,{})
- end
-
- end
-
- end
-
- private
-
- def initialize_associations
- define_method(:_has_many_association) do |slotname, additional_query|
- slot_has_many = meta["has_many_#{slotname}"]
- reference_slotname = slot_has_many[:reference_slotname]
- through = slot_has_many[:through]
- meta = slot_has_many[:meta]
- query = slot_has_many[:query]
- effective_query = query.merge(:meta => meta.constantize.document, reference_slotname => self).merge(additional_query)
-
- result = LazyArray.new.load_with do |lazy_array|
- store.search(effective_query).map do |d|
- begin
- through.each { |t| d = d.send(t) }
- rescue SlotNotFoundError
- d = nil
- end
- d
- end.compact
- end
- if extend_with = slot_has_many[:extend_with]
- result.extend(extend_with.constantize)
- end
- result.instance_variable_set(:@association_owner, self)
- result.instance_variable_set(:@association_slotname, slotname)
- result.extend(HasManyAssociation)
- result
- end
- end
- end
-end |
| |   |
| 1 | module StrokeDB |
| 2 | |
| 3 | module Associations |
| 4 | |
| 5 | module HasManyAssociation |
| 6 | attr_reader :association_owner, :association_slotname |
| 7 | def new(slots={}) |
| 8 | association_meta.constantize.new(association_owner.store, slots.merge({association_reference_slotname => association_owner})) |
| 9 | end |
| 10 | alias :build :new |
| 11 | |
| 12 | def create!(slots={}) |
| 13 | new(slots).save! |
| 14 | end |
| 15 | |
| 16 | def find(query={}) |
| 17 | association_owner._has_many_association(association_slotname,query) |
| 18 | end |
| 19 | def <<(doc) |
| 20 | doc.update_slots! association_reference_slotname => association_owner |
| 21 | self |
| 22 | end |
| 23 | |
| 24 | private |
| 25 | |
| 26 | def association_reference_slotname |
| 27 | association_owner.meta["has_many_#{association_slotname}"][:reference_slotname] |
| 28 | end |
| 29 | |
| 30 | def association_meta |
| 31 | association_owner.meta["has_many_#{association_slotname}"][:meta] |
| 32 | end |
| 33 | |
| 34 | end |
| 35 | |
| 36 | def has_many(slotname, opts={}, &block) |
| 37 | opts = opts.stringify_keys |
| 38 | |
| 39 | reference_slotname = opts['foreign_reference'] |
| 40 | through = opts['through'] || [] |
| 41 | through = [through] unless through.is_a?(Array) |
| 42 | meta = (through.shift || slotname).to_s.singularize.camelize |
| 43 | query = opts['conditions'] || {} |
| 44 | |
| 45 | extend_with = opts['extend'] || block |
| 46 | |
| 47 | @meta_initialization_procs << Proc.new do |
| 48 | case extend_with |
| 49 | when Proc |
| 50 | extend_with_proc = extend_with |
| 51 | extend_with = "HasMany#{slotname.to_s.camelize}" |
| 52 | const_set(extend_with, Module.new(&extend_with_proc)) |
| 53 | extend_with = "#{self.name}::HasMany#{slotname.to_s.camelize}" |
| 54 | when Module |
| 55 | extend_with = extend_with.name |
| 56 | when NilClass |
| 57 | else |
| 58 | raise "has_many extension should be either Module or Proc" |
| 59 | end |
| 60 | reference_slotname = reference_slotname || name.demodulize.tableize.singularize |
| 61 | if name.index('::') # we're in namespaced meta |
| 62 | _t = name.split('::') |
| 63 | _t.pop |
| 64 | _t << meta |
| 65 | meta = _t.join('::') |
| 66 | end |
| 67 | @args.last.reverse_merge!({"has_many_#{slotname}" => { :reference_slotname => reference_slotname, :through => through, :meta => meta, :query => query, :extend_with => extend_with } }) |
| 68 | define_method(slotname) do |
| 69 | _has_many_association(slotname,{}) |
| 70 | end |
| 71 | |
| 72 | end |
| 73 | |
| 74 | end |
| 75 | |
| 76 | private |
| 77 | |
| 78 | def initialize_associations |
| 79 | define_method(:_has_many_association) do |slotname, additional_query| |
| 80 | slot_has_many = meta["has_many_#{slotname}"] |
| 81 | reference_slotname = slot_has_many[:reference_slotname] |
| 82 | through = slot_has_many[:through] |
| 83 | meta = slot_has_many[:meta] |
| 84 | query = slot_has_many[:query] |
| 85 | effective_query = query.merge(:meta => meta.constantize.document, reference_slotname => self).merge(additional_query) |
| 86 | |
| 87 | result = LazyArray.new.load_with do |lazy_array| |
| 88 | store.search(effective_query).map do |d| |
| 89 | begin |
| 90 | through.each { |t| d = d.send(t) } |
| 91 | rescue SlotNotFoundError |
| 92 | d = nil |
| 93 | end |
| 94 | d |
| 95 | end.compact |
| 96 | end |
| 97 | if extend_with = slot_has_many[:extend_with] |
| 98 | result.extend(extend_with.constantize) |
| 99 | end |
| 100 | result.instance_variable_set(:@association_owner, self) |
| 101 | result.instance_variable_set(:@association_slotname, slotname) |
| 102 | result.extend(HasManyAssociation) |
| 103 | result |
| 104 | end |
| 105 | end |
| 106 | end |
| 107 | end |
| toggle raw diff |
--- /dev/null
+++ b/lib/strokedb/document/dsl/associations.rb
@@ -0,0 +1,107 @@
+module StrokeDB
+
+ module Associations
+
+ module HasManyAssociation
+ attr_reader :association_owner, :association_slotname
+ def new(slots={})
+ association_meta.constantize.new(association_owner.store, slots.merge({association_reference_slotname => association_owner}))
+ end
+ alias :build :new
+
+ def create!(slots={})
+ new(slots).save!
+ end
+
+ def find(query={})
+ association_owner._has_many_association(association_slotname,query)
+ end
+ def <<(doc)
+ doc.update_slots! association_reference_slotname => association_owner
+ self
+ end
+
+ private
+
+ def association_reference_slotname
+ association_owner.meta["has_many_#{association_slotname}"][:reference_slotname]
+ end
+
+ def association_meta
+ association_owner.meta["has_many_#{association_slotname}"][:meta]
+ end
+
+ end
+
+ def has_many(slotname, opts={}, &block)
+ opts = opts.stringify_keys
+
+ reference_slotname = opts['foreign_reference']
+ through = opts['through'] || []
+ through = [through] unless through.is_a?(Array)
+ meta = (through.shift || slotname).to_s.singularize.camelize
+ query = opts['conditions'] || {}
+
+ extend_with = opts['extend'] || block
+
+ @meta_initialization_procs << Proc.new do
+ case extend_with
+ when Proc
+ extend_with_proc = extend_with
+ extend_with = "HasMany#{slotname.to_s.camelize}"
+ const_set(extend_with, Module.new(&extend_with_proc))
+ extend_with = "#{self.name}::HasMany#{slotname.to_s.camelize}"
+ when Module
+ extend_with = extend_with.name
+ when NilClass
+ else
+ raise "has_many extension should be either Module or Proc"
+ end
+ reference_slotname = reference_slotname || name.demodulize.tableize.singularize
+ if name.index('::') # we're in namespaced meta
+ _t = name.split('::')
+ _t.pop
+ _t << meta
+ meta = _t.join('::')
+ end
+ @args.last.reverse_merge!({"has_many_#{slotname}" => { :reference_slotname => reference_slotname, :through => through, :meta => meta, :query => query, :extend_with => extend_with } })
+ define_method(slotname) do
+ _has_many_association(slotname,{})
+ end
+
+ end
+
+ end
+
+ private
+
+ def initialize_associations
+ define_method(:_has_many_association) do |slotname, additional_query|
+ slot_has_many = meta["has_many_#{slotname}"]
+ reference_slotname = slot_has_many[:reference_slotname]
+ through = slot_has_many[:through]
+ meta = slot_has_many[:meta]
+ query = slot_has_many[:query]
+ effective_query = query.merge(:meta => meta.constantize.document, reference_slotname => self).merge(additional_query)
+
+ result = LazyArray.new.load_with do |lazy_array|
+ store.search(effective_query).map do |d|
+ begin
+ through.each { |t| d = d.send(t) }
+ rescue SlotNotFoundError
+ d = nil
+ end
+ d
+ end.compact
+ end
+ if extend_with = slot_has_many[:extend_with]
+ result.extend(extend_with.constantize)
+ end
+ result.instance_variable_set(:@association_owner, self)
+ result.instance_variable_set(:@association_slotname, slotname)
+ result.extend(HasManyAssociation)
+ result
+ end
+ end
+ end
+end |
| |   |
| 1 | require 'ostruct' |
| 2 | |
| 3 | module StrokeDB |
| 4 | module Validations |
| 5 | ERROR_MESSAGES = { |
| 6 | :should_be_present => '#{meta}\'s #{slotname} should be present on #{on}', |
| 7 | :invalid_type => '#{meta}\'s #{slotname} should be of type #{validation_type}', |
| 8 | :already_exists => 'A document with a #{slotname} of #{slotvalue} already exists', |
| 9 | :not_included => 'Value of #{slotname} is not included in the list', |
| 10 | :not_excluded => 'Value of #{slotname} is reserved', |
| 11 | :invalid_format => 'Value of #{slotname} should match #{slotvalue}', |
| 12 | :not_confirmed => '#{meta}\'s #{slotname} doesn\'t match confirmation', |
| 13 | :not_accepted => '#{slotname} must be accepted', |
| 14 | :wrong_length => '#{slotname} has the wrong length (should be %d characters)', |
| 15 | :too_short => '#{slotname} is too short (minimum is %d characters)', |
| 16 | :too_long => '#{slotname} is too long (maximum is %d characters)', |
| 17 | :invalid => '#{slotname} is invalid', |
| 18 | :must_be_integer => '#{slotname} must be integer', |
| 19 | :not_a_number => '#{slotname} is not a number', |
| 20 | }.freeze unless defined? ERROR_MESSAGES |
| 21 | |
| 22 | # Validates that the specified slot exists in the document. Happens by default on save. Example: |
| 23 | # |
| 24 | # Person = Meta.new do |
| 25 | # validates_presence_of :first_name |
| 26 | # end |
| 27 | # |
| 28 | # The first_name slot must be in the document. |
| 29 | # |
| 30 | # Configuration options: |
| 31 | # * <tt>message</tt> - A custom error message (default is: "should be present on ...") |
| 32 | # * <tt>on</tt> - Specifies when this validation is active (default is :save, other options :create, :update) |
| 33 | # * <tt>if</tt> - Specifies a method or slot name to call to determine if the validation should |
| 34 | # occur (e.g. :if => :allow_validation, or :if => 'signup_step_less_than_three'). The |
| 35 | # method result or slot should be equal to a true or false value. |
| 36 | # * <tt>unless</tt> - Specifies a method or slot name to call to determine if the validation should |
| 37 | # not occur (e.g. :unless => :skip_validation, or :unless => 'signup_step_less_than_three'). The |
| 38 | # method result or slot should be equal to a true or false value. |
| 39 | def validates_presence_of(slotname, opts={}) |
| 40 | register_validation("presence_of", slotname, opts, :should_be_present) |
| 41 | end |
| 42 | |
| 43 | # Validates that the specified slot value has a specific type. Happens by default on save. Example: |
| 44 | # |
| 45 | # Person = Meta.new do |
| 46 | # validates_type_of :first_name, :as => :string |
| 47 | # end |
| 48 | # |
| 49 | # The first_name value for each Person must be unique. |
| 50 | # |
| 51 | # Configuration options: |
| 52 | # * <tt>message</tt> - A custom error message (default is: "document with value already exists") |
| 53 | # * <tt>on</tt> - Specifies when this validation is active (default is :save, other options :create, :update) |
| 54 | # * <tt>allow_nil</tt> - If set to true, skips this validation if the attribute is null (default is: false) |
| 55 | # * <tt>if</tt> - Specifies a method or slot name to call to determine if the validation should |
| 56 | # occur (e.g. :if => :allow_validation, or :if => 'signup_step_less_than_three'). The |
| 57 | # method result or slot should be equal to a true or false value. |
| 58 | # * <tt>unless</tt> - Specifies a method or slot name to call to determine if the validation should |
| 59 | # not occur (e.g. :unless => :skip_validation, or :unless => 'signup_step_less_than_three'). The |
| 60 | # method result or slot should be equal to a true or false value. |
| 61 | # |
| 62 | # === Warning |
| 63 | # When the slot doesn't exist, validation gets skipped. |
| 64 | def validates_type_of(slotname, opts={}) |
| 65 | register_validation("type_of", slotname, opts, :invalid_type) do |opts| |
| 66 | raise ArgumentError, "validates_type_of requires :as => type" unless type = opts['as'] |
| 67 | |
| 68 | { |
| 69 | :validation_type => type.to_s.camelize, |
| 70 | :allow_nil => !!opts['allow_nil'] |
| 71 | } |
| 72 | end |
| 73 | end |
| 74 | |
| 75 | # Validates that the specified slot value is unique in the store |
| 76 | # |
| 77 | # Person = Meta.new do |
| 78 | # validates_uniqueness_of :first_name |
| 79 | # end |
| 80 | # |
| 81 | # The first_name slot must be in the document. |
| 82 | # |
| 83 | # Configuration options: |
| 84 | # * <tt>message</tt> - A custom error message (default is: "A document with a ... of ... already exists") |
| 85 | # * <tt>on</tt> - Specifies when this validation is active (default is :save, other options :create, :update) |
| 86 | # * <tt>case_sensitive</tt> - Looks for an exact match. Ignored by non-text columns (true by default). NOT YET IMPLEMENTED |
| 87 | # * <tt>allow_nil</tt> - If set to true, skips this validation if the attribute is null (default is: false) |
| 88 | # * <tt>allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is: false) |
| 89 | # * <tt>if</tt> - Specifies a method or slot name to call to determine if the validation should |
| 90 | # occur (e.g. :if => :allow_validation, or :if => 'signup_step_less_than_three'). The |
| 91 | # method result or slot should be equal to a true or false value. |
| 92 | # * <tt>unless</tt> - Specifies a method or slot name to call to determine if the validation should |
| 93 | # not occur (e.g. :unless => :skip_validation, or :unless => 'signup_step_less_than_three'). The |
| 94 | # method result or slot should be equal to a true or false value. |
| 95 | def validates_uniqueness_of(slotname, opts={}) |
| 96 | register_validation("uniqueness_of", slotname, opts, :already_exists) do |opts| |
| 97 | { :allow_nil => !!opts['allow_nil'], :allow_blank => !!opts['allow_blank'] } |
| 98 | end |
| 99 | end |
| 100 | |
| 101 | # Validates whether the value of the specified slot is available in a particular enumerable object. |
| 102 | # |
| 103 | # Person = Meta.new do |
| 104 | # validates_inclusion_of :gender, :in => %w( m f ), :message => "woah! what are you then!??!!" |
| 105 | # validates_inclusion_of :age, :in => 0..99 |
| 106 | # validates_inclusion_of :format, :in => %w( jpg gif png ), :message => 'extension #{slotvalue} is not included in the list' |
| 107 | # end |
| 108 | # |
| 109 | # Configuration options: |
| 110 | # * <tt>in</tt> - An enumerable object of available items |
| 111 | # * <tt>message</tt> - Specifies a customer error message (default is: "is |
| 112 | # not included in the list") |
| 113 | # * <tt>allow_nil</tt> - If set to true, skips this validation if the slot is null (default is: false) |
| 114 | # * <tt>allow_blank</tt> - If set to true, skips this validation if the slot is blank (default is: false) |
| 115 | # * <tt>if</tt> - Specifies a method or slot name to call to determine if the validation should |
| 116 | # occur (e.g. :if => :allow_validation, or :if => 'signup_step_less_than_three'). The |
| 117 | # method result or slot should be equal to a true or false value. |
| 118 | # * <tt>unless</tt> - Specifies a method or slot name to call to determine if the validation should |
| 119 | # not occur (e.g. :unless => :skip_validation, or :unless => 'signup_step_less_than_three'). The |
| 120 | # method result or slot should be equal to a true or false value. |
| 121 | def validates_inclusion_of(slotname, opts={}) |
| 122 | register_validation("inclusion_of", slotname, opts, :not_included) do |opts| |
| 123 | raise ArgumentError, "validates_inclusion_of requires :in set" unless opts['in'] |
| 124 | raise ArgumentError, "object must respond to the method include?" unless opts['in'].respond_to? :include? |
| 125 | |
| 126 | { |
| 127 | :in => opts['in'], |
| 128 | :allow_nil => !!opts['allow_nil'], |
| 129 | :allow_blank => !!opts['allow_blank'] |
| 130 | } |
| 131 | end |
| 132 | end |
| 133 | |
| 134 | # Validates that the value of the specified slot is not in a particular enumerable object. |
| 135 | # |
| 136 | # Person = Meta.new do |
| 137 | # validates_exclusion_of :username, :in => %w( admin superuser ), :message => "You don't belong here" |
| 138 | # validates_exclusion_of :age, :in => 30..60, :message => "This site is only for under 30 and over 60" |
| 139 | # validates_exclusion_of :format, :in => %w( mov avi ), :message => 'extension #{slotvalue} is not allowed' |
| 140 | # end |
| 141 | # |
| 142 | # Configuration options: |
| 143 | # * <tt>in</tt> - An enumerable object of items that the value shouldn't be part of |
| 144 | # * <tt>message</tt> - Specifies a customer error message (default is: "is reserved") |
| 145 | # * <tt>allow_nil</tt> - If set to true, skips this validation if the attribute is null (default is: false) |
| 146 | # * <tt>allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is: false) |
| 147 | # * <tt>if</tt> - Specifies a method or slot name to call to determine if the validation should |
| 148 | # occur (e.g. :if => :allow_validation, or :if => 'signup_step_less_than_three'). The |
| 149 | # method result or slot should be equal to a true or false value. |
| 150 | # * <tt>unless</tt> - Specifies a method or slot name to call to determine if the validation should |
| 151 | # not occur (e.g. :unless => :skip_validation, or :unless => 'signup_step_less_than_three'). The |
| 152 | # method result or slot should be equal to a true or false value. |
| 153 | def validates_exclusion_of(slotname, opts={}) |
| 154 | register_validation("exclusion_of", slotname, opts, :not_excluded) do |opts| |
| 155 | raise ArgumentError, "validates_exclusion_of requires :in set" unless opts['in'] |
| 156 | raise ArgumentError, "object must respond to the method include?" unless opts['in'].respond_to? :include? |
| 157 | |
| 158 | { |
| 159 | :in => opts['in'], |
| 160 | :allow_nil => !!opts['allow_nil'], |
| 161 | :allow_blank => !!opts['allow_blank'] |
| 162 | } |
| 163 | end |
| 164 | end |
| 165 | |
| 166 | # Validates whether the value of the specified attribute is numeric by trying to convert it to |
| 167 | # a float with Kernel.Float (if <tt>only_integer</tt> is false) or applying it to the regular expression |
| 168 | # <tt>/\A[\+\-]?\d+\Z/</tt> (if <tt>only_integer</tt> is set to true). |
| 169 | # |
| 170 | # Item = Meta.new do |
| 171 | # validates_numericality_of :price |
| 172 | # end |
| 173 | # |
| 174 | # * <tt>message</tt> - A custom error message (default is: "is not a number") |
| 175 | # * <tt>on</tt> Specifies when this validation is active (default is :save, other options :create, :update) |
| 176 | # * <tt>only_integer</tt> Specifies whether the value has to be an integer, e.g. an integral value (default is false) |
| 177 | # * <tt>allow_nil</tt> Skip validation if attribute is nil (default is |
| 178 | # false). Notice that for fixnum and float columns empty strings are converted to nil |
| 179 | # * <tt>greater_than</tt> Specifies the value must be greater than the supplied value |
| 180 | # * <tt>greater_than_or_equal_to</tt> Specifies the value must be greater than or equal the supplied value |
| 181 | # * <tt>equal_to</tt> Specifies the value must be equal to the supplied value |
| 182 | # * <tt>less_than</tt> Specifies the value must be less than the supplied value |
| 183 | # * <tt>less_than_or_equal_to</tt> Specifies the value must be less than or equal the supplied value |
| 184 | # * <tt>odd</tt> Specifies the value must be an odd number |
| 185 | # * <tt>even</tt> Specifies the value must be an even number |
| 186 | # * <tt>if</tt> - Specifies a method or slot name to call to determine if the validation should |
| 187 | # occur (e.g. :if => :allow_validation, or :if => 'signup_step_less_than_three'). The |
| 188 | # method result or slot should be equal to a true or false value. |
| 189 | # * <tt>unless</tt> - Specifies a method or slot name to call to determine if the validation should |
| 190 | # not occur (e.g. :unless => :skip_validation, or :unless => 'signup_step_less_than_three'). The |
| 191 | # method result or slot should be equal to a true or false value. |
| 192 | NUMERICALITY_CHECKS = { 'greater_than' => :>, 'greater_than_or_equal_to' => :>=, |
| 193 | 'equal_to' => :==, 'less_than' => :<, 'less_than_or_equal_to' => :<=, |
| 194 | 'odd' => :odd?, 'even' => :even? }.freeze |
| 195 | |
| 196 | def validates_numericality_of(slotname, opts={}) |
| 197 | register_validation("numericality_of", slotname, opts, nil) do |opts| |
| 198 | numeric_checks = opts.reject { |key, val| !NUMERICALITY_CHECKS.include? key } |
| 199 | |
| 200 | %w(odd even).each do |o| |
| 201 | raise ArgumentError, ":#{o} must be set to true if set at all" if opts.include?(o) && opts[o] != true |
| 202 | end |
| 203 | |
| 204 | (numeric_checks.keys - %w(odd even)).each do |option| |
| 205 | raise ArgumentError, "#{option} must be a number" unless opts[option].is_a? Numeric |
| 206 | end |
| 207 | |
| 208 | { |
| 209 | :only_integer => opts['only_integer'], |
| 210 | :numeric_checks => numeric_checks, |
| 211 | :allow_nil => !!opts['allow_nil'] |
| 212 | } |
| 213 | end |
| 214 | end |
| 215 | |
| 216 | # Validates whether the value of the specified attribute is of the correct |
| 217 | # form by matching it against the regular expression provided. |
| 218 | # |
| 219 | # Person = Meta.new do |
| 220 | # validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, :on => :create |
| 221 | # end |
| 222 | # |
| 223 | # Note: use \A and \Z to match the start and end of the string, ^ and $ match the start/end of a line. |
| 224 | # |
| 225 | # A regular expression must be provided or else an exception will be |
| 226 | # raised. |
| 227 | # |
| 228 | # Configuration options: |
| 229 | # * <tt>message</tt> - A custom error message (default is: "is invalid") |
| 230 | # * <tt>with</tt> - The regular expression used to validate the format with (note: must be supplied!) |
| 231 | # * <tt>on</tt> Specifies when this validation is active (default is :save, other options :create, :update) |
| 232 | # * <tt>if</tt> - Specifies a method or slot name to call to determine if the validation should |
| 233 | # occur (e.g. :if => :allow_validation, or :if => 'signup_step_less_than_three'). The |
| 234 | # method result or slot should be equal to a true or false value. |
| 235 | # * <tt>unless</tt> - Specifies a method or slot name to call to determine if the validation should |
| 236 | # not occur (e.g. :unless => :skip_validation, or :unless => 'signup_step_less_than_three'). The |
| 237 | # method result or slot should be equal to a true or false value. |
| 238 | def validates_format_of(slotname, opts={}) |
| 239 | register_validation("format_of", slotname, opts, :invalid_format) do |opts| |
| 240 | unless regexp = opts['with'].is_a?(Regexp) |
| 241 | raise ArgumentError, "validates_format_of requires :with => regexp" |
| 242 | end |
| 243 | { :with => opts['with'] } |
| 244 | end |
| 245 | end |
| 246 | |
| 247 | # Encapsulates the pattern of wanting to validate a password or email |
| 248 | # address field with a confirmation. Example: |
| 249 | # |
| 250 | # Model: |
| 251 | # Person = Meta.new do |
| 252 | # validates_confirmation_of :password |
| 253 | # validates_confirmation_of :email_address, :message => "should match confirmation" |
| 254 | # end |
| 255 | # |
| 256 | # View: |
| 257 | # <%= password_field "person", "password" %> |
| 258 | # <%= password_field "person", "password_confirmation" %> |
| 259 | # |
| 260 | # The added +password_confirmation+ slot is virtual; it exists only as |
| 261 | # an in-memory slot for validating the password. To achieve this, the |
| 262 | # validation adds accessors to the model for the confirmation slot. |
| 263 | # NOTE: This check is performed only if +password_confirmation+ is not nil, |
| 264 | # and by default only on save. To require confirmation, make sure to add a |
| 265 | # presence check for the confirmation attribute: |
| 266 | # |
| 267 | # validates_presence_of :password_confirmation, :if => :password_changed? |
| 268 | # |
| 269 | # Configuration options: |
| 270 | # * <tt>message</tt> - A custom error message (default is: "doesn't match confirmation") |
| 271 | # * <tt>on</tt> - Specifies when this validation is active (default is :save, other options :create, :update) |
| 272 | # * <tt>if</tt> - Specifies a method or slot name to call to determine if the validation should |
| 273 | # occur (e.g. :if => :allow_validation, or :if => 'signup_step_less_than_three'). The |
| 274 | # method result or slot should be equal to a true or false value. |
| 275 | # * <tt>unless</tt> - Specifies a method or slot name to call to determine if the validation should |
| 276 | # not occur (e.g. :unless => :skip_validation, or :unless => 'signup_step_less_than_three'). The |
| 277 | # method result or slot should be equal to a true or false value. |
| 278 | def validates_confirmation_of(slotname, opts = {}) |
| 279 | register_validation("confirmation_of", slotname, opts, :not_confirmed) |
| 280 | |
| 281 | virtualizes(slotname.to_s + "_confirmation") |
| 282 | end |
| 283 | |
| 284 | # Encapsulates the pattern of wanting to validate the acceptance of a terms |
| 285 | # of service check box (or similar agreement). Example: |
| 286 | # |
| 287 | # Person = Meta.new do |