datamapper 0.1.1 → 0.2.0

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 (131) hide show
  1. data/CHANGELOG +65 -0
  2. data/README +193 -1
  3. data/do_performance.rb +153 -0
  4. data/environment.rb +45 -0
  5. data/example.rb +119 -22
  6. data/lib/data_mapper.rb +36 -16
  7. data/lib/data_mapper/adapters/abstract_adapter.rb +8 -0
  8. data/lib/data_mapper/adapters/data_object_adapter.rb +360 -0
  9. data/lib/data_mapper/adapters/mysql_adapter.rb +30 -179
  10. data/lib/data_mapper/adapters/postgresql_adapter.rb +90 -199
  11. data/lib/data_mapper/adapters/sql/coersion.rb +32 -3
  12. data/lib/data_mapper/adapters/sql/commands/conditions.rb +97 -128
  13. data/lib/data_mapper/adapters/sql/commands/load_command.rb +234 -231
  14. data/lib/data_mapper/adapters/sql/commands/loader.rb +99 -0
  15. data/lib/data_mapper/adapters/sql/mappings/associations_set.rb +30 -0
  16. data/lib/data_mapper/adapters/sql/mappings/column.rb +68 -6
  17. data/lib/data_mapper/adapters/sql/mappings/schema.rb +6 -3
  18. data/lib/data_mapper/adapters/sql/mappings/table.rb +71 -42
  19. data/lib/data_mapper/adapters/sql/quoting.rb +8 -2
  20. data/lib/data_mapper/adapters/sqlite3_adapter.rb +32 -201
  21. data/lib/data_mapper/associations.rb +21 -7
  22. data/lib/data_mapper/associations/belongs_to_association.rb +96 -80
  23. data/lib/data_mapper/associations/has_and_belongs_to_many_association.rb +158 -67
  24. data/lib/data_mapper/associations/has_many_association.rb +96 -78
  25. data/lib/data_mapper/associations/has_n_association.rb +64 -0
  26. data/lib/data_mapper/associations/has_one_association.rb +49 -79
  27. data/lib/data_mapper/associations/reference.rb +47 -0
  28. data/lib/data_mapper/base.rb +216 -50
  29. data/lib/data_mapper/callbacks.rb +71 -24
  30. data/lib/data_mapper/{session.rb → context.rb} +20 -8
  31. data/lib/data_mapper/database.rb +176 -45
  32. data/lib/data_mapper/embedded_value.rb +65 -0
  33. data/lib/data_mapper/identity_map.rb +12 -4
  34. data/lib/data_mapper/support/active_record_impersonation.rb +12 -8
  35. data/lib/data_mapper/support/enumerable.rb +8 -0
  36. data/lib/data_mapper/support/serialization.rb +13 -0
  37. data/lib/data_mapper/support/string.rb +1 -12
  38. data/lib/data_mapper/support/symbol.rb +3 -0
  39. data/lib/data_mapper/validations/unique_validator.rb +1 -2
  40. data/lib/data_mapper/validations/validation_helper.rb +18 -1
  41. data/performance.rb +109 -34
  42. data/plugins/can_has_sphinx/LICENSE +23 -0
  43. data/plugins/can_has_sphinx/README +4 -0
  44. data/plugins/can_has_sphinx/REVISION +1 -0
  45. data/plugins/can_has_sphinx/Rakefile +22 -0
  46. data/plugins/can_has_sphinx/init.rb +1 -0
  47. data/plugins/can_has_sphinx/install.rb +1 -0
  48. data/plugins/can_has_sphinx/lib/acts_as_sphinx.rb +123 -0
  49. data/plugins/can_has_sphinx/lib/sphinx.rb +460 -0
  50. data/plugins/can_has_sphinx/scripts/sphinx.sh +47 -0
  51. data/plugins/can_has_sphinx/tasks/acts_as_sphinx_tasks.rake +41 -0
  52. data/plugins/dataobjects/REVISION +1 -0
  53. data/plugins/dataobjects/Rakefile +7 -0
  54. data/plugins/dataobjects/do.rb +246 -0
  55. data/plugins/dataobjects/do_mysql.rb +179 -0
  56. data/plugins/dataobjects/do_postgres.rb +181 -0
  57. data/plugins/dataobjects/do_sqlite3.rb +153 -0
  58. data/plugins/dataobjects/spec/do_spec.rb +150 -0
  59. data/plugins/dataobjects/spec/spec_helper.rb +81 -0
  60. data/plugins/dataobjects/swig_mysql/do_mysql.bundle +0 -0
  61. data/plugins/dataobjects/swig_mysql/extconf.rb +33 -0
  62. data/plugins/dataobjects/swig_mysql/mysql_c.c +18800 -0
  63. data/plugins/dataobjects/swig_mysql/mysql_c.i +8 -0
  64. data/plugins/dataobjects/swig_mysql/mysql_supp.i +46 -0
  65. data/plugins/dataobjects/swig_postgres/Makefile +146 -0
  66. data/plugins/dataobjects/swig_postgres/extconf.rb +29 -0
  67. data/plugins/dataobjects/swig_postgres/postgres_c.bundle +0 -0
  68. data/plugins/dataobjects/swig_postgres/postgres_c.c +8185 -0
  69. data/plugins/dataobjects/swig_postgres/postgres_c.i +73 -0
  70. data/plugins/dataobjects/swig_sqlite/db +0 -0
  71. data/plugins/dataobjects/swig_sqlite/extconf.rb +9 -0
  72. data/plugins/dataobjects/swig_sqlite/sqlite3_c.c +4725 -0
  73. data/plugins/dataobjects/swig_sqlite/sqlite_c.i +168 -0
  74. data/rakefile.rb +45 -23
  75. data/spec/acts_as_tree_spec.rb +39 -0
  76. data/spec/associations_spec.rb +220 -0
  77. data/spec/attributes_spec.rb +15 -0
  78. data/spec/base_spec.rb +44 -0
  79. data/spec/callbacks_spec.rb +45 -0
  80. data/spec/can_has_sphinx.rb +6 -0
  81. data/spec/coersion_spec.rb +34 -0
  82. data/spec/conditions_spec.rb +49 -0
  83. data/spec/conversions_to_yaml_spec.rb +17 -0
  84. data/spec/count_command_spec.rb +11 -0
  85. data/spec/delete_command_spec.rb +1 -1
  86. data/spec/embedded_value_spec.rb +23 -0
  87. data/spec/fixtures/animals_exhibits.yaml +2 -0
  88. data/spec/fixtures/people.yaml +18 -1
  89. data/spec/{legacy.rb → legacy_spec.rb} +3 -3
  90. data/spec/load_command_spec.rb +157 -20
  91. data/spec/magic_columns_spec.rb +9 -0
  92. data/spec/mock_adapter.rb +20 -0
  93. data/spec/models/animal.rb +1 -1
  94. data/spec/models/animals_exhibit.rb +6 -0
  95. data/spec/models/exhibit.rb +2 -0
  96. data/spec/models/person.rb +26 -1
  97. data/spec/models/project.rb +19 -0
  98. data/spec/models/sales_person.rb +1 -0
  99. data/spec/models/section.rb +6 -0
  100. data/spec/models/zoo.rb +3 -1
  101. data/spec/query_spec.rb +9 -0
  102. data/spec/save_command_spec.rb +65 -1
  103. data/spec/schema_spec.rb +89 -0
  104. data/spec/single_table_inheritance_spec.rb +27 -0
  105. data/spec/spec_helper.rb +9 -55
  106. data/spec/{symbolic_operators.rb → symbolic_operators_spec.rb} +9 -5
  107. data/spec/{validates_confirmation_of.rb → validates_confirmation_of_spec.rb} +4 -3
  108. data/spec/{validates_format_of.rb → validates_format_of_spec.rb} +5 -4
  109. data/spec/{validates_length_of.rb → validates_length_of_spec.rb} +8 -7
  110. data/spec/{validates_uniqueness_of.rb → validates_uniqueness_of_spec.rb} +7 -10
  111. data/spec/{validations.rb → validations_spec.rb} +24 -6
  112. data/tasks/drivers.rb +20 -0
  113. data/tasks/fixtures.rb +42 -0
  114. metadata +181 -42
  115. data/lib/data_mapper/adapters/sql/commands/advanced_load_command.rb +0 -140
  116. data/lib/data_mapper/adapters/sql/commands/delete_command.rb +0 -113
  117. data/lib/data_mapper/adapters/sql/commands/save_command.rb +0 -141
  118. data/lib/data_mapper/adapters/sql/commands/table_exists_command.rb +0 -33
  119. data/lib/data_mapper/adapters/sql_adapter.rb +0 -163
  120. data/lib/data_mapper/associations/advanced_has_many_association.rb +0 -55
  121. data/lib/data_mapper/support/blank_slate.rb +0 -3
  122. data/lib/data_mapper/support/proc.rb +0 -69
  123. data/lib/data_mapper/support/struct.rb +0 -26
  124. data/lib/data_mapper/unit_of_work.rb +0 -38
  125. data/spec/basic_finder.rb +0 -67
  126. data/spec/belongs_to.rb +0 -47
  127. data/spec/has_and_belongs_to_many.rb +0 -25
  128. data/spec/has_many.rb +0 -34
  129. data/spec/new_record.rb +0 -24
  130. data/spec/sub_select.rb +0 -16
  131. data/spec/support/string_spec.rb +0 -7
