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.
Files changed (42) hide show
  1. data/CHANGELOG +10 -2
  2. data/environment.rb +0 -3
  3. data/lib/data_mapper.rb +2 -0
  4. data/lib/data_mapper/adapters/abstract_adapter.rb +2 -2
  5. data/lib/data_mapper/adapters/data_object_adapter.rb +63 -73
  6. data/lib/data_mapper/adapters/mysql_adapter.rb +2 -13
  7. data/lib/data_mapper/adapters/postgresql_adapter.rb +3 -2
  8. data/lib/data_mapper/adapters/sql/coersion.rb +29 -11
  9. data/lib/data_mapper/adapters/sql/commands/load_command.rb +13 -9
  10. data/lib/data_mapper/adapters/sql/mappings/table.rb +7 -3
  11. data/lib/data_mapper/adapters/sql/quoting.rb +0 -53
  12. data/lib/data_mapper/adapters/sqlite3_adapter.rb +2 -1
  13. data/lib/data_mapper/base.rb +25 -9
  14. data/lib/data_mapper/context.rb +2 -2
  15. data/lib/data_mapper/database.rb +6 -6
  16. data/lib/data_mapper/support/active_record_impersonation.rb +1 -1
  17. data/lib/data_mapper/support/serialization.rb +10 -2
  18. data/lib/data_mapper/support/silence.rb +10 -0
  19. data/lib/data_mapper/support/string.rb +21 -1
  20. data/lib/data_mapper/validations/confirmation_validator.rb +1 -3
  21. data/lib/data_mapper/validations/format_validator.rb +6 -6
  22. data/lib/data_mapper/validations/generic_validator.rb +0 -5
  23. data/lib/data_mapper/validations/length_validator.rb +14 -13
  24. data/lib/data_mapper/validations/required_field_validator.rb +2 -8
  25. data/lib/data_mapper/validations/unique_validator.rb +1 -7
  26. data/plugins/dataobjects/do.rb +94 -2
  27. data/plugins/dataobjects/do_mysql.rb +17 -17
  28. data/plugins/dataobjects/do_postgres.rb +34 -15
  29. data/plugins/dataobjects/do_sqlite3.rb +9 -6
  30. data/rakefile.rb +3 -2
  31. data/spec/base_spec.rb +28 -0
  32. data/spec/dataobjects_spec.rb +2 -1
  33. data/spec/load_command_spec.rb +1 -1
  34. data/spec/serialization_spec.rb +2 -2
  35. data/spec/spec_helper.rb +1 -1
  36. data/spec/support_spec.rb +7 -0
  37. data/spec/validations_spec.rb +13 -0
  38. metadata +14 -6
  39. data/plugins/dataobjects/swig_mysql/Makefile +0 -146
  40. data/plugins/dataobjects/swig_mysql/mysql_c.o +0 -0
  41. data/plugins/dataobjects/swig_postgres/Makefile +0 -146
  42. 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) if exists?
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 = #{@adapter.quote_value(name)}
137
- AND TABLE_SCHEMA = #{@adapter.quote_value(@adapter.schema.name)}
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" = #{@adapter.quote_value(name)}
47
+ AND "name" = ?
47
48
  EOS
48
49
  end
49
50
  end # class Table
@@ -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
- if value = self.instance_variable_get(column.instance_variable_name)
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}").hash != original_hashes[key]
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)).hash != original_hashes[column.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 original_hashes
266
- @original_hashes || (@original_hashes = {})
281
+ def original_values
282
+ @original_values || (@original_values = {})
267
283
  end
268
284
 
269
285
  def protected_attribute?(key)
@@ -94,8 +94,8 @@ module DataMapper
94
94
  @adapter.table(klass)
95
95
  end
96
96
 
97
- def log
98
- @adapter.log
97
+ def logger
98
+ @logger || @logger = @adapter.logger
99
99
  end
100
100
 
101
101
  end
@@ -205,16 +205,16 @@ module DataMapper
205
205
  end
206
206
 
207
207
  # Default Logger from Ruby's logger.rb
208
- def log
209
- @log = Logger.new(@log_stream, File::WRONLY | File::APPEND | File::CREAT)
210
- @log.level = @log_level
211
- at_exit { @log.close }
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 :log
214
+ attr_reader :logger
215
215
  end
216
216
 
217
- return @log
217
+ return @logger
218
218
  end
219
219
 
220
220
  end
@@ -12,7 +12,7 @@ module DataMapper
12
12
  end
13
13
 
14
14
  def reload!
15
- session.first(self.class, key, :select => original_hashes.keys, :reload => true)
15
+ session.first(self.class, key, :select => original_values.keys, :reload => true)
16
16
  end
17
17
 
18
18
  def reload
@@ -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
@@ -0,0 +1,10 @@
1
+ module Kernel
2
+ def silence_warnings
3
+ old_verbose, $VERBOSE = $VERBOSE, nil
4
+ begin
5
+ yield
6
+ ensure
7
+ $VERBOSE = old_verbose
8
+ end
9
+ end
10
+ end
@@ -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.const_set('EMPTY', ''.freeze)
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 = validation_error_message(ERROR_MESSAGES[:confirmation], nil, binding)
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 = validation_error_message(ERROR_MESSAGES[message_key], nil, binding)
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 = validation_error_message(ERROR_MESSAGES[@validation_method], nil, binding)
31
+ error_message = nil
39
32
 
40
- valid = case @validation_method
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
- field = Inflector.humanize(@field_name)
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 = validation_error_message(ERROR_MESSAGES[:unique], nil, binding)
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