datamapper 0.2.0 → 0.2.1
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 +20 -1
- data/environment.rb +5 -3
- data/lib/data_mapper.rb +42 -23
- data/lib/data_mapper/adapters/data_object_adapter.rb +76 -73
- data/lib/data_mapper/adapters/mysql_adapter.rb +15 -1
- data/lib/data_mapper/adapters/postgresql_adapter.rb +1 -1
- data/lib/data_mapper/adapters/sql/commands/load_command.rb +242 -28
- data/lib/data_mapper/adapters/sql/mappings/column.rb +18 -1
- data/lib/data_mapper/adapters/sql/mappings/table.rb +20 -32
- data/lib/data_mapper/adapters/sql/quoting.rb +40 -7
- data/lib/data_mapper/adapters/sqlite3_adapter.rb +7 -1
- data/lib/data_mapper/associations/has_and_belongs_to_many_association.rb +6 -1
- data/lib/data_mapper/associations/has_many_association.rb +1 -1
- data/lib/data_mapper/context.rb +4 -2
- data/lib/data_mapper/database.rb +2 -12
- data/lib/data_mapper/support/active_record_impersonation.rb +1 -1
- data/lib/data_mapper/support/blank.rb +2 -2
- data/lib/data_mapper/support/serialization.rb +60 -4
- data/lib/data_mapper/support/string.rb +24 -2
- data/lib/data_mapper/validations/validation_errors.rb +6 -3
- data/performance.rb +20 -5
- data/plugins/dataobjects/Rakefile +2 -0
- data/plugins/dataobjects/do.rb +18 -8
- data/plugins/dataobjects/do_mysql.rb +57 -24
- data/plugins/dataobjects/do_postgres.rb +3 -7
- data/plugins/dataobjects/do_sqlite3.rb +18 -17
- data/plugins/dataobjects/swig_mysql/Makefile +146 -0
- data/plugins/dataobjects/swig_mysql/extconf.rb +13 -1
- data/plugins/dataobjects/swig_mysql/mkmf.log +24 -0
- data/plugins/dataobjects/swig_mysql/mysql_c.bundle +0 -0
- data/plugins/dataobjects/swig_mysql/mysql_c.c +303 -2501
- data/plugins/dataobjects/swig_mysql/mysql_c.i +63 -4
- data/plugins/dataobjects/swig_mysql/mysql_c.o +0 -0
- data/profile_data_mapper.rb +4 -4
- data/rakefile.rb +13 -7
- data/spec/acts_as_tree_spec.rb +2 -0
- data/spec/associations_spec.rb +12 -0
- data/spec/attributes_spec.rb +2 -0
- data/spec/base_spec.rb +2 -0
- data/spec/callbacks_spec.rb +2 -0
- data/spec/can_has_sphinx.rb +0 -1
- data/spec/coersion_spec.rb +10 -3
- data/spec/column_spec.rb +23 -0
- data/spec/conditions_spec.rb +18 -18
- data/spec/count_command_spec.rb +2 -0
- data/spec/dataobjects_spec.rb +26 -0
- data/spec/delete_command_spec.rb +2 -0
- data/spec/embedded_value_spec.rb +2 -0
- data/spec/fixtures/people.yaml +1 -1
- data/spec/fixtures/posts.yaml +3 -0
- data/spec/legacy_spec.rb +2 -0
- data/spec/load_command_spec.rb +28 -2
- data/spec/magic_columns_spec.rb +2 -0
- data/spec/models/person.rb +1 -1
- data/spec/models/post.rb +8 -0
- data/spec/query_spec.rb +2 -0
- data/spec/save_command_spec.rb +2 -0
- data/spec/schema_spec.rb +2 -0
- data/spec/serialization_spec.rb +58 -0
- data/spec/single_table_inheritance_spec.rb +2 -0
- data/spec/symbolic_operators_spec.rb +2 -0
- data/spec/validates_confirmation_of_spec.rb +2 -0
- data/spec/validates_format_of_spec.rb +2 -0
- data/spec/validates_length_of_spec.rb +2 -0
- data/spec/validates_uniqueness_of_spec.rb +2 -0
- data/spec/validations_spec.rb +2 -0
- data/tasks/fixtures.rb +15 -10
- metadata +10 -13
- data/lib/data_mapper/adapters/sql/commands/conditions.rb +0 -130
- data/lib/data_mapper/adapters/sql/commands/loader.rb +0 -99
- data/plugins/dataobjects/swig_mysql/do_mysql.bundle +0 -0
- data/spec/conversions_to_yaml_spec.rb +0 -17
@@ -9,10 +9,11 @@ module DataMapper
|
|
9
9
|
|
10
10
|
attr_accessor :table, :name, :type, :options
|
11
11
|
|
12
|
-
def initialize(adapter, table, name, type, options = {})
|
12
|
+
def initialize(adapter, table, name, type, ordinal, options = {})
|
13
13
|
@adapter = adapter
|
14
14
|
@table = table
|
15
15
|
@name, @type, @options = name.to_sym, type, options
|
16
|
+
@ordinal = ordinal
|
16
17
|
|
17
18
|
@key = (@options[:key] == true)
|
18
19
|
@nullable = @options.has_key?(:nullable) ? @options[:nullable] : !@key
|
@@ -26,6 +27,10 @@ module DataMapper
|
|
26
27
|
end
|
27
28
|
EOS
|
28
29
|
end
|
30
|
+
|
31
|
+
def ordinal
|
32
|
+
@ordinal
|
33
|
+
end
|
29
34
|
|
30
35
|
def lazy=(value)
|
31
36
|
@lazy = value
|
@@ -121,6 +126,18 @@ module DataMapper
|
|
121
126
|
end
|
122
127
|
end
|
123
128
|
|
129
|
+
def <=>(other)
|
130
|
+
ordinal <=> other.ordinal
|
131
|
+
end
|
132
|
+
|
133
|
+
def hash
|
134
|
+
name.hash
|
135
|
+
end
|
136
|
+
|
137
|
+
def eql?(other)
|
138
|
+
name == other.name
|
139
|
+
end
|
140
|
+
|
124
141
|
private
|
125
142
|
|
126
143
|
def primary_key_declaration
|
@@ -17,9 +17,8 @@ module DataMapper
|
|
17
17
|
@klass_or_name = klass_or_name
|
18
18
|
|
19
19
|
@adapter = adapter
|
20
|
-
@columns =
|
21
|
-
@columns_hash = Hash.new { |h,k| h[k] =
|
22
|
-
@columns_by_column_name = Hash.new { |h,k| h[k.to_s] = @columns.find { |c| c.column_name == k.to_s } }
|
20
|
+
@columns = SortedSet.new
|
21
|
+
@columns_hash = Hash.new { |h,k| h[k] = columns.find { |c| c.name == k } }
|
23
22
|
|
24
23
|
@associations = AssociationsSet.new
|
25
24
|
|
@@ -65,40 +64,36 @@ module DataMapper
|
|
65
64
|
end
|
66
65
|
|
67
66
|
def key
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
67
|
+
@key || begin
|
68
|
+
@key = @columns.find { |column| column.key? }
|
69
|
+
|
70
|
+
if @key.nil?
|
71
|
+
@key = add_column(:id, :integer, :key => true, :ordinal => -1)
|
72
72
|
@klass.send(:attr_reader, :id) unless @klass.methods.include?(:id)
|
73
|
-
column
|
74
|
-
else
|
75
|
-
key_column
|
76
73
|
end
|
74
|
+
|
75
|
+
@key
|
77
76
|
end
|
78
|
-
|
79
|
-
@key
|
80
77
|
end
|
81
78
|
|
82
79
|
def add_column(column_name, type, options)
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
@columns.send(column_name == :id ? :unshift : :push, column)
|
89
|
-
@multi_class = true if column_name == :type
|
80
|
+
|
81
|
+
column_ordinal = if options.is_a?(Hash) && options.has_key?(:ordinal)
|
82
|
+
options.delete(:ordinal)
|
83
|
+
else
|
84
|
+
@columns.size
|
90
85
|
end
|
86
|
+
|
87
|
+
column = @adapter.class::Mappings::Column.new(@adapter, self, column_name, type, column_ordinal, options)
|
88
|
+
@columns << column
|
89
|
+
|
90
|
+
@multi_class = true if column_name == :type
|
91
91
|
|
92
92
|
return column
|
93
93
|
end
|
94
94
|
|
95
95
|
def [](column_name)
|
96
|
-
|
97
|
-
@columns_hash[column_name.kind_of?(Symbol) ? column_name : column_name.to_sym]
|
98
|
-
end
|
99
|
-
|
100
|
-
def find_by_column_name(column_name)
|
101
|
-
@columns_by_column_name[column_name.kind_of?(String) ? column_name : column_name.to_s]
|
96
|
+
@columns_hash[column_name.to_sym]
|
102
97
|
end
|
103
98
|
|
104
99
|
def name
|
@@ -157,13 +152,6 @@ module DataMapper
|
|
157
152
|
]
|
158
153
|
end
|
159
154
|
|
160
|
-
private
|
161
|
-
def reset_derived_columns!
|
162
|
-
@columns_hash.clear
|
163
|
-
@columns_by_column_name.clear
|
164
|
-
@key = nil
|
165
|
-
end
|
166
|
-
|
167
155
|
end
|
168
156
|
|
169
157
|
end
|
@@ -22,13 +22,14 @@ module DataMapper
|
|
22
22
|
return 'NULL' if value.nil?
|
23
23
|
|
24
24
|
case value
|
25
|
-
when Numeric then value
|
26
|
-
when String then
|
27
|
-
when Class then
|
28
|
-
when
|
29
|
-
when
|
30
|
-
when
|
31
|
-
when
|
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)
|
32
33
|
else
|
33
34
|
if value.respond_to?(:to_sql)
|
34
35
|
value.to_sql
|
@@ -37,6 +38,38 @@ module DataMapper
|
|
37
38
|
end
|
38
39
|
end
|
39
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
|
40
73
|
|
41
74
|
end # module Quoting
|
42
75
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'data_mapper/adapters/data_object_adapter'
|
2
2
|
begin
|
3
3
|
require 'do_sqlite3'
|
4
|
-
rescue
|
4
|
+
rescue LoadError
|
5
5
|
STDERR.puts <<-EOS
|
6
6
|
You must install the DataObjects::SQLite3 driver.
|
7
7
|
rake dm:install:sqlite3
|
@@ -29,6 +29,12 @@ module DataMapper
|
|
29
29
|
conn.open
|
30
30
|
return conn
|
31
31
|
end
|
32
|
+
|
33
|
+
def truncate(session, name)
|
34
|
+
result = execute("DELETE FROM #{table(name).to_sql}")
|
35
|
+
session.identity_map.clear!(name)
|
36
|
+
result.to_i > 0
|
37
|
+
end
|
32
38
|
|
33
39
|
module Mappings
|
34
40
|
class Table
|
@@ -150,7 +150,12 @@ module DataMapper
|
|
150
150
|
|
151
151
|
# Locate the column for the left-key.
|
152
152
|
unless left_key_index
|
153
|
-
|
153
|
+
columns.each_with_index do |column, index|
|
154
|
+
if column.name == association.left_foreign_key.name
|
155
|
+
left_key_index = index
|
156
|
+
break
|
157
|
+
end
|
158
|
+
end
|
154
159
|
end
|
155
160
|
|
156
161
|
if instance.kind_of?(association_constant)
|
data/lib/data_mapper/context.rb
CHANGED
@@ -35,9 +35,11 @@ module DataMapper
|
|
35
35
|
raise ArgumentError.new('Session#first takes a class, and optional type_or_id and/or options arguments')
|
36
36
|
end
|
37
37
|
|
38
|
-
|
38
|
+
# Account for undesired behaviour in MySQL that returns the
|
39
|
+
# last inserted row when the WHERE clause contains a "#{primary_key} IS NULL".
|
40
|
+
return nil if options.has_key?(:id) && options[:id] == nil
|
39
41
|
|
40
|
-
@adapter.load(self, klass, options)
|
42
|
+
@adapter.load(self, klass, options).first
|
41
43
|
end
|
42
44
|
|
43
45
|
def all(klass, options = {})
|
data/lib/data_mapper/database.rb
CHANGED
@@ -87,7 +87,6 @@ module DataMapper
|
|
87
87
|
class Database
|
88
88
|
|
89
89
|
@databases = {}
|
90
|
-
@context = []
|
91
90
|
|
92
91
|
# Allows you to access any of the named databases you have already setup.
|
93
92
|
#
|
@@ -101,7 +100,7 @@ module DataMapper
|
|
101
100
|
#
|
102
101
|
# This is what gives us thread safety, boys and girls
|
103
102
|
def self.context
|
104
|
-
|
103
|
+
Thread::current[:context] || Thread::current[:context] = []
|
105
104
|
end
|
106
105
|
|
107
106
|
# Setup creates a database and sets all of your properties for that database.
|
@@ -157,11 +156,10 @@ module DataMapper
|
|
157
156
|
# Creates a new database object with the name you specify, and a default set of options.
|
158
157
|
#
|
159
158
|
# The default options are as follows:
|
160
|
-
# {
|
159
|
+
# { :host => 'localhost', :database => nil, :username => 'root', :password => '', :adapter = nil }
|
161
160
|
def initialize(name)
|
162
161
|
@name = name
|
163
162
|
|
164
|
-
@single_threaded = true
|
165
163
|
@adapter = nil
|
166
164
|
@host = 'localhost'
|
167
165
|
@database = nil
|
@@ -175,16 +173,8 @@ module DataMapper
|
|
175
173
|
end
|
176
174
|
|
177
175
|
attr_reader :name, :adapter
|
178
|
-
attr_writer :single_threaded
|
179
176
|
attr_accessor :host, :database, :schema_search_path, :username, :password, :log_stream, :log_level, :index_path, :socket
|
180
177
|
|
181
|
-
# Returns true or false
|
182
|
-
#
|
183
|
-
# NOTE: single_threaded is true unless explicitly set to false.
|
184
|
-
def single_threaded?
|
185
|
-
@single_threaded
|
186
|
-
end
|
187
|
-
|
188
178
|
# Allows us to set the adapter for this database object. It can only be set once, and expects two types of values.
|
189
179
|
#
|
190
180
|
# You may pass in either a class inheriting from DataMapper::Adapters::AbstractAdapter
|
@@ -12,7 +12,7 @@ module DataMapper
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def reload!
|
15
|
-
session.first(self.class, key, :select =>
|
15
|
+
session.first(self.class, key, :select => original_hashes.keys, :reload => true)
|
16
16
|
end
|
17
17
|
|
18
18
|
def reload
|
@@ -1,13 +1,69 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'json/ext'
|
5
|
+
rescue LoadError
|
6
|
+
require 'json/pure'
|
7
|
+
end
|
8
|
+
|
1
9
|
module DataMapper
|
2
10
|
module Support
|
3
11
|
module Serialization
|
4
12
|
|
5
|
-
def to_yaml
|
6
|
-
|
7
|
-
|
8
|
-
|
13
|
+
def to_yaml(opts = {})
|
14
|
+
|
15
|
+
YAML::quick_emit( object_id, opts ) do |out|
|
16
|
+
out.map(nil, to_yaml_style ) do |map|
|
17
|
+
session.table(self).columns.each do |column|
|
18
|
+
lazy_load!(column.name) if column.lazy?
|
19
|
+
value = instance_variable_get(column.instance_variable_name)
|
20
|
+
map.add(column.to_s, value.is_a?(Class) ? value.to_s : value)
|
21
|
+
end
|
22
|
+
(self.instance_variable_get("@yaml_added") || []).each do |k,v|
|
23
|
+
map.add(k.to_s, v)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
9
28
|
end
|
10
29
|
|
30
|
+
def to_xml
|
31
|
+
doc = REXML::Document.new
|
32
|
+
|
33
|
+
table = session.table(self.class)
|
34
|
+
root = doc.add_element(Inflector.underscore(self.class.name))
|
35
|
+
|
36
|
+
key_attribute = root.attributes << REXML::Attribute.new(table.key.to_s, key)
|
37
|
+
|
38
|
+
# Single-quoted attributes are ugly. :p
|
39
|
+
# NOTE: I don't want to break existing REXML specs for everyone, so I'm
|
40
|
+
# overwriting REXML::Attribute#to_string just for this instance.
|
41
|
+
def key_attribute.to_string
|
42
|
+
%Q[#@expanded_name="#{to_s().gsub(/"/, '"')}"]
|
43
|
+
end
|
44
|
+
|
45
|
+
table.columns.each do |column|
|
46
|
+
next if column.key?
|
47
|
+
value = send(column.name)
|
48
|
+
node = root.add_element(column.to_s)
|
49
|
+
node << REXML::Text.new(value.to_s) unless value.nil?
|
50
|
+
end
|
51
|
+
|
52
|
+
doc.to_s
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_json(*a)
|
56
|
+
table = session.table(self.class)
|
57
|
+
|
58
|
+
result = '{ '
|
59
|
+
|
60
|
+
result << table.columns.map do |column|
|
61
|
+
"#{column.name.to_json}: #{send(column.name).to_json(*a)}"
|
62
|
+
end.join(', ')
|
63
|
+
|
64
|
+
result << ' }'
|
65
|
+
result
|
66
|
+
end
|
11
67
|
end
|
12
68
|
end # module Support
|
13
69
|
end # module DataMapper
|
@@ -26,8 +26,30 @@ module DataMapper
|
|
26
26
|
# FROM users
|
27
27
|
# QUERY
|
28
28
|
# => "SELECT name FROM users"
|
29
|
-
def compress_lines
|
30
|
-
|
29
|
+
def compress_lines(spaced = true)
|
30
|
+
split($/).map { |line| line.strip }.join(spaced ? ' ' : '')
|
31
|
+
end
|
32
|
+
|
33
|
+
def margin(indicator = nil)
|
34
|
+
target = dup
|
35
|
+
lines = target.split($/)
|
36
|
+
|
37
|
+
if indicator.nil?
|
38
|
+
min_margin = nil
|
39
|
+
lines.each do |line|
|
40
|
+
if line =~ /(\s+)/ && (min_margin.nil? || $1.size < min_margin)
|
41
|
+
min_margin = $1.size
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
lines.map do |line|
|
46
|
+
line.sub(/^\s{#{min_margin}}/, '')
|
47
|
+
end.join($/)
|
48
|
+
else
|
49
|
+
lines.map do |line|
|
50
|
+
line.sub(/^.*?#{"\\" + indicator}/, '')
|
51
|
+
end.join($/)
|
52
|
+
end
|
31
53
|
end
|
32
54
|
|
33
55
|
end # module String
|
data/performance.rb
CHANGED
@@ -4,7 +4,8 @@ require 'active_record'
|
|
4
4
|
ActiveRecord::Base.establish_connection :adapter => 'mysql',
|
5
5
|
:username => 'root',
|
6
6
|
:password => '',
|
7
|
-
:database => 'data_mapper_1'
|
7
|
+
:database => 'data_mapper_1',
|
8
|
+
:socker => "/tmp/mysql.sock"
|
8
9
|
|
9
10
|
ActiveRecord::Base.find_by_sql('SELECT 1')
|
10
11
|
|
@@ -21,7 +22,8 @@ require 'lib/data_mapper'
|
|
21
22
|
DataMapper::Database.setup({
|
22
23
|
:adapter => 'mysql',
|
23
24
|
:database => 'data_mapper_1',
|
24
|
-
:username => 'root'
|
25
|
+
:username => 'root',
|
26
|
+
:socket => "/tmp/mysql.sock"
|
25
27
|
})
|
26
28
|
|
27
29
|
class DMAnimal < DataMapper::Base
|
@@ -89,16 +91,16 @@ Benchmark::send(ENV['BM'] || :bmbm, 40) do |x|
|
|
89
91
|
end
|
90
92
|
|
91
93
|
x.report('ActiveRecord:all') do
|
92
|
-
N.times {
|
94
|
+
N.times { ARZoo.find(:all).each { |a| a.name } }
|
93
95
|
end
|
94
96
|
|
95
97
|
x.report('DataMapper:all') do
|
96
|
-
N.times {
|
98
|
+
N.times { Zoo.all.each { |a| a.name } }
|
97
99
|
end
|
98
100
|
|
99
101
|
x.report('DataMapper:all:in-session') do
|
100
102
|
database do
|
101
|
-
N.times {
|
103
|
+
N.times { Zoo.all.each { |a| a.name } }
|
102
104
|
end
|
103
105
|
end
|
104
106
|
|
@@ -212,6 +214,19 @@ Benchmark::send(ENV['BM'] || :bmbm, 40) do |x|
|
|
212
214
|
end
|
213
215
|
end
|
214
216
|
|
217
|
+
x.report('DataMapper:raw-query') do
|
218
|
+
N.times do
|
219
|
+
database.adapter.connection do |db|
|
220
|
+
|
221
|
+
command = db.create_command("SELECT * FROM zoos")
|
222
|
+
|
223
|
+
command.execute_reader do |reader|
|
224
|
+
reader.each { reader.current_row }
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
215
230
|
x.report('ActiveRecord:accessors') do
|
216
231
|
person = ARPerson.find(:first)
|
217
232
|
|