@@ -0,0 +1,99 @@
1
+ module DataMapper
2
+ module Adapters
3
+ module Sql
4
+ module Commands
5
+
6
+ class Loader
7
+
8
+ def initialize(load_command, klass)
9
+ @load_command, @klass = load_command, klass
10
+ @columns = {}
11
+ @key = nil
12
+ @key_index = nil
13
+ @type_override_present = false
14
+ @type_override_index = nil
15
+ @type_override = nil
16
+ @session = load_command.session
17
+ @reload = load_command.reload?
18
+ @set = []
19
+ end
20
+
21
+ def add_column(column, index)
22
+ if column.key?
23
+ @key = column
24
+ @key_index = index
25
+ end
26
+
27
+ if column.type == :class
28
+ @type_override_present = true
29
+ @type_override_index = index
30
+ @type_override = column
31
+ end
32
+
33
+ @columns[index] = column
34
+
35
+ self
36
+ end
37
+
38
+ def materialize(values)
39
+
40
+ instance_id = @key.type_cast_value(values[@key_index])
41
+ instance = if @type_override_present
42
+ create_instance(instance_id, @type_override.type_cast_value(values[@type_override_index]))
43
+ else
44
+ create_instance(instance_id)
45
+ end
46
+
47
+ @klass.callbacks.execute(:before_materialize, instance)
48
+
49
+ original_hashes = instance.original_hashes
50
+
51
+ @columns.each_pair do |index, column|
52
+ # This may be a little confusing, but we're
53
+ # setting both the original-hash value, and the
54
+ # instance-variable through method chaining to avoid
55
+ # lots of extra short-lived local variables.
56
+ original_hashes[column.name] = instance.instance_variable_set(
57
+ column.instance_variable_name,
58
+ column.type_cast_value(values[index])
59
+ ).hash
60
+ end
61
+
62
+ instance.instance_variable_set(:@loaded_set, @set)
63
+ @set << instance
64
+
65
+ @klass.callbacks.execute(:after_materialize, instance)
66
+
67
+ return instance
68
+ end
69
+
70
+ def loaded_set
71
+ @set
72
+ end
73
+
74
+ private
75
+
76
+ def create_instance(instance_id, instance_type = @klass)
77
+ instance = @session.identity_map.get(@klass, instance_id)
78
+
79
+ if instance.nil? || @reload
80
+ instance = instance_type.new() if instance.nil?
81
+ instance.instance_variable_set(:@__key, instance_id)
82
+ instance.instance_variable_set(:@new_record, false)
83
+ @session.identity_map.set(instance)
84
+ elsif instance.new_record?
85
+ instance.instance_variable_set(:@__key, instance_id)
86
+ instance.instance_variable_set(:@new_record, false)
87
+ end
88
+
89
+ instance.session = @session
90
+
91
+ return instance
92
+ end
93
+
94
+ end
95
+
96
+ end # module Commands
97
+ end # module Sql
98
+ end # module Adapters
99
+ end # module DataMapper
@@ -0,0 +1,30 @@
1
+ module DataMapper
2
+ module Adapters
3
+ module Sql
4
+ module Mappings
5
+
6
+ class AssociationsSet
7
+
8
+ include Enumerable
9
+
10
+ def initialize
11
+ @set = {}
12
+ end
13
+
14
+ def <<(association)
15
+ @set[association.name] = association
16
+ end
17
+
18
+ def [](name)
19
+ @set[name]
20
+ end
21
+
22
+ def each
23
+ @set.each { |name, association| yield(association) }
24
+ end
25
+ end
26
+
27
+ end # module Mappings
28
+ end # module Sql
29
+ end # module Adapters
30
+ end # module DataMapper
@@ -7,13 +7,19 @@ module DataMapper
7
7
  # Ordinal, Length/Size, Nullability are just a few.
