datamapper 0.2.2 → 0.2.3
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/CHANGELOG +10 -2
- data/environment.rb +0 -3
- data/lib/data_mapper.rb +2 -0
- data/lib/data_mapper/adapters/abstract_adapter.rb +2 -2
- data/lib/data_mapper/adapters/data_object_adapter.rb +63 -73
- data/lib/data_mapper/adapters/mysql_adapter.rb +2 -13
- data/lib/data_mapper/adapters/postgresql_adapter.rb +3 -2
- data/lib/data_mapper/adapters/sql/coersion.rb +29 -11
- data/lib/data_mapper/adapters/sql/commands/load_command.rb +13 -9
- data/lib/data_mapper/adapters/sql/mappings/table.rb +7 -3
- data/lib/data_mapper/adapters/sql/quoting.rb +0 -53
- data/lib/data_mapper/adapters/sqlite3_adapter.rb +2 -1
- data/lib/data_mapper/base.rb +25 -9
- data/lib/data_mapper/context.rb +2 -2
- data/lib/data_mapper/database.rb +6 -6
- data/lib/data_mapper/support/active_record_impersonation.rb +1 -1
- data/lib/data_mapper/support/serialization.rb +10 -2
- data/lib/data_mapper/support/silence.rb +10 -0
- data/lib/data_mapper/support/string.rb +21 -1
- data/lib/data_mapper/validations/confirmation_validator.rb +1 -3
- data/lib/data_mapper/validations/format_validator.rb +6 -6
- data/lib/data_mapper/validations/generic_validator.rb +0 -5
- data/lib/data_mapper/validations/length_validator.rb +14 -13
- data/lib/data_mapper/validations/required_field_validator.rb +2 -8
- data/lib/data_mapper/validations/unique_validator.rb +1 -7
- data/plugins/dataobjects/do.rb +94 -2
- data/plugins/dataobjects/do_mysql.rb +17 -17
- data/plugins/dataobjects/do_postgres.rb +34 -15
- data/plugins/dataobjects/do_sqlite3.rb +9 -6
- data/rakefile.rb +3 -2
- data/spec/base_spec.rb +28 -0
- data/spec/dataobjects_spec.rb +2 -1
- data/spec/load_command_spec.rb +1 -1
- data/spec/serialization_spec.rb +2 -2
- data/spec/spec_helper.rb +1 -1
- data/spec/support_spec.rb +7 -0
- data/spec/validations_spec.rb +13 -0
- metadata +14 -6
- data/plugins/dataobjects/swig_mysql/Makefile +0 -146
- data/plugins/dataobjects/swig_mysql/mysql_c.o +0 -0
- data/plugins/dataobjects/swig_postgres/Makefile +0 -146
- data/plugins/dataobjects/swig_sqlite/db +0 -0
@@ -31,6 +31,10 @@ module DataMapper
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
+
def schema
|
35
|
+
@schema || @schema = @adapter.schema
|
36
|
+
end
|
37
|
+
|
34
38
|
def multi_class?
|
35
39
|
@multi_class
|
36
40
|
end
|
@@ -53,7 +57,7 @@ module DataMapper
|
|
53
57
|
end
|
54
58
|
|
55
59
|
def drop!
|
56
|
-
@adapter.drop(database, self)
|
60
|
+
@adapter.drop(database, self)
|
57
61
|
end
|
58
62
|
|
59
63
|
def create!(force = false)
|
@@ -133,8 +137,8 @@ module DataMapper
|
|
133
137
|
@to_exists_sql || @to_exists_sql = <<-EOS.compress_lines
|
134
138
|
SELECT TABLE_NAME
|
135
139
|
FROM INFORMATION_SCHEMA.TABLES
|
136
|
-
WHERE TABLE_NAME =
|
137
|
-
AND TABLE_SCHEMA =
|
140
|
+
WHERE TABLE_NAME = ?
|
141
|
+
AND TABLE_SCHEMA = ?
|
138
142
|
EOS
|
139
143
|
end
|
140
144
|
|
@@ -18,59 +18,6 @@ module DataMapper
|
|
18
18
|
name.ensure_wrapped_with(self.class::COLUMN_QUOTING_CHARACTER)
|
19
19
|
end
|
20
20
|
|
21
|
-
def quote_value(value)
|
22
|
-
return 'NULL' if value.nil?
|
23
|
-
|
24
|
-
case value
|
25
|
-
when Numeric then quote_numeric(value)
|
26
|
-
when String then quote_string(value)
|
27
|
-
when Class then quote_class(value)
|
28
|
-
when Time then quote_time(value)
|
29
|
-
when DateTime then quote_datetime(value)
|
30
|
-
when Date then quote_date(value)
|
31
|
-
when TrueClass, FalseClass then quote_boolean(value)
|
32
|
-
when Array then quote_array(value)
|
33
|
-
else
|
34
|
-
if value.respond_to?(:to_sql)
|
35
|
-
value.to_sql
|
36
|
-
else
|
37
|
-
raise "Don't know how to quote #{value.inspect}"
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def quote_numeric(value)
|
43
|
-
value.to_s
|
44
|
-
end
|
45
|
-
|
46
|
-
def quote_string(value)
|
47
|
-
"'#{value.gsub("'", "''")}'"
|
48
|
-
end
|
49
|
-
|
50
|
-
def quote_class(value)
|
51
|
-
"'#{value.name}'"
|
52
|
-
end
|
53
|
-
|
54
|
-
def quote_time(value)
|
55
|
-
"'#{value.xmlschema}'"
|
56
|
-
end
|
57
|
-
|
58
|
-
def quote_datetime(value)
|
59
|
-
"'#{value}'"
|
60
|
-
end
|
61
|
-
|
62
|
-
def quote_date(value)
|
63
|
-
"'#{value.strftime("%Y-%m-%d")}'"
|
64
|
-
end
|
65
|
-
|
66
|
-
def quote_boolean(value)
|
67
|
-
value.to_s.upcase
|
68
|
-
end
|
69
|
-
|
70
|
-
def quote_array(value)
|
71
|
-
"(#{value.map { |entry| quote_value(entry) }.join(', ')})"
|
72
|
-
end
|
73
|
-
|
74
21
|
end # module Quoting
|
75
22
|
end
|
76
23
|
end
|
@@ -26,6 +26,7 @@ module DataMapper
|
|
26
26
|
|
27
27
|
def create_connection
|
28
28
|
conn = DataObject::Sqlite3::Connection.new("dbname=#{@configuration.database}")
|
29
|
+
conn.logger = self.logger
|
29
30
|
conn.open
|
30
31
|
return conn
|
31
32
|
end
|
@@ -43,7 +44,7 @@ module DataMapper
|
|
43
44
|
SELECT "name"
|
44
45
|
FROM "sqlite_master"
|
45
46
|
WHERE "type" = "table"
|
46
|
-
AND "name" =
|
47
|
+
AND "name" = ?
|
47
48
|
EOS
|
48
49
|
end
|
49
50
|
end # class Table
|
data/lib/data_mapper/base.rb
CHANGED
@@ -184,6 +184,21 @@ module DataMapper
|
|
184
184
|
@new_record.nil? || @new_record
|
185
185
|
end
|
186
186
|
|
187
|
+
def ^(other)
|
188
|
+
results = {}
|
189
|
+
|
190
|
+
self_attributes, other_attributes = attributes, other.attributes
|
191
|
+
|
192
|
+
self_attributes.each_pair do |k,v|
|
193
|
+
other_value = other_attributes[k]
|
194
|
+
unless v == other_value
|
195
|
+
results[k] = [v, other_value]
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
results
|
200
|
+
end
|
201
|
+
|
187
202
|
def loaded_attributes
|
188
203
|
pairs = {}
|
189
204
|
|
@@ -194,6 +209,11 @@ module DataMapper
|
|
194
209
|
pairs
|
195
210
|
end
|
196
211
|
|
212
|
+
def update_attributes(update_hash)
|
213
|
+
self.attributes = update_hash
|
214
|
+
self.save
|
215
|
+
end
|
216
|
+
|
197
217
|
def attributes
|
198
218
|
pairs = {}
|
199
219
|
|
@@ -224,11 +244,7 @@ module DataMapper
|
|
224
244
|
def dirty?(name = nil)
|
225
245
|
if name.nil?
|
226
246
|
session.table(self).columns.any? do |column|
|
227
|
-
|
228
|
-
value.hash != original_hashes[column.name]
|
229
|
-
else
|
230
|
-
false
|
231
|
-
end
|
247
|
+
self.instance_variable_get(column.instance_variable_name) != original_values[column.name]
|
232
248
|
end || loaded_associations.any? do |loaded_association|
|
233
249
|
if loaded_association.respond_to?(:dirty?)
|
234
250
|
loaded_association.dirty?
|
@@ -238,7 +254,7 @@ module DataMapper
|
|
238
254
|
end
|
239
255
|
else
|
240
256
|
key = name.kind_of?(Symbol) ? name : name.to_sym
|
241
|
-
self.instance_variable_get("@#{name}")
|
257
|
+
self.instance_variable_get("@#{name}") != original_values[key]
|
242
258
|
end
|
243
259
|
end
|
244
260
|
|
@@ -253,7 +269,7 @@ module DataMapper
|
|
253
269
|
end
|
254
270
|
else
|
255
271
|
session.table(self).columns.each do |column|
|
256
|
-
if (value = instance_variable_get(column.instance_variable_name))
|
272
|
+
if (value = instance_variable_get(column.instance_variable_name)) != original_values[column.name]
|
257
273
|
pairs[column.name] = value
|
258
274
|
end
|
259
275
|
end
|
@@ -262,8 +278,8 @@ module DataMapper
|
|
262
278
|
pairs
|
263
279
|
end
|
264
280
|
|
265
|
-
def
|
266
|
-
@
|
281
|
+
def original_values
|
282
|
+
@original_values || (@original_values = {})
|
267
283
|
end
|
268
284
|
|
269
285
|
def protected_attribute?(key)
|
data/lib/data_mapper/context.rb
CHANGED
data/lib/data_mapper/database.rb
CHANGED
@@ -205,16 +205,16 @@ module DataMapper
|
|
205
205
|
end
|
206
206
|
|
207
207
|
# Default Logger from Ruby's logger.rb
|
208
|
-
def
|
209
|
-
@
|
210
|
-
@
|
211
|
-
at_exit { @
|
208
|
+
def logger
|
209
|
+
@logger = Logger.new(@log_stream, File::WRONLY | File::APPEND | File::CREAT)
|
210
|
+
@logger.level = @log_level
|
211
|
+
at_exit { @logger.close }
|
212
212
|
|
213
213
|
class << self
|
214
|
-
attr_reader :
|
214
|
+
attr_reader :logger
|
215
215
|
end
|
216
216
|
|
217
|
-
return @
|
217
|
+
return @logger
|
218
218
|
end
|
219
219
|
|
220
220
|
end
|
@@ -46,7 +46,7 @@ module DataMapper
|
|
46
46
|
next if column.key?
|
47
47
|
value = send(column.name)
|
48
48
|
node = root.add_element(column.to_s)
|
49
|
-
node << REXML::Text.new(value.to_s) unless value.nil?
|
49
|
+
node << REXML::Text.new(copy_frozen_value(value).to_s) unless value.nil?
|
50
50
|
end
|
51
51
|
|
52
52
|
doc.to_s
|
@@ -58,12 +58,20 @@ module DataMapper
|
|
58
58
|
result = '{ '
|
59
59
|
|
60
60
|
result << table.columns.map do |column|
|
61
|
-
"#{column.name.to_json}: #{send(column.name).to_json(*a)}"
|
61
|
+
"#{column.name.to_json}: #{copy_frozen_value(send(column.name)).to_json(*a)}"
|
62
62
|
end.join(', ')
|
63
63
|
|
64
64
|
result << ' }'
|
65
65
|
result
|
66
66
|
end
|
67
|
+
|
68
|
+
def copy_frozen_value(value)
|
69
|
+
case value
|
70
|
+
when Date, DateTime, Time, String then value.dup
|
71
|
+
when Fixnum, Class then value
|
72
|
+
else value
|
73
|
+
end
|
74
|
+
end
|
67
75
|
end
|
68
76
|
end # module Support
|
69
77
|
end # module DataMapper
|
@@ -4,7 +4,18 @@ module DataMapper
|
|
4
4
|
|
5
5
|
# I set the constant on the String itself to avoid inheritance chain lookups.
|
6
6
|
def self.included(base)
|
7
|
-
base.
|
7
|
+
base.extend(ClassMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
# Overwrite this method to provide your own translations.
|
12
|
+
def translate(value)
|
13
|
+
translations[value] || value
|
14
|
+
end
|
15
|
+
|
16
|
+
def translations
|
17
|
+
@translations || @translations = {}
|
18
|
+
end
|
8
19
|
end
|
9
20
|
|
10
21
|
def ensure_starts_with(part)
|
@@ -52,6 +63,15 @@ module DataMapper
|
|
52
63
|
end
|
53
64
|
end
|
54
65
|
|
66
|
+
# Formats String for easy translation. Replaces an arbitrary number of
|
67
|
+
# values using numeric identifier replacement.
|
68
|
+
#
|
69
|
+
# "%s %s %s" % %w(one two three) #=> "one two three"
|
70
|
+
# "%3$s %2$s %1$s" % %w(one two three) #=> "three two one"
|
71
|
+
def t(*values)
|
72
|
+
self.class::translate(self) % values
|
73
|
+
end
|
74
|
+
|
55
75
|
end # module String
|
56
76
|
end # module Support
|
57
77
|
end # module DataMapper
|
@@ -13,10 +13,8 @@ module DataMapper
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def call(target)
|
16
|
-
field = Inflector.humanize(@field_name)
|
17
|
-
|
18
16
|
unless valid?(target)
|
19
|
-
error_message =
|
17
|
+
error_message = '%s does not match the confirmation'.t(Inflector.humanize(@field_name))
|
20
18
|
add_error(target, error_message , @field_name)
|
21
19
|
return false
|
22
20
|
end
|
@@ -8,11 +8,6 @@ module DataMapper
|
|
8
8
|
# Seems to me that all this email garbage belongs somewhere else... Where's the best
|
9
9
|
# place to stick it?
|
10
10
|
include DataMapper::Validations::Helpers::Email
|
11
|
-
|
12
|
-
ERROR_MESSAGES = {
|
13
|
-
:invalid => '#{field} is invalid',
|
14
|
-
:invalid_email => '#{value} is not a valid email address'
|
15
|
-
}
|
16
11
|
|
17
12
|
FORMATS = {
|
18
13
|
:email_address => [lambda { |email_address| email_address =~ DataMapper::Validations::Helpers::Email::RFC2822::EmailAddress }, :invalid_email]
|
@@ -52,7 +47,12 @@ module DataMapper
|
|
52
47
|
field = Inflector.humanize(@field_name)
|
53
48
|
value = target.instance_variable_get("@#{@field_name}")
|
54
49
|
|
55
|
-
error_message =
|
50
|
+
error_message = if message_key == :invalid
|
51
|
+
'%s is invalid'.t(field)
|
52
|
+
else
|
53
|
+
'%s is not a valid email address'.t(value)
|
54
|
+
end
|
55
|
+
|
56
56
|
add_error(target, error_message , @field_name)
|
57
57
|
end
|
58
58
|
|
@@ -9,11 +9,6 @@ module DataMapper
|
|
9
9
|
target.errors.add(attribute, message)
|
10
10
|
end
|
11
11
|
|
12
|
-
# Gets the proper error message
|
13
|
-
def validation_error_message(default, custom_message, validation_binding)
|
14
|
-
eval("\"#{(custom_message || default)}\"", validation_binding)
|
15
|
-
end
|
16
|
-
|
17
12
|
# Call the validator. We use "call" so the operation
|
18
13
|
# is BoundMethod and Block compatible.
|
19
14
|
# The result should always be TRUE or FALSE.
|
@@ -3,13 +3,6 @@ module DataMapper
|
|
3
3
|
|
4
4
|
class LengthValidator < GenericValidator
|
5
5
|
|
6
|
-
ERROR_MESSAGES = {
|
7
|
-
:range => '#{field} must be between #{min} and #{max} characters long',
|
8
|
-
:min => '#{field} must be more than #{min} characters long',
|
9
|
-
:max => '#{field} must be less than #{max} characters long',
|
10
|
-
:equals => '#{field} must be #{equal} characters long'
|
11
|
-
}
|
12
|
-
|
13
6
|
def initialize(field_name, options)
|
14
7
|
@field_name = field_name
|
15
8
|
@options = options
|
@@ -35,17 +28,25 @@ module DataMapper
|
|
35
28
|
max = @range ? @range.max : @max
|
36
29
|
equal = @equal
|
37
30
|
|
38
|
-
error_message =
|
31
|
+
error_message = nil
|
39
32
|
|
40
|
-
|
33
|
+
case @validation_method
|
41
34
|
when :range then
|
42
|
-
@range.include?(field_value.size)
|
35
|
+
unless valid = @range.include?(field_value.size)
|
36
|
+
error_message = '%s must be between %s and %s characters long'.t(field, min, max)
|
37
|
+
end
|
43
38
|
when :min then
|
44
|
-
field_value.size >= min
|
39
|
+
unless valid = field_value.size >= min
|
40
|
+
error_message = '%s must be more than %s characters long'.t(field, min)
|
41
|
+
end
|
45
42
|
when :max then
|
46
|
-
field_value.size <= max
|
43
|
+
unless valid = field_value.size <= max
|
44
|
+
error_message = '%s must be less than %s characters long'.t(field, max)
|
45
|
+
end
|
47
46
|
when :equals then
|
48
|
-
field_value.size == equal
|
47
|
+
unless valid = field_value.size == equal
|
48
|
+
error_message = '%s must be %s characters long'.t(field, equal)
|
49
|
+
end
|
49
50
|
end
|
50
51
|
|
51
52
|
add_error(target, error_message, @field_name) unless valid
|
@@ -2,11 +2,7 @@ module DataMapper
|
|
2
2
|
module Validations
|
3
3
|
|
4
4
|
class RequiredFieldValidator < GenericValidator
|
5
|
-
|
6
|
-
ERROR_MESSAGES = {
|
7
|
-
:required => '#{field} must not be blank'
|
8
|
-
}
|
9
|
-
|
5
|
+
|
10
6
|
def initialize(field_name)
|
11
7
|
@field_name = field_name
|
12
8
|
end
|
@@ -15,9 +11,7 @@ module DataMapper
|
|
15
11
|
field_value = !target.instance_variable_get("@#{@field_name}").nil?
|
16
12
|
return true if field_value
|
17
13
|
|
18
|
-
|
19
|
-
|
20
|
-
error_message = validation_error_message(ERROR_MESSAGES[:required], nil, binding)
|
14
|
+
error_message = "%s must not be blank".t(Inflector.humanize(@field_name))
|
21
15
|
add_error(target, error_message , @field_name)
|
22
16
|
|
23
17
|
return false
|
@@ -3,20 +3,14 @@ module DataMapper
|
|
3
3
|
|
4
4
|
class UniqueValidator < GenericValidator
|
5
5
|
|
6
|
-
ERROR_MESSAGES = {
|
7
|
-
:unique => '#{field} has already been taken'
|
8
|
-
}
|
9
|
-
|
10
6
|
def initialize(field_name, options = {})
|
11
7
|
@options = options
|
12
8
|
@field_name = field_name.to_sym
|
13
9
|
end
|
14
10
|
|
15
11
|
def call(target)
|
16
|
-
field = Inflector.humanize(@field_name)
|
17
|
-
|
18
12
|
unless valid?(target)
|
19
|
-
error_message =
|
13
|
+
error_message = '%s has already been taken'.t(Inflector.humanize(@field_name))
|
20
14
|
add_error(target, error_message , @field_name)
|
21
15
|
return false
|
22
16
|
end
|