datamapper 0.2.3 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/example.rb +5 -5
- data/lib/data_mapper/adapters/abstract_adapter.rb +2 -2
- data/lib/data_mapper/adapters/data_object_adapter.rb +141 -147
- data/lib/data_mapper/adapters/mysql_adapter.rb +14 -1
- data/lib/data_mapper/adapters/postgresql_adapter.rb +123 -18
- data/lib/data_mapper/adapters/sql/coersion.rb +21 -9
- data/lib/data_mapper/adapters/sql/commands/load_command.rb +36 -19
- data/lib/data_mapper/adapters/sql/mappings/column.rb +111 -17
- data/lib/data_mapper/adapters/sql/mappings/schema.rb +27 -0
- data/lib/data_mapper/adapters/sql/mappings/table.rb +256 -29
- data/lib/data_mapper/adapters/sqlite3_adapter.rb +93 -8
- data/lib/data_mapper/associations/belongs_to_association.rb +53 -54
- data/lib/data_mapper/associations/has_and_belongs_to_many_association.rb +157 -25
- data/lib/data_mapper/associations/has_many_association.rb +45 -15
- data/lib/data_mapper/associations/has_n_association.rb +79 -20
- data/lib/data_mapper/associations/has_one_association.rb +2 -2
- data/lib/data_mapper/associations/reference.rb +1 -1
- data/lib/data_mapper/auto_migrations.rb +40 -0
- data/lib/data_mapper/base.rb +201 -98
- data/lib/data_mapper/context.rb +16 -10
- data/lib/data_mapper/database.rb +22 -11
- data/lib/data_mapper/dependency_queue.rb +28 -0
- data/lib/data_mapper/embedded_value.rb +61 -17
- data/lib/data_mapper/property.rb +4 -0
- data/lib/data_mapper/support/active_record_impersonation.rb +13 -5
- data/lib/data_mapper/support/errors.rb +5 -0
- data/lib/data_mapper/support/serialization.rb +8 -4
- data/lib/data_mapper/validatable_extensions/errors.rb +12 -0
- data/lib/data_mapper/validatable_extensions/macros.rb +7 -0
- data/lib/data_mapper/validatable_extensions/validatable_instance_methods.rb +62 -0
- data/lib/data_mapper/validatable_extensions/validation_base.rb +18 -0
- data/lib/data_mapper/validatable_extensions/validations/formats/email.rb +43 -0
- data/lib/data_mapper/validatable_extensions/validations/validates_acceptance_of.rb +7 -0
- data/lib/data_mapper/validatable_extensions/validations/validates_confirmation_of.rb +7 -0
- data/lib/data_mapper/validatable_extensions/validations/validates_each.rb +7 -0
- data/lib/data_mapper/validatable_extensions/validations/validates_format_of.rb +28 -0
- data/lib/data_mapper/validatable_extensions/validations/validates_length_of.rb +15 -0
- data/lib/data_mapper/validatable_extensions/validations/validates_numericality_of.rb +7 -0
- data/lib/data_mapper/validatable_extensions/validations/validates_presence_of.rb +7 -0
- data/lib/data_mapper/validatable_extensions/validations/validates_true_for.rb +7 -0
- data/lib/data_mapper/validatable_extensions/validations/validates_uniqueness_of.rb +33 -0
- data/lib/data_mapper/validations.rb +20 -0
- data/lib/data_mapper.rb +39 -34
- data/performance.rb +24 -18
- data/plugins/dataobjects/do_rb +0 -0
- data/rakefile.rb +12 -2
- data/spec/active_record_impersonation_spec.rb +133 -0
- data/spec/acts_as_tree_spec.rb +25 -9
- data/spec/associations_spec.rb +124 -4
- data/spec/attributes_spec.rb +13 -0
- data/spec/auto_migrations_spec.rb +44 -0
- data/spec/base_spec.rb +189 -1
- data/spec/column_spec.rb +85 -7
- data/spec/conditions_spec.rb +2 -2
- data/spec/dependency_spec.rb +25 -0
- data/spec/embedded_value_spec.rb +123 -3
- data/spec/fixtures/animals.yaml +1 -0
- data/spec/fixtures/careers.yaml +5 -0
- data/spec/fixtures/comments.yaml +1 -0
- data/spec/fixtures/people.yaml +14 -9
- data/spec/fixtures/projects.yaml +4 -0
- data/spec/fixtures/sections.yaml +5 -0
- data/spec/fixtures/serializers.yaml +6 -0
- data/spec/fixtures/users.yaml +1 -0
- data/spec/load_command_spec.rb +5 -4
- data/spec/mock_adapter.rb +2 -2
- data/spec/models/animal.rb +2 -1
- data/spec/models/animals_exhibit.rb +2 -2
- data/spec/models/career.rb +6 -0
- data/spec/models/comment.rb +4 -0
- data/spec/models/exhibit.rb +4 -0
- data/spec/models/person.rb +3 -13
- data/spec/models/project.rb +1 -1
- data/spec/models/serializer.rb +3 -0
- data/spec/models/user.rb +4 -0
- data/spec/models/zoo.rb +8 -1
- data/spec/natural_key_spec.rb +36 -0
- data/spec/paranoia_spec.rb +36 -0
- data/spec/property_spec.rb +70 -0
- data/spec/schema_spec.rb +10 -2
- data/spec/serialization_spec.rb +6 -3
- data/spec/serialize_spec.rb +19 -0
- data/spec/single_table_inheritance_spec.rb +7 -1
- data/spec/spec_helper.rb +26 -8
- data/spec/table_spec.rb +33 -0
- data/spec/validates_confirmation_of_spec.rb +20 -4
- data/spec/validates_format_of_spec.rb +22 -8
- data/spec/validates_length_of_spec.rb +26 -13
- data/spec/validates_uniqueness_of_spec.rb +18 -5
- data/spec/validations_spec.rb +55 -10
- data/tasks/fixtures.rb +13 -7
- metadata +189 -153
- data/lib/data_mapper/validations/confirmation_validator.rb +0 -53
- data/lib/data_mapper/validations/contextual_validations.rb +0 -50
- data/lib/data_mapper/validations/format_validator.rb +0 -85
- data/lib/data_mapper/validations/formats/email.rb +0 -78
- data/lib/data_mapper/validations/generic_validator.rb +0 -22
- data/lib/data_mapper/validations/length_validator.rb +0 -76
- data/lib/data_mapper/validations/required_field_validator.rb +0 -41
- data/lib/data_mapper/validations/unique_validator.rb +0 -56
- data/lib/data_mapper/validations/validation_errors.rb +0 -37
- data/lib/data_mapper/validations/validation_helper.rb +0 -77
- data/plugins/dataobjects/REVISION +0 -1
- data/plugins/dataobjects/Rakefile +0 -9
- data/plugins/dataobjects/do.rb +0 -348
- data/plugins/dataobjects/do_mysql.rb +0 -212
- data/plugins/dataobjects/do_postgres.rb +0 -196
- data/plugins/dataobjects/do_sqlite3.rb +0 -157
- data/plugins/dataobjects/spec/do_spec.rb +0 -150
- data/plugins/dataobjects/spec/spec_helper.rb +0 -81
- data/plugins/dataobjects/swig_mysql/extconf.rb +0 -45
- data/plugins/dataobjects/swig_mysql/mysql_c.c +0 -16602
- data/plugins/dataobjects/swig_mysql/mysql_c.i +0 -67
- data/plugins/dataobjects/swig_mysql/mysql_supp.i +0 -46
- data/plugins/dataobjects/swig_postgres/extconf.rb +0 -29
- data/plugins/dataobjects/swig_postgres/postgres_c.c +0 -8185
- data/plugins/dataobjects/swig_postgres/postgres_c.i +0 -73
- data/plugins/dataobjects/swig_sqlite/extconf.rb +0 -9
- data/plugins/dataobjects/swig_sqlite/sqlite3_c.c +0 -4725
- data/plugins/dataobjects/swig_sqlite/sqlite_c.i +0 -168
- data/tasks/drivers.rb +0 -20
@@ -1,27 +1,67 @@
|
|
1
1
|
module DataMapper
|
2
2
|
|
3
3
|
class EmbeddedValue
|
4
|
+
EMBEDDED_PROPERTIES = []
|
4
5
|
|
5
6
|
def initialize(instance)
|
6
7
|
@instance = instance
|
8
|
+
@container_prefix = ''
|
9
|
+
|
10
|
+
# force lazy load on access to any lazy-loaded embed property
|
11
|
+
@instance.lazy_load!(*self.class::EMBEDDED_PROPERTIES) # if @container_lazy
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.inherited(base)
|
15
|
+
base.const_set('EMBEDDED_PROPERTIES', [])
|
7
16
|
end
|
8
17
|
|
18
|
+
# add an embedded property
|
9
19
|
def self.property(name, type, options = {})
|
10
|
-
mapping
|
11
|
-
|
12
|
-
|
20
|
+
# set lazy option on the mapping if defined in the embed block
|
21
|
+
options[:lazy] ||= @container_lazy
|
22
|
+
|
23
|
+
visibility_options = [:public, :protected, :private]
|
24
|
+
reader_visibility = options[:reader] || options[:accessor] || @container_reader_visibility
|
25
|
+
writer_visibility = options[:writer] || options[:accessor] || @container_writer_visibility
|
26
|
+
writer_visibility = :protected if options[:protected]
|
27
|
+
writer_visibility = :private if options[:private]
|
28
|
+
|
29
|
+
raise(ArgumentError.new, "property visibility must be :public, :protected, or :private") unless visibility_options.include?(reader_visibility) && visibility_options.include?(writer_visibility)
|
30
|
+
|
31
|
+
mapping = database.schema[containing_class].add_column("#{@container_prefix}#{name}", type, options)
|
32
|
+
|
33
|
+
self::EMBEDDED_PROPERTIES << "#{@container_prefix}#{name}"
|
34
|
+
define_property_getter(name, mapping, reader_visibility)
|
35
|
+
define_property_setter(name, mapping, writer_visibility)
|
13
36
|
end
|
14
37
|
|
15
|
-
|
38
|
+
# define embedded property getters
|
39
|
+
def self.define_property_getter(name, mapping, visibility = :public)
|
40
|
+
# add convenience method on non-embedded base for #update_attributes
|
41
|
+
self.containing_class.property_getter(mapping, visibility)
|
42
|
+
|
43
|
+
# add the method on the embedded class
|
16
44
|
class_eval <<-EOS
|
45
|
+
#{visibility.to_s}
|
17
46
|
def #{name}
|
18
47
|
@instance.instance_variable_get(#{mapping.instance_variable_name.inspect})
|
19
48
|
end
|
20
49
|
EOS
|
50
|
+
|
51
|
+
# add a shortcut boolean? method if applicable (ex: activated?)
|
52
|
+
if mapping.type == :boolean
|
53
|
+
class_eval("alias #{name}? #{name}")
|
54
|
+
end
|
21
55
|
end
|
22
56
|
|
23
|
-
|
57
|
+
# define embedded property setters
|
58
|
+
def self.define_property_setter(name, mapping, visibility = :public)
|
59
|
+
# add convenience method on non-embedded base for #update_attributes
|
60
|
+
self.containing_class.property_setter(mapping, visibility)
|
61
|
+
|
62
|
+
# add the method on the embedded class
|
24
63
|
class_eval <<-EOS
|
64
|
+
#{visibility.to_s}
|
25
65
|
def #{name.to_s.sub(/\?$/, '')}=(value)
|
26
66
|
@instance.instance_variable_set(#{mapping.instance_variable_name.inspect}, value)
|
27
67
|
end
|
@@ -36,21 +76,25 @@ module DataMapper
|
|
36
76
|
end
|
37
77
|
end
|
38
78
|
|
39
|
-
def self.define(container,
|
40
|
-
|
79
|
+
def self.define(container, name, options, &block)
|
80
|
+
embedded_class, embedded_class_name, accessor_name = nil
|
41
81
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
embedded_class
|
50
|
-
container.const_set(embedded_class_name, embedded_class) unless container.const_defined?(embedded_class_name)
|
82
|
+
accessor_name = name.to_s
|
83
|
+
embedded_class_name = Inflector.camelize(accessor_name)
|
84
|
+
embedded_class = Class.new(EmbeddedValue)
|
85
|
+
container.const_set(embedded_class_name, embedded_class) unless container.const_defined?(embedded_class_name)
|
86
|
+
|
87
|
+
if options[:prefix]
|
88
|
+
container_prefix = options[:prefix].kind_of?(String) ? options[:prefix] : "#{accessor_name}_"
|
89
|
+
embedded_class.instance_variable_set('@container_prefix', container_prefix)
|
51
90
|
end
|
52
91
|
|
53
92
|
embedded_class.instance_variable_set('@containing_class', container)
|
93
|
+
|
94
|
+
embedded_class.instance_variable_set('@container_lazy', !!options[:lazy])
|
95
|
+
embedded_class.instance_variable_set('@container_reader_visibility', options[:reader] || options[:accessor] || :public)
|
96
|
+
embedded_class.instance_variable_set('@container_writer_visibility', options[:writer] || options[:accessor] || :public)
|
97
|
+
|
54
98
|
embedded_class.class_eval(&block) if block_given?
|
55
99
|
|
56
100
|
container.class_eval <<-EOS
|
@@ -62,4 +106,4 @@ module DataMapper
|
|
62
106
|
|
63
107
|
end
|
64
108
|
|
65
|
-
end # module DataMapper
|
109
|
+
end # module DataMapper
|
@@ -8,11 +8,13 @@ module DataMapper
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def save
|
11
|
-
|
11
|
+
database_context.save(self)
|
12
12
|
end
|
13
13
|
|
14
14
|
def reload!
|
15
|
-
|
15
|
+
database_context.first(self.class, key, :select => original_values.keys, :reload => true)
|
16
|
+
self.loaded_associations.each { |association| association.reload! }
|
17
|
+
self
|
16
18
|
end
|
17
19
|
|
18
20
|
def reload
|
@@ -20,12 +22,12 @@ module DataMapper
|
|
20
22
|
end
|
21
23
|
|
22
24
|
def destroy!
|
23
|
-
|
25
|
+
database_context.destroy(self)
|
24
26
|
end
|
25
27
|
|
26
28
|
module ClassMethods
|
27
29
|
|
28
|
-
def find_or_create(search_attributes, create_attributes =
|
30
|
+
def find_or_create(search_attributes, create_attributes = {})
|
29
31
|
first(search_attributes) || create(search_attributes.merge(create_attributes))
|
30
32
|
end
|
31
33
|
|
@@ -70,8 +72,14 @@ module DataMapper
|
|
70
72
|
instance.save
|
71
73
|
instance
|
72
74
|
end
|
75
|
+
|
76
|
+
def create!(attributes)
|
77
|
+
instance = self.new(attributes)
|
78
|
+
raise InvalidRecord.new(instance) unless instance.save
|
79
|
+
instance
|
80
|
+
end
|
73
81
|
end
|
74
82
|
end
|
75
83
|
|
76
84
|
end
|
77
|
-
end
|
85
|
+
end
|
@@ -14,7 +14,7 @@ module DataMapper
|
|
14
14
|
|
15
15
|
YAML::quick_emit( object_id, opts ) do |out|
|
16
16
|
out.map(nil, to_yaml_style ) do |map|
|
17
|
-
|
17
|
+
database_context.table(self).columns.each do |column|
|
18
18
|
lazy_load!(column.name) if column.lazy?
|
19
19
|
value = instance_variable_get(column.instance_variable_name)
|
20
20
|
map.add(column.to_s, value.is_a?(Class) ? value.to_s : value)
|
@@ -28,9 +28,13 @@ module DataMapper
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def to_xml
|
31
|
+
to_xml_document.to_s
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_xml_document
|
31
35
|
doc = REXML::Document.new
|
32
36
|
|
33
|
-
table =
|
37
|
+
table = database_context.table(self.class)
|
34
38
|
root = doc.add_element(Inflector.underscore(self.class.name))
|
35
39
|
|
36
40
|
key_attribute = root.attributes << REXML::Attribute.new(table.key.to_s, key)
|
@@ -49,11 +53,11 @@ module DataMapper
|
|
49
53
|
node << REXML::Text.new(copy_frozen_value(value).to_s) unless value.nil?
|
50
54
|
end
|
51
55
|
|
52
|
-
doc
|
56
|
+
doc
|
53
57
|
end
|
54
58
|
|
55
59
|
def to_json(*a)
|
56
|
-
table =
|
60
|
+
table = database_context.table(self.class)
|
57
61
|
|
58
62
|
result = '{ '
|
59
63
|
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# = Validations
|
2
|
+
# DataMapper uses the 'Validatable' gem to validate models.
|
3
|
+
#
|
4
|
+
# Example:
|
5
|
+
# class Person < DataMapper::Base
|
6
|
+
# property :name, :string
|
7
|
+
# property :email, :string
|
8
|
+
# property :password, :string
|
9
|
+
#
|
10
|
+
# validates_presence_of :name, :email
|
11
|
+
# validates_length_of :password, :minimum => 6, :on => :create
|
12
|
+
# validates_format_of :email, :with => :email_address, :message => 'Please provide a valid email address.'
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# p = Person.new
|
16
|
+
# p.valid? #=> false
|
17
|
+
# p.errors.full_messages #=> ["Email must not be blank", "Please provide a valid email address.", "Name must not be blank"]
|
18
|
+
#
|
19
|
+
# p.save #=> false
|
20
|
+
# p.errors.full_messages #=> ["Password must be more than 5 characters long", "Email must not be blank",
|
21
|
+
# "Please provide a valid email address.", "Name must not be blank"]
|
22
|
+
#
|
23
|
+
#
|
24
|
+
|
25
|
+
module Validatable
|
26
|
+
alias_method :valid_in_all_cases?, :valid? #:nodoc:
|
27
|
+
|
28
|
+
# Returns true if no errors were added otherwise false. Only executes validations that have no :groups option specified
|
29
|
+
def valid?(event = :validate)
|
30
|
+
validate_recursively(event, Set.new)
|
31
|
+
end
|
32
|
+
|
33
|
+
#TODO should callbacks really affect the flow? shouldn't validations themselves do that?
|
34
|
+
def validate_recursively(event, cleared) #:nodoc:
|
35
|
+
return true if cleared.include?(self)
|
36
|
+
cleared << self
|
37
|
+
|
38
|
+
self.class.callbacks.execute(:before_validation, self)
|
39
|
+
|
40
|
+
# Validatable clears the errors list when running valid_for_some_group?, so we save general errors
|
41
|
+
# and then merge them back in after we've validated for events
|
42
|
+
generally_valid = valid_for_group?(nil)
|
43
|
+
general_errors = self.errors.errors.dup
|
44
|
+
|
45
|
+
if respond_to?(:"valid_for_#{event}?")
|
46
|
+
valid_for_events = send(:"valid_for_#{event}?")
|
47
|
+
self.errors.errors.merge!(general_errors) {|key, val1, val2| val1+val2 }
|
48
|
+
return false unless generally_valid && valid_for_events
|
49
|
+
end
|
50
|
+
|
51
|
+
return false unless generally_valid
|
52
|
+
|
53
|
+
if self.respond_to?(:loaded_associations)
|
54
|
+
return false unless self.loaded_associations.all? do |association|
|
55
|
+
association.validate_recursively(event, cleared)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
self.class.callbacks.execute(:after_validation, self)
|
60
|
+
return true
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Validatable
|
2
|
+
class ValidationBase
|
3
|
+
alias_method :old_init, :initialize
|
4
|
+
|
5
|
+
DEFAULT_EVENTS = [:validate, :create, :save, :update]
|
6
|
+
|
7
|
+
def initialize(klass, attribute, options={})
|
8
|
+
events = [options.delete(:on)].flatten.compact + [options.delete(:event)].flatten.compact
|
9
|
+
raise ArgumentError.new("Events must be one of #{DEFAULT_EVENTS.inspect}") unless (events & DEFAULT_EVENTS).size == events.size
|
10
|
+
options[:groups] ||= events unless events.empty? ### <- Danger will robinson
|
11
|
+
old_init(klass, attribute, options)
|
12
|
+
end
|
13
|
+
|
14
|
+
def humanized_attribute
|
15
|
+
@humanized_attribute ||= Inflector.humanize(self.attribute.to_s)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Validatable
|
2
|
+
module Helpers #:nodoc:
|
3
|
+
module Formats #:nodoc:
|
4
|
+
|
5
|
+
# Allows you to validate fields as email addresses
|
6
|
+
# validates_format_of :email_field, :with => :email_address
|
7
|
+
|
8
|
+
module Email
|
9
|
+
|
10
|
+
def self.included(base)
|
11
|
+
base::FORMATS.merge!( :email_address => [ EmailAddress,
|
12
|
+
lambda { |attribute| lambda { "%s is not a valid email address".t(self.send(attribute)) } }] )
|
13
|
+
end
|
14
|
+
|
15
|
+
EmailAddress = begin
|
16
|
+
alpha = "a-zA-Z"
|
17
|
+
digit = "0-9"
|
18
|
+
atext = "[#{alpha}#{digit}\!\#\$\%\&\'\*+\/\=\?\^\_\`\{\|\}\~\-]"
|
19
|
+
dot_atom_text = "#{atext}+([.]#{atext}*)*"
|
20
|
+
dot_atom = "#{dot_atom_text}"
|
21
|
+
qtext = '[^\\x0d\\x22\\x5c\\x80-\\xff]'
|
22
|
+
text = "[\\x01-\\x09\\x11\\x12\\x14-\\x7f]"
|
23
|
+
quoted_pair = "(\\x5c#{text})"
|
24
|
+
qcontent = "(?:#{qtext}|#{quoted_pair})"
|
25
|
+
quoted_string = "[\"]#{qcontent}+[\"]"
|
26
|
+
atom = "#{atext}+"
|
27
|
+
word = "(?:#{atom}|#{quoted_string})"
|
28
|
+
obs_local_part = "#{word}([.]#{word})*"
|
29
|
+
local_part = "(?:#{dot_atom}|#{quoted_string}|#{obs_local_part})"
|
30
|
+
no_ws_ctl = "\\x01-\\x08\\x11\\x12\\x14-\\x1f\\x7f"
|
31
|
+
dtext = "[#{no_ws_ctl}\\x21-\\x5a\\x5e-\\x7e]"
|
32
|
+
dcontent = "(?:#{dtext}|#{quoted_pair})"
|
33
|
+
domain_literal = "\\[#{dcontent}+\\]"
|
34
|
+
obs_domain = "#{atom}([.]#{atom})*"
|
35
|
+
domain = "(?:#{dot_atom}|#{domain_literal}|#{obs_domain})"
|
36
|
+
addr_spec = "#{local_part}\@#{domain}"
|
37
|
+
pattern = /^#{addr_spec}$/
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/formats/email.rb'
|
2
|
+
|
3
|
+
module Validatable
|
4
|
+
|
5
|
+
class ValidatesFormatOf < ValidationBase
|
6
|
+
FORMATS = {}
|
7
|
+
|
8
|
+
include Validatable::Helpers::Formats::Email
|
9
|
+
|
10
|
+
def initialize(klass, attribute, options={})
|
11
|
+
super
|
12
|
+
if with.is_a? Symbol
|
13
|
+
self.with = if FORMATS[with].is_a? Array
|
14
|
+
@message = (FORMATS[with][1].respond_to?(:call) ? FORMATS[with][1].call(attribute) : FORMATS[with][1]) unless @message
|
15
|
+
FORMATS[with][0]
|
16
|
+
else
|
17
|
+
FORMATS[with]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def message(instance)
|
23
|
+
super || '%s is invalid'.t(humanized_attribute)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Validatable
|
2
|
+
class ValidatesLengthOf < ValidationBase
|
3
|
+
def message(instance)
|
4
|
+
super || unless minimum.nil?
|
5
|
+
'%s must be more than %d characters long'.t(humanized_attribute, minimum-1)
|
6
|
+
else unless maximum.nil?
|
7
|
+
'%s must be less than %d characters long'.t(humanized_attribute, maximum+1)
|
8
|
+
else unless is.nil?
|
9
|
+
'%s must be %d characters long'.t(humanized_attribute, is)
|
10
|
+
else unless within.nil?
|
11
|
+
'%s must be between %d and %d characters long'.t(humanized_attribute, within.first, within.last)
|
12
|
+
end;end;end;end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Validatable
|
2
|
+
class ValidatesUniquenessOf < ValidationBase
|
3
|
+
option :scope, :allow_nil, :case_sensitive
|
4
|
+
|
5
|
+
def message(instance)
|
6
|
+
super || '%s has already been taken'.t(humanized_attribute)
|
7
|
+
end
|
8
|
+
|
9
|
+
def case_sensitive?
|
10
|
+
case_sensitive
|
11
|
+
end
|
12
|
+
|
13
|
+
def valid?(instance)
|
14
|
+
value = instance.send(self.attribute)
|
15
|
+
return true if allow_nil && value.nil?
|
16
|
+
|
17
|
+
finder_options = if case_sensitive?
|
18
|
+
{ self.attribute => value }
|
19
|
+
else
|
20
|
+
{ self.attribute.like => value }
|
21
|
+
end
|
22
|
+
|
23
|
+
if scope
|
24
|
+
scope_value = instance.send(scope)
|
25
|
+
finder_options.merge! scope => scope_value
|
26
|
+
end
|
27
|
+
|
28
|
+
finder_options.merge!({ instance.database_context.table(instance.class).key.name.not => instance.key }) unless instance.new_record?
|
29
|
+
instance.database_context.first(instance.class, finder_options).nil?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
Dir[File.dirname(__FILE__) + "/validatable_extensions/**/*.rb"].each do |path|
|
2
|
+
require path
|
3
|
+
end
|
4
|
+
|
5
|
+
#--
|
6
|
+
# TODO:
|
7
|
+
# implement alias_option method for all validations (so we can set :with == :as)
|
8
|
+
# make validations orthogonal with allow_nil default to true
|
9
|
+
#
|
10
|
+
module DataMapper
|
11
|
+
module Validations
|
12
|
+
|
13
|
+
def self.included(base) #:nodoc:
|
14
|
+
base.class_eval do
|
15
|
+
include Validatable
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
data/lib/data_mapper.rb
CHANGED
@@ -20,57 +20,62 @@ require 'rubygems'
|
|
20
20
|
require 'yaml'
|
21
21
|
require 'set'
|
22
22
|
require 'fastthread'
|
23
|
+
require 'validatable'
|
23
24
|
require 'data_mapper/support/blank'
|
24
25
|
require 'data_mapper/support/enumerable'
|
25
26
|
require 'data_mapper/support/symbol'
|
26
27
|
require 'data_mapper/support/string'
|
27
28
|
require 'data_mapper/support/silence'
|
28
29
|
require 'data_mapper/support/inflector'
|
30
|
+
require 'data_mapper/support/errors'
|
29
31
|
require 'data_mapper/database'
|
30
32
|
require 'data_mapper/base'
|
31
33
|
|
32
|
-
|
33
|
-
#
|
34
|
-
#
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
end
|
34
|
+
begin
|
35
|
+
# This block of code is for compatibility with Ruby On Rails' or Merb's database.yml
|
36
|
+
# file, allowing you to simply require the data_mapper.rb in your
|
37
|
+
# Rails application's environment.rb to configure the DataMapper.
|
38
|
+
unless defined?(DM_APP_ROOT)
|
39
|
+
application_root, application_environment = *if defined?(RAILS_ROOT)
|
40
|
+
[RAILS_ROOT, RAILS_ENV]
|
41
|
+
end
|
41
42
|
|
42
|
-
|
43
|
+
DM_APP_ROOT = application_root || Dir::pwd
|
43
44
|
|
44
|
-
|
45
|
+
if application_root && File.exists?(application_root + '/config/database.yml')
|
45
46
|
|
46
|
-
|
47
|
-
|
47
|
+
database_configurations = YAML::load_file(application_root + '/config/database.yml')
|
48
|
+
current_database_config = database_configurations[application_environment] || database_configurations[application_environment.to_sym]
|
48
49
|
|
49
|
-
|
50
|
+
config = lambda { |key| current_database_config[key.to_s] || current_database_config[key] }
|
50
51
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
52
|
+
default_database_config = {
|
53
|
+
:adapter => config[:adapter],
|
54
|
+
:host => config[:host],
|
55
|
+
:database => config[:database],
|
56
|
+
:username => config[:username],
|
57
|
+
:password => config[:password],
|
58
|
+
:socket => config[:socket]
|
59
|
+
}
|
58
60
|
|
59
|
-
|
61
|
+
DataMapper::Database.setup(default_database_config)
|
60
62
|
|
61
|
-
|
63
|
+
elsif application_root && FileTest.directory?(application_root + '/config')
|
62
64
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
65
|
+
%w(development testing production).map do |environment|
|
66
|
+
<<-EOS.margin
|
67
|
+
#{environment}:
|
68
|
+
adapter: mysql
|
69
|
+
username: root
|
70
|
+
password:
|
71
|
+
host: localhost
|
72
|
+
database: #{File.dirname(DM_APP_ROOT).split('/').last}_#{environment}
|
73
|
+
EOS
|
74
|
+
end
|
73
75
|
|
74
|
-
|
76
|
+
#File::open(application_root + '/config/database.yml')
|
77
|
+
end
|
75
78
|
end
|
79
|
+
rescue Exception
|
80
|
+
warn "Could not connect to database specified by database.yml."
|
76
81
|
end
|