8
8
  class Column
9
9
 
10
- attr_accessor :name, :type, :options
10
+ attr_accessor :table, :name, :type, :options
11
11
 
12
12
  def initialize(adapter, table, name, type, options = {})
13
13
  @adapter = adapter
14
14
  @table = table
15
15
  @name, @type, @options = name.to_sym, type, options
16
16
 
17
+ @key = (@options[:key] == true)
18
+ @nullable = @options.has_key?(:nullable) ? @options[:nullable] : !@key
19
+ @lazy = @options.has_key?(:lazy) ? @options[:lazy] : @type == :text
20
+ @serial = (@key == true && @type == :integer && @options[:serial] != false)
21
+ @default = @options[:default]
22
+
17
23
  (class << self; self end).class_eval <<-EOS
18
24
  def type_cast_value(value)
19
25
  @adapter.type_cast_#{type}(value)
@@ -22,24 +28,32 @@ module DataMapper
22
28
  end
23
29
 
24
30
  def lazy=(value)
25
- @options[:lazy] = value
31
+ @lazy = value
26
32
  end
27
33
 
28
34
  # Determines if the field should be lazy loaded.
29
35
  # You can set this explicitly, or accept the default,
30
36
  # which is false for all but text fields.
31
37
  def lazy?
32
- @options[:lazy] || (type == :text)
38
+ @lazy
33
39
  end
