datamapper 0.2.3 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|