dm-core 0.9.3 → 0.9.4
Sign up to get free protection for your applications and to get access to all the features.
- data/CONTRIBUTING +51 -0
- data/FAQ +26 -8
- data/Manifest.txt +2 -0
- data/Rakefile +2 -1
- data/lib/dm-core.rb +8 -3
- data/lib/dm-core/adapters/abstract_adapter.rb +2 -2
- data/lib/dm-core/adapters/data_objects_adapter.rb +18 -4
- data/lib/dm-core/adapters/mysql_adapter.rb +8 -4
- data/lib/dm-core/adapters/postgres_adapter.rb +12 -3
- data/lib/dm-core/adapters/sqlite3_adapter.rb +1 -1
- data/lib/dm-core/associations.rb +1 -0
- data/lib/dm-core/associations/many_to_one.rb +7 -1
- data/lib/dm-core/associations/one_to_many.rb +1 -1
- data/lib/dm-core/associations/relationship.rb +14 -13
- data/lib/dm-core/auto_migrations.rb +57 -5
- data/lib/dm-core/collection.rb +2 -1
- data/lib/dm-core/logger.rb +5 -6
- data/lib/dm-core/model.rb +17 -2
- data/lib/dm-core/naming_conventions.rb +57 -25
- data/lib/dm-core/property.rb +6 -5
- data/lib/dm-core/query.rb +20 -16
- data/lib/dm-core/resource.rb +24 -7
- data/lib/dm-core/version.rb +1 -1
- data/script/performance.rb +2 -1
- data/script/profile.rb +1 -0
- data/spec/integration/association_spec.rb +131 -81
- data/spec/integration/association_through_spec.rb +87 -42
- data/spec/integration/associations/many_to_many_spec.rb +76 -16
- data/spec/integration/associations/many_to_one_spec.rb +6 -1
- data/spec/integration/associations/one_to_many_spec.rb +69 -0
- data/spec/integration/collection_spec.rb +7 -0
- data/spec/integration/query_spec.rb +24 -7
- data/spec/integration/resource_spec.rb +59 -10
- data/spec/integration/sti_spec.rb +23 -0
- data/spec/models/zoo.rb +2 -3
- data/spec/spec_helper.rb +9 -1
- data/spec/unit/adapters/postgres_adapter_spec.rb +9 -1
- data/spec/unit/associations/many_to_one_spec.rb +6 -0
- data/spec/unit/auto_migrations_spec.rb +2 -1
- data/spec/unit/naming_conventions_spec.rb +26 -18
- data/spec/unit/property_spec.rb +3 -4
- data/spec/unit/resource_spec.rb +19 -22
- data/tasks/gemspec.rb +23 -0
- data/tasks/hoe.rb +9 -1
- metadata +6 -14
data/CONTRIBUTING
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# NOTE: This is a work in progress. As of July 24, it applies only to dm-core.
|
2
|
+
|
3
|
+
# Contributing to Edge DataMapper
|
4
|
+
|
5
|
+
We have now implemented Hoe throughout the DataMapper suite, so there will be a
|
6
|
+
handful of new procedures for contributing to our git repositories. I'll give
|
7
|
+
you a run through of how to set up your machine, and then provide a few
|
8
|
+
commands that should be run before committing or pushing changes.
|
9
|
+
|
10
|
+
## Installing and configuring Hoe
|
11
|
+
|
12
|
+
The first step is to install hoe. You'll need at least version 1.7.0.
|
13
|
+
|
14
|
+
(sudo) gem install hoe --include-dependencies
|
15
|
+
|
16
|
+
Now you'll need to configure hoe. You'll need to run this from inside of
|
17
|
+
dm-core, or one of the other DataMapper projects.
|
18
|
+
|
19
|
+
rake config_hoe
|
20
|
+
|
21
|
+
The only thing you should need to change is the exclude regular expression,
|
22
|
+
which needs to look like this:
|
23
|
+
|
24
|
+
exclude: !ruby/regexp /tmp$|CVS|\.svn|\.git|.+\.gemspec/
|
25
|
+
|
26
|
+
Now you have the correct setup for contributing.
|
27
|
+
|
28
|
+
## Before committing changes
|
29
|
+
|
30
|
+
Before you commit changes, you must verify that `Manifest.txt` (the file which
|
31
|
+
contains the names of every file to be included in a gem release) and
|
32
|
+
`[project-name].gemspec` are up to date. We have create a rake task to make
|
33
|
+
this easy:
|
34
|
+
|
35
|
+
rake gemspec
|
36
|
+
|
37
|
+
This will check `Manifest.txt` (using Hoe's `rake check_manifest`) to ensure
|
38
|
+
there are no differences between the files in the project, and those listed in
|
39
|
+
the manifest. If there is a difference, it will display a warning and a list of
|
40
|
+
the differences in `diff` format.
|
41
|
+
|
42
|
+
If the changes in the diff are correct, then you can run the following command
|
43
|
+
to update the manifest.
|
44
|
+
|
45
|
+
rake check_manifest | patch
|
46
|
+
|
47
|
+
If there are files you do not want added to the manifest, then you should
|
48
|
+
remove the files from the project, and then run `rake gemspec` again.
|
49
|
+
|
50
|
+
If `rake gemspec` says it was successful, then you can proceed with committing
|
51
|
+
and pushing your changes.
|
data/FAQ
CHANGED
@@ -61,14 +61,32 @@ back. If you want to just execute something against the database, use
|
|
61
61
|
|
62
62
|
=== Can I get an query log of what DataMapper is issuing?
|
63
63
|
|
64
|
-
|
64
|
+
An example of how to modify an existing logger:
|
65
65
|
|
66
|
-
DataMapper
|
66
|
+
DataMapper.logger.set_log(STDOUT, :debug)
|
67
67
|
|
68
|
-
|
68
|
+
An example of how to create new logger:
|
69
69
|
|
70
|
-
DataMapper.
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
DataMapper.logger.
|
70
|
+
DataMapper::Logger.new(STDOUT, :info)
|
71
|
+
|
72
|
+
To send a message to the DataMapper logger:
|
73
|
+
|
74
|
+
DataMapper.logger.debug("something")
|
75
|
+
DataMapper.logger.info ("something")
|
76
|
+
DataMapper.logger.warn ("something")
|
77
|
+
DataMapper.logger.error("something")
|
78
|
+
DataMapper.logger.fatal("something")
|
79
|
+
|
80
|
+
|
81
|
+
=== I want to run the specs, but I have a custom database setup
|
82
|
+
|
83
|
+
For example, if you installed MySQL using MacPorts, your socket may be located
|
84
|
+
at /opt/local/var/run/mysql5/mysqld.sock instead of /tmp/mysql.sock
|
85
|
+
|
86
|
+
In that case, setup an environment variable in your shell before running the
|
87
|
+
specs:
|
88
|
+
export MYSQL_SPEC_URI="mysql://localhost/dm_core_test?socket=/opt/local/var/run/mysql5/mysqld.sock"
|
89
|
+
rake spec
|
90
|
+
|
91
|
+
Using another kind of database? Note that spec_helper.rb will also look for
|
92
|
+
SQLITE3_SPEC_URI AND POSTGRES_SPEC_URI.
|
data/Manifest.txt
CHANGED
data/Rakefile
CHANGED
@@ -13,7 +13,7 @@ AUTHOR = "Sam Smoot"
|
|
13
13
|
EMAIL = "ssmoot@gmail.com"
|
14
14
|
GEM_NAME = "dm-core"
|
15
15
|
GEM_VERSION = DataMapper::VERSION
|
16
|
-
GEM_DEPENDENCIES = ["data_objects", GEM_VERSION], ["extlib",
|
16
|
+
GEM_DEPENDENCIES = ["data_objects", GEM_VERSION], ["extlib", "=0.9.4"],
|
17
17
|
["rspec", ">=1.1.3"], ["addressable", ">=1.0.4"]
|
18
18
|
|
19
19
|
|
@@ -23,6 +23,7 @@ PROJECT_SUMMARY = "An Object/Relational Mapper for Ruby"
|
|
23
23
|
PROJECT_URL = "http://datamapper.org"
|
24
24
|
|
25
25
|
require ROOT + 'tasks/hoe'
|
26
|
+
require ROOT + 'tasks/gemspec'
|
26
27
|
require ROOT + 'tasks/install'
|
27
28
|
require ROOT + 'tasks/dm'
|
28
29
|
require ROOT + 'tasks/doc'
|
data/lib/dm-core.rb
CHANGED
@@ -20,7 +20,7 @@ require 'rubygems'
|
|
20
20
|
gem 'addressable', '>=1.0.4'
|
21
21
|
require 'addressable/uri'
|
22
22
|
|
23
|
-
gem 'extlib', '=0.9.
|
23
|
+
gem 'extlib', '=0.9.4'
|
24
24
|
require 'extlib'
|
25
25
|
|
26
26
|
begin
|
@@ -143,8 +143,13 @@ module DataMapper
|
|
143
143
|
lib_name = "#{Extlib::Inflection.underscore(adapter_name)}_adapter"
|
144
144
|
begin
|
145
145
|
require root / 'lib' / 'dm-core' / 'adapters' / lib_name
|
146
|
-
rescue LoadError
|
147
|
-
|
146
|
+
rescue LoadError => e
|
147
|
+
begin
|
148
|
+
require lib_name
|
149
|
+
rescue Exception
|
150
|
+
# library not found, raise the original error
|
151
|
+
raise e
|
152
|
+
end
|
148
153
|
end
|
149
154
|
end
|
150
155
|
|
@@ -43,8 +43,8 @@ module DataMapper
|
|
43
43
|
@name = name
|
44
44
|
@uri = normalize_uri(uri_or_options)
|
45
45
|
|
46
|
-
@resource_naming_convention = NamingConventions::UnderscoredAndPluralized
|
47
|
-
@field_naming_convention = NamingConventions::Underscored
|
46
|
+
@resource_naming_convention = NamingConventions::Resource::UnderscoredAndPluralized
|
47
|
+
@field_naming_convention = NamingConventions::Field::Underscored
|
48
48
|
|
49
49
|
@transactions = Hash.new do |hash, key|
|
50
50
|
hash.delete_if do |k, v|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
gem 'data_objects', '=0.9.
|
1
|
+
gem 'data_objects', '=0.9.4'
|
2
2
|
require 'data_objects'
|
3
3
|
|
4
4
|
module DataMapper
|
@@ -327,7 +327,17 @@ module DataMapper
|
|
327
327
|
query.merge_subquery(operator, opposite, condition)
|
328
328
|
"(#{read_statement(condition)})"
|
329
329
|
elsif condition.kind_of?(Array) && condition.all? { |p| p.kind_of?(Property) }
|
330
|
-
|
330
|
+
property_values = condition.map { |p| property_to_column_name(query.repository, p, qualify) }
|
331
|
+
# An IN() clause with an empty set is the same as a false condition
|
332
|
+
# IN() with an empty set is not supported on all RDMS, but this is.
|
333
|
+
if property_values.empty?
|
334
|
+
if [:eql, :in, :not].include? operator
|
335
|
+
return operator == :not ? "1=1" : "0=1"
|
336
|
+
else
|
337
|
+
raise "Invalid query operator #{operator.inspect} for #{property_values.inspect}"
|
338
|
+
end
|
339
|
+
end
|
340
|
+
"(#{property_values * ', '})"
|
331
341
|
else
|
332
342
|
'?'
|
333
343
|
end
|
@@ -500,7 +510,7 @@ module DataMapper
|
|
500
510
|
end
|
501
511
|
|
502
512
|
# TODO: move to dm-more/dm-migrations
|
503
|
-
def drop_table_statement(
|
513
|
+
def drop_table_statement(repository, model)
|
504
514
|
"DROP TABLE IF EXISTS #{quote_table_name(model.storage_name(repository.name))}"
|
505
515
|
end
|
506
516
|
|
@@ -546,7 +556,11 @@ module DataMapper
|
|
546
556
|
# remove the default if the property is not nullable
|
547
557
|
schema.delete(:default) unless property.nullable?
|
548
558
|
else
|
549
|
-
|
559
|
+
if property.type.respond_to?(:dump)
|
560
|
+
schema[:default] = property.type.dump(property.default, property)
|
561
|
+
else
|
562
|
+
schema[:default] = property.default
|
563
|
+
end
|
550
564
|
end
|
551
565
|
|
552
566
|
schema
|
@@ -1,4 +1,4 @@
|
|
1
|
-
gem 'do_mysql', '=0.9.
|
1
|
+
gem 'do_mysql', '=0.9.4'
|
2
2
|
require 'do_mysql'
|
3
3
|
|
4
4
|
module DataMapper
|
@@ -39,8 +39,10 @@ module DataMapper
|
|
39
39
|
def storage_exists?(storage_name)
|
40
40
|
statement = <<-EOS.compress_lines
|
41
41
|
SELECT COUNT(*)
|
42
|
-
FROM `information_schema`.`
|
43
|
-
WHERE `
|
42
|
+
FROM `information_schema`.`tables`
|
43
|
+
WHERE `table_type` = 'BASE TABLE'
|
44
|
+
AND `table_schema` = ?
|
45
|
+
AND `table_name` = ?
|
44
46
|
EOS
|
45
47
|
|
46
48
|
query(statement, db_name, storage_name).first > 0
|
@@ -51,7 +53,9 @@ module DataMapper
|
|
51
53
|
statement = <<-EOS.compress_lines
|
52
54
|
SELECT COUNT(*)
|
53
55
|
FROM `information_schema`.`columns`
|
54
|
-
WHERE `table_schema` = ?
|
56
|
+
WHERE `table_schema` = ?
|
57
|
+
AND `table_name` = ?
|
58
|
+
AND `column_name` = ?
|
55
59
|
EOS
|
56
60
|
|
57
61
|
query(statement, db_name, storage_name, field_name).first > 0
|
@@ -1,4 +1,4 @@
|
|
1
|
-
gem 'do_postgres', '=0.9.
|
1
|
+
gem 'do_postgres', '=0.9.4'
|
2
2
|
require 'do_postgres'
|
3
3
|
|
4
4
|
module DataMapper
|
@@ -21,7 +21,8 @@ module DataMapper
|
|
21
21
|
statement = <<-EOS.compress_lines
|
22
22
|
SELECT COUNT(*)
|
23
23
|
FROM "information_schema"."columns"
|
24
|
-
WHERE "table_name" = ?
|
24
|
+
WHERE "table_name" = ?
|
25
|
+
AND "table_schema" = current_schema()
|
25
26
|
EOS
|
26
27
|
|
27
28
|
query(statement, storage_name).first > 0
|
@@ -33,7 +34,9 @@ module DataMapper
|
|
33
34
|
SELECT COUNT(*)
|
34
35
|
FROM "pg_class"
|
35
36
|
JOIN "pg_attribute" ON "pg_class"."oid" = "pg_attribute"."attrelid"
|
36
|
-
WHERE "pg_attribute"."attname" = ?
|
37
|
+
WHERE "pg_attribute"."attname" = ?
|
38
|
+
AND "pg_class"."relname" = ?
|
39
|
+
AND "pg_attribute"."attnum" >= 0
|
37
40
|
EOS
|
38
41
|
|
39
42
|
query(statement, column_name, storage_name).first > 0
|
@@ -53,6 +56,7 @@ module DataMapper
|
|
53
56
|
|
54
57
|
# TODO: move to dm-more/dm-migrations
|
55
58
|
def destroy_model_storage(repository, model)
|
59
|
+
return true unless storage_exists?(model.storage_name(repository.name))
|
56
60
|
success = without_notices { super }
|
57
61
|
model.properties(repository.name).each do |property|
|
58
62
|
drop_sequence(repository, property) if property.serial?
|
@@ -76,6 +80,11 @@ module DataMapper
|
|
76
80
|
module SQL
|
77
81
|
private
|
78
82
|
|
83
|
+
# TODO: move to dm-more/dm-migrations
|
84
|
+
def drop_table_statement(repository, model)
|
85
|
+
"DROP TABLE #{quote_table_name(model.storage_name(repository.name))}"
|
86
|
+
end
|
87
|
+
|
79
88
|
# TODO: move to dm-more/dm-migrations
|
80
89
|
def without_notices(&block)
|
81
90
|
# execute the block with NOTICE messages disabled
|
data/lib/dm-core/associations.rb
CHANGED
@@ -40,6 +40,7 @@ module DataMapper
|
|
40
40
|
#
|
41
41
|
# @api private
|
42
42
|
def many_to_one_relationships
|
43
|
+
relationships unless @relationships # needs to be initialized!
|
43
44
|
@relationships.values.collect do |rels| rels.values end.flatten.select do |relationship| relationship.child_model == self end
|
44
45
|
end
|
45
46
|
|
@@ -61,7 +61,9 @@ module DataMapper
|
|
61
61
|
return true unless parent.new_record?
|
62
62
|
|
63
63
|
@relationship.with_repository(parent) do
|
64
|
-
parent.save
|
64
|
+
result = parent.save
|
65
|
+
@relationship.child_key.set(@child, @relationship.parent_key.get(parent)) if result
|
66
|
+
result
|
65
67
|
end
|
66
68
|
end
|
67
69
|
|
@@ -78,6 +80,10 @@ module DataMapper
|
|
78
80
|
super || parent.respond_to?(method, include_private)
|
79
81
|
end
|
80
82
|
|
83
|
+
def instance_variable_get(variable)
|
84
|
+
super || parent.instance_variable_get(variable)
|
85
|
+
end
|
86
|
+
|
81
87
|
private
|
82
88
|
|
83
89
|
def initialize(relationship, child)
|
@@ -244,7 +244,7 @@ module DataMapper
|
|
244
244
|
end
|
245
245
|
|
246
246
|
def assert_mutable
|
247
|
-
raise ImmutableAssociationError, 'You can not modify this
|
247
|
+
raise ImmutableAssociationError, 'You can not modify this association' if children.frozen?
|
248
248
|
end
|
249
249
|
|
250
250
|
def default_attributes
|
@@ -103,15 +103,14 @@ module DataMapper
|
|
103
103
|
|
104
104
|
query = collection.query.dup
|
105
105
|
query.conditions.map! do |operator, property, bind_value|
|
106
|
-
if child_key.has_property?(property.name)
|
106
|
+
if operator != :raw && child_key.has_property?(property.name)
|
107
107
|
bind_value = *children.map { |child| property.get(child) }.uniq
|
108
108
|
end
|
109
109
|
[ operator, property, bind_value ]
|
110
110
|
end
|
111
111
|
|
112
|
-
parents_children = Collection.new(query)
|
113
|
-
|
114
|
-
end
|
112
|
+
parents_children = Collection.new(query)
|
113
|
+
children.each { |child| parents_children.send(:add, child) }
|
115
114
|
|
116
115
|
if parent_key.get(parent) == parent_values
|
117
116
|
ret = parents_children
|
@@ -137,22 +136,24 @@ module DataMapper
|
|
137
136
|
return parent
|
138
137
|
end
|
139
138
|
|
140
|
-
children = child_identity_map.values
|
139
|
+
children = child_identity_map.values | [child]
|
141
140
|
|
142
|
-
bind_values =
|
143
|
-
query_values = bind_values.reject { |k| parent_identity_map[
|
141
|
+
bind_values = children.map { |c| child_key.get(c) }.uniq
|
142
|
+
query_values = bind_values.reject { |k| parent_identity_map[k] }
|
144
143
|
|
145
144
|
bind_values = query_values unless query_values.empty?
|
146
|
-
query = parent_key.
|
147
|
-
|
145
|
+
query = parent_key.zip(bind_values.transpose).to_hash
|
148
146
|
association_accessor = "#{self.name}_association"
|
149
147
|
|
150
148
|
collection = parent_model.send(:all, query)
|
151
|
-
collection.
|
152
|
-
|
153
|
-
|
149
|
+
unless collection.empty?
|
150
|
+
collection.send(:lazy_load)
|
151
|
+
children.each do |c|
|
152
|
+
c.send(association_accessor).instance_variable_set(:@parent, collection.get(*child_key.get(c)))
|
153
|
+
end
|
154
|
+
|
155
|
+
child.send(association_accessor).instance_variable_get(:@parent)
|
154
156
|
end
|
155
|
-
child.send(association_accessor).instance_variable_get(:@parent)
|
156
157
|
end
|
157
158
|
end
|
158
159
|
|
@@ -3,14 +3,40 @@
|
|
3
3
|
module DataMapper
|
4
4
|
class AutoMigrator
|
5
5
|
##
|
6
|
-
# Destructively automigrates the data-store to match the model
|
6
|
+
# Destructively automigrates the data-store to match the model.
|
7
|
+
# First migrates all models down and then up.
|
7
8
|
# REPEAT: THIS IS DESTRUCTIVE
|
8
9
|
#
|
9
10
|
# @param Symbol repository_name the repository to be migrated
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
def self.auto_migrate(repository_name = nil, *descendants)
|
12
|
+
auto_migrate_down(repository_name, *descendants)
|
13
|
+
auto_migrate_up(repository_name, *descendants)
|
14
|
+
end
|
15
|
+
|
16
|
+
##
|
17
|
+
# Destructively automigrates the data-store down
|
18
|
+
# REPEAT: THIS IS DESTRUCTIVE
|
19
|
+
#
|
20
|
+
# @param Symbol repository_name the repository to be migrated
|
21
|
+
# @calls DataMapper::Resource#auto_migrate_down!
|
22
|
+
# @api private
|
23
|
+
def self.auto_migrate_down(repository_name = nil, *descendants)
|
24
|
+
descendants = DataMapper::Resource.descendants.to_a if descendants.empty?
|
25
|
+
descendants.reverse.each do |model|
|
26
|
+
model.auto_migrate_down!(repository_name)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# Automigrates the data-store up
|
32
|
+
#
|
33
|
+
# @param Symbol repository_name the repository to be migrated
|
34
|
+
# @calls DataMapper::Resource#auto_migrate_up!
|
35
|
+
# @api private
|
36
|
+
def self.auto_migrate_up(repository_name = nil, *descendants)
|
37
|
+
descendants = DataMapper::Resource.descendants.to_a if descendants.empty?
|
38
|
+
descendants.each do |model|
|
39
|
+
model.auto_migrate_up!(repository_name)
|
14
40
|
end
|
15
41
|
end
|
16
42
|
|
@@ -34,11 +60,37 @@ module DataMapper
|
|
34
60
|
#
|
35
61
|
# @param Symbol repository_name the repository to be migrated
|
36
62
|
def auto_migrate!(repository_name = nil)
|
63
|
+
auto_migrate_down!(repository_name)
|
64
|
+
auto_migrate_up!(repository_name)
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# Destructively migrates the data-store down, which basically
|
69
|
+
# deletes all the models.
|
70
|
+
# REPEAT: THIS IS DESTRUCTIVE
|
71
|
+
#
|
72
|
+
# @param Symbol repository_name the repository to be migrated
|
73
|
+
# @api private
|
74
|
+
def auto_migrate_down!(repository_name = nil)
|
37
75
|
if self.superclass != Object
|
38
76
|
self.superclass.auto_migrate!(repository_name)
|
39
77
|
else
|
40
78
|
repository(repository_name) do |r|
|
41
79
|
r.adapter.destroy_model_storage(r, self)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
##
|
85
|
+
# Auto migrates the data-store to match the model
|
86
|
+
#
|
87
|
+
# @param Symbol repository_name the repository to be migrated
|
88
|
+
# @api private
|
89
|
+
def auto_migrate_up!(repository_name = nil)
|
90
|
+
if self.superclass != Object
|
91
|
+
self.superclass.auto_migrate!(repository_name)
|
92
|
+
else
|
93
|
+
repository(repository_name) do |r|
|
42
94
|
r.adapter.create_model_storage(r, self)
|
43
95
|
end
|
44
96
|
end
|