34
40
 
35
41
  def nullable?
36
- @options[:nullable] || true
42
+ @nullable
37
43
  end
38
44
 
39
45
  def key?
40
- @options[:key] || false
46
+ @key
41
47
  end
42
-
48
+
49
+ def serial?
50
+ @serial
51
+ end
52
+
53
+ def default
54
+ @default
55
+ end
56
+
43
57
  def to_sym
44
58
  @name
45
59
  end
@@ -82,6 +96,54 @@ module DataMapper
82
96
  def inspect
83
97
  "#<%s:0x%x @name=%s, @type=%s, @options=%s>" % [self.class.name, (object_id * 2), to_sql, type.inspect, options.inspect]
84
98
  end
99
+
100
+ def to_long_form
101
+ @to_long_form || begin
102
+ @to_long_form = "#{to_sql} #{type_declaration}"
103
+
104
+ unless nullable? || not_null_declaration.blank?
105
+ @to_long_form << " #{not_null_declaration}"
106
+ end
107
+
108
+ if key? && !primary_key_declaration.blank?
109
+ @to_long_form << " #{primary_key_declaration}"
110
+ end
111
+
112
+ if serial? && !serial_declaration.blank?
113
+ @to_long_form << " #{serial_declaration}"
114
+ end
115
+
116
+ if default && !default_declaration.blank?
117
+ @to_long_form << " #{default_declaration}"
118
+ end
119
+
120
+ @to_long_form
121
+ end
122
+ end
123
+
124
+ private
125
+
126
+ def primary_key_declaration
127
+ "PRIMARY KEY"
128
+ end
129
+
130
+ def type_declaration
131
+ sql = "#{@adapter.class::TYPES[type] || type}"
132
+ sql << "(#{size})" unless size.nil?
133
+ sql
134
+ end
135
+
136
+ def not_null_declaration
137
+ "NOT NULL"
138
+ end
139
+
140
+ def serial_declaration
141
+ "AUTO_INCREMENT"
142
+ end
143
+
144
+ def default_declaration
145
+ "DEFAULT #{default}"
146
+ end
85
147
 
