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.
Files changed (45) hide show
  1. data/CONTRIBUTING +51 -0
  2. data/FAQ +26 -8
  3. data/Manifest.txt +2 -0
  4. data/Rakefile +2 -1
  5. data/lib/dm-core.rb +8 -3
  6. data/lib/dm-core/adapters/abstract_adapter.rb +2 -2
  7. data/lib/dm-core/adapters/data_objects_adapter.rb +18 -4
  8. data/lib/dm-core/adapters/mysql_adapter.rb +8 -4
  9. data/lib/dm-core/adapters/postgres_adapter.rb +12 -3
  10. data/lib/dm-core/adapters/sqlite3_adapter.rb +1 -1
  11. data/lib/dm-core/associations.rb +1 -0
  12. data/lib/dm-core/associations/many_to_one.rb +7 -1
  13. data/lib/dm-core/associations/one_to_many.rb +1 -1
  14. data/lib/dm-core/associations/relationship.rb +14 -13
  15. data/lib/dm-core/auto_migrations.rb +57 -5
  16. data/lib/dm-core/collection.rb +2 -1
  17. data/lib/dm-core/logger.rb +5 -6
  18. data/lib/dm-core/model.rb +17 -2
  19. data/lib/dm-core/naming_conventions.rb +57 -25
  20. data/lib/dm-core/property.rb +6 -5
  21. data/lib/dm-core/query.rb +20 -16
  22. data/lib/dm-core/resource.rb +24 -7
  23. data/lib/dm-core/version.rb +1 -1
  24. data/script/performance.rb +2 -1
  25. data/script/profile.rb +1 -0
  26. data/spec/integration/association_spec.rb +131 -81
  27. data/spec/integration/association_through_spec.rb +87 -42
  28. data/spec/integration/associations/many_to_many_spec.rb +76 -16
  29. data/spec/integration/associations/many_to_one_spec.rb +6 -1
  30. data/spec/integration/associations/one_to_many_spec.rb +69 -0
  31. data/spec/integration/collection_spec.rb +7 -0
  32. data/spec/integration/query_spec.rb +24 -7
  33. data/spec/integration/resource_spec.rb +59 -10
  34. data/spec/integration/sti_spec.rb +23 -0
  35. data/spec/models/zoo.rb +2 -3
  36. data/spec/spec_helper.rb +9 -1
  37. data/spec/unit/adapters/postgres_adapter_spec.rb +9 -1
  38. data/spec/unit/associations/many_to_one_spec.rb +6 -0
  39. data/spec/unit/auto_migrations_spec.rb +2 -1
  40. data/spec/unit/naming_conventions_spec.rb +26 -18
  41. data/spec/unit/property_spec.rb +3 -4
  42. data/spec/unit/resource_spec.rb +19 -22
  43. data/tasks/gemspec.rb +23 -0
  44. data/tasks/hoe.rb +9 -1
  45. 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
- Yup, to set this up, do:
64
+ An example of how to modify an existing logger:
65
65
 
66
- DataMapper::Logger.new(STDOUT, 0)
66
+ DataMapper.logger.set_log(STDOUT, :debug)
67
67
 
68
- Incidentally, if you'd like to send a message into the DataMapper logger, do:
68
+ An example of how to create new logger:
69
69
 
70
- DataMapper.logger.debug { "something" }
71
- DataMapper.logger.info { "something" }
72
- DataMapper.logger.warn { "something" }
73
- DataMapper.logger.error { "something" }
74
- DataMapper.logger.fatal { "something" }
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
@@ -1,4 +1,5 @@
1
1
  .autotest
2
+ CONTRIBUTING
2
3
  FAQ
3
4
  History.txt
4
5
  MIT-LICENSE
@@ -119,5 +120,6 @@ spec/unit/type_spec.rb
119
120
  tasks/ci.rb
120
121
  tasks/dm.rb
121
122
  tasks/doc.rb
123
+ tasks/gemspec.rb
122
124
  tasks/hoe.rb
123
125
  tasks/install.rb
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", GEM_VERSION],
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.3'
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
- require lib_name
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.3'
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
- "(#{condition.map { |p| property_to_column_name(query.repository, p, qualify) } * ', '})"
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(repisitory, model)
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
- schema[:default] = property.default
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.3'
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`.`columns`
43
- WHERE `table_schema` = ? AND `table_name` = ?
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` = ? AND `table_name` = ? AND `column_name` = ?
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.3'
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" = ? AND "table_schema" = current_schema()
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" = ? AND "pg_class"."relname" = ? AND "pg_attribute"."attnum" >= 0
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
@@ -1,4 +1,4 @@
1
- gem 'do_sqlite3', '=0.9.3'
1
+ gem 'do_sqlite3', '=0.9.4'
2
2
  require 'do_sqlite3'
3
3
 
4
4
  module DataMapper
@@ -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 assocation' if children.frozen?
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) do |collection|
113
- children.each { |child| collection.send(:add, child) }
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 = *children.map { |c| child_key.get(c) }.uniq
143
- query_values = bind_values.reject { |k| parent_identity_map[[k]] }
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.map { |k| [ k, bind_values ] }.to_hash
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.send(:lazy_load)
152
- children.each do |c|
153
- c.send(association_accessor).instance_variable_set(:@parent, collection.get(*child_key.get(c)))
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
- # @calls DataMapper::Resource#auto_migrate!
11
- def self.auto_migrate(repository_name = nil)
12
- DataMapper::Resource.descendants.each do |model|
13
- model.auto_migrate!(repository_name)
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