datamapper 0.1.1 → 0.2.0

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