86
148
  end
87
149
 
@@ -7,11 +7,14 @@ module DataMapper
7
7
 
8
8
  class Schema
9
9
 
10
- def initialize(adapter)
10
+ attr_reader :name
11
+
12
+ def initialize(adapter, database_name)
13
+ @name = database_name
11
14
  @adapter = adapter
12
- @tables = Hash.new { |h,k| h[k] = Table.new(@adapter, k) }
15
+ @tables = Hash.new { |h,k| h[k] = adapter.class::Mappings::Table.new(@adapter, k) }
13
16
  end
14
-
17
+
15
18
  def [](klass)
16
19
  @tables[klass]
17
20
  end
@@ -1,4 +1,5 @@
1
1
  require File.dirname(__FILE__) + '/column'
2
+ require File.dirname(__FILE__) + '/associations_set'
2
3
 
3
4
  module DataMapper
4
5
  module Adapters
@@ -7,47 +8,41 @@ module DataMapper
7
8
 
8
9
  class Table
9
10
 
10
- class Association
11
- def initialize(association_name, constant_name)
12
- @association_name, @constant_name = association_name, constant_name
13
- end
14
-
15
- def name
16
- @association_name
17
- end
18
-
19
- def constant
20
- @constant || @constant = begin
21
- Object.const_get(@constant_name)
22
- end
23
- end
11
+ attr_reader :klass, :name
12
+
13
+ def initialize(adapter, klass_or_name)
14
+ raise "\"klass_or_name\" must not be nil!" if klass_or_name.nil?
24
15
 
25
- end
16
+ @klass = klass_or_name.kind_of?(String) ? nil : klass_or_name
17
+ @klass_or_name = klass_or_name
26
18
 
27
- attr_reader :klass
28
-
29
- def initialize(adapter, setup_klass)
30
- raise "\"setup_klass\" must not be nil!" if setup_klass.nil?
31
19
  @adapter = adapter
32
- @klass = setup_klass
33
20
  @columns = []
34
21
  @columns_hash = Hash.new { |h,k| h[k] = @columns.find { |c| c.name == k } }
35
22
  @columns_by_column_name = Hash.new { |h,k| h[k.to_s] = @columns.find { |c| c.column_name == k.to_s } }
36
- @has_many = []
37
- @associations = []
23
+
24
+ @associations = AssociationsSet.new
25
+
26
+ @multi_class = false
27
+
28
+ if @klass && @klass.ancestors.include?(DataMapper::Base) && @klass.superclass != DataMapper::Base
29
+ @adapter.table(@klass.superclass).columns.each do |column|
30
+ self.add_column(column.name, column.type, column.options)
31
+ end
32
+ end
33
+ end
34
+
35
+ def multi_class?
36
+ @multi_class
38
37
  end
39
38
 
40
39
  def associations
41
40
  @associations
42
41
  end
43
-
44
- # def has_many(association_name, options)
45
- # @associtaions << Association.new(association_name, Inflector.classify(Inflector.singularize(association_name.to_s)))
46
- # end
47
42
 
48
- # def has_many_associations
49
- # @has_many
50
- # end
43
+ def reflect_columns
44
+ @adapter.reflect_columns(self)
45
+ end
51
46
 
52
47
  def columns
53
48
  key if @key.nil?
@@ -59,11 +54,14 @@ module DataMapper
59
54
  end
60
55
 
61
56
  def drop!
62
- @adapter.delete(@klass, :drop => true) if exists?
57
+ @adapter.drop(database, self) if exists?
63
58
  end
64
59
 
65
- def create!
66
- @adapter.save(database, @klass) unless exists?
60
+ def create!(force = false)
61
+ unless exists? || force
62
+ drop! if force
63
+ @adapter.create_table(self)
64
+ end
67
65
  end
68
66
 
69
67
  def key
@@ -86,8 +84,9 @@ module DataMapper
86
84
 
87
85
  if column.nil?
88
86
  reset_derived_columns!
89
- column = Column.new(@adapter, self, column_name, type, options)
87
+ column = @adapter.class::Mappings::Column.new(@adapter, self, column_name, type, options)
90
88
  @columns.send(column_name == :id ? :unshift : :push, column)
89
+ @multi_class = true if column_name == :type
91
90
  end
92
91
 
93
92
  return column
@@ -103,13 +102,18 @@ module DataMapper
103
102
  end
104
103
 
105
104
  def name
106
- @name || begin
107
- @name = if @klass.superclass != DataMapper::Base && @klass.superclass != Object
108
- @adapter[@klass.superclass].name
105
+ @name || @name = if @klass_or_name.kind_of?(String)
106
+ @klass_or_name
107
+ elsif @klass_or_name.kind_of?(Class)
108
+ if @klass_or_name.superclass != DataMapper::Base \
109
+ && @klass_or_name.ancestors.include?(DataMapper::Base)
110
+ @adapter.table(@klass_or_name.superclass).name
109
111
  else
110
- Inflector.tableize(@klass.name)
111
- end.freeze
112
- end
112
+ Inflector.tableize(@klass_or_name.name)
113
+ end
114
+ else
115
+ raise "+klass_or_name+ (#{@klass_or_name.inspect}) must be a Class or a string containing the name of a table"
116
+ end.freeze
113
117
  end
114
118
 
115
119
  def name=(value)
@@ -117,15 +121,40 @@ module DataMapper
117
121
  end
118
122
 
119
123
  def default_foreign_key
120
- @default_foreign_key || (@default_foreign_key = "#{String::memoized_underscore(Inflector.singularize(name))}_id".freeze)
124
+ @default_foreign_key || (@default_foreign_key = "#{Inflector.underscore(Inflector.singularize(name))}_id".freeze)
121
125
  end
122
126
 
123
127
  def to_sql
124
- @to_sql || (@to_sql = @adapter.quote_table_name(name).freeze)
128
+ @to_sql || @to_sql = quote_table.freeze
129
+ end
130
+
131
+ def to_create_table_sql
132
+ @to_create_table_sql || @to_create_table_sql = begin
133
+ "CREATE TABLE #{to_sql} (#{columns.map { |c| c.to_long_form }.join(', ')})"
134
+ end
135
+ end
136
+
137
+ def to_exists_sql
138
+ @to_exists_sql || @to_exists_sql = <<-EOS.compress_lines
139
+ SELECT TABLE_NAME
140
+ FROM INFORMATION_SCHEMA.TABLES
141
+ WHERE TABLE_NAME = #{@adapter.quote_value(name)}
142
+ AND TABLE_SCHEMA = #{@adapter.quote_value(@adapter.schema.name)}
143
+ EOS
144
+ end
145
+
146
+ def quote_table
147
+ @adapter.quote_table_name(name)
125
148
  end
126
149
 
127
150
  def inspect
128
- "#<%s:0x%x @klass=%s, @name=%s, @columns=%s>" % [self.class.name, (object_id * 2), @klass.name, to_sql, @columns.inspect]
151
+ "#<%s:0x%x @klass=%s, @name=%s, @columns=%s>" % [
152
+ self.class.name,
153
+ (object_id * 2),
154
+ klass.inspect,
155
+ to_sql,
156
+ @columns.inspect
157
+ ]
129
158
  end
130
159
 
131
160
  private
@@ -26,9 +26,15 @@ module DataMapper
26
26
  when String then "'#{value.gsub("'", "''")}'"
27
27
  when Class then "'#{value.name}'"
28
28
  when Date then "'#{value.to_s}'"
29
- when Time, DateTime then "'#{value.strftime("%Y-%m-%d %H:%M:%S")}'"
29
+ when Time, DateTime then "'#{value.utc.strftime("%Y-%m-%d %H:%M:%S")}'"
30
30
  when TrueClass, FalseClass then value.to_s.upcase
31
- else raise "Don't know how to quote #{value.inspect}"
31
+ when Array then "(#{value.map { |entry| quote_value(entry) }.join(', ')})"
32
+ else
33
+ if value.respond_to?(:to_sql)
34
+ value.to_sql
35
+ else
36
+ raise "Don't know how to quote #{value.inspect}"
37
+ end
32
38
  end
33
39
  end
34
40