Permalink
Browse files

Resolved require order dependency issues

It is now possible to require 'dm-migrations'
either before or after DataMapper.setup has been
called, and the behavior will always be the same.

The API for Repository and Model gets included right
when the gem is required.

If one or more adapters that support migrations
are already set up, the API for these adapter(s)
is included into all those adapters.

In any case, a DataMapper::Adapters.const_added
extension gets installed at the time that the gem
gets required. This extension will make sure that
the adapter related migrations API will get
included into any new adapter that might get set
up. This is guaranteed to happen if the newly set
up adapter properly calls const_added(:AdapterName)
after it was defined.
  • Loading branch information...
1 parent 68259b5 commit 553049f0a9f87c185f5ef863748a61d1bfd19473 @snusnu snusnu committed May 3, 2010
View
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Paul Sadauskas"]
- s.date = %q{2010-04-30}
+ s.date = %q{2010-05-03}
s.description = %q{DataMapper plugin for writing and speccing migrations}
s.email = %q{psadauskas [a] gmail [d] com}
s.extra_rdoc_files = [
@@ -36,6 +36,7 @@ Gem::Specification.new do |s|
"lib/dm-migrations/adapters/dm-postgres-adapter.rb",
"lib/dm-migrations/adapters/dm-sqlite-adapter.rb",
"lib/dm-migrations/adapters/dm-sqlserver-adapter.rb",
+ "lib/dm-migrations/adapters/dm-yaml-adapter.rb",
"lib/dm-migrations/auto_migration.rb",
"lib/dm-migrations/migration.rb",
"lib/dm-migrations/migration_runner.rb",
@@ -53,6 +54,9 @@ Gem::Specification.new do |s|
"spec/integration/migration_runner_spec.rb",
"spec/integration/migration_spec.rb",
"spec/integration/sql_spec.rb",
+ "spec/isolated/require_after_setup_spec.rb",
+ "spec/isolated/require_before_setup_spec.rb",
+ "spec/isolated/require_spec.rb",
"spec/rcov.opts",
"spec/spec.opts",
"spec/spec_helper.rb",
@@ -82,6 +86,9 @@ Gem::Specification.new do |s|
"spec/integration/migration_runner_spec.rb",
"spec/integration/migration_spec.rb",
"spec/integration/sql_spec.rb",
+ "spec/isolated/require_after_setup_spec.rb",
+ "spec/isolated/require_before_setup_spec.rb",
+ "spec/isolated/require_spec.rb",
"spec/spec_helper.rb",
"spec/unit/migration_spec.rb",
"spec/unit/sql/column_spec.rb",
@@ -4,16 +4,6 @@ module DataMapper
module Migrations
module DataObjectsAdapter
- # @api private
- def self.included(base)
- base.extend ClassMethods
-
- DataMapper.extend(Migrations::SingletonMethods)
-
- [ :Repository, :Model ].each do |name|
- DataMapper.const_get(name).send(:include, Migrations.const_get(name))
- end
- end
# Returns whether the storage_name exists.
#
@@ -9,8 +9,11 @@ module MysqlAdapter
DEFAULT_CHARACTER_SET = 'utf8'.freeze
DEFAULT_COLLATION = 'utf8_unicode_ci'.freeze
+ include DataObjectsAdapter
+
# @api private
def self.included(base)
+ base.extend DataObjectsAdapter::ClassMethods
base.extend ClassMethods
end
@@ -5,8 +5,11 @@ module DataMapper
module Migrations
module OracleAdapter
+ include DataObjectsAdapter
+
# @api private
def self.included(base)
+ base.extend DataObjectsAdapter::ClassMethods
base.extend ClassMethods
end
@@ -5,8 +5,11 @@ module DataMapper
module Migrations
module PostgresAdapter
+ include DataObjectsAdapter
+
# @api private
def self.included(base)
+ base.extend DataObjectsAdapter::ClassMethods
base.extend ClassMethods
end
@@ -5,8 +5,11 @@ module DataMapper
module Migrations
module SqliteAdapter
+ include DataObjectsAdapter
+
# @api private
def self.included(base)
+ base.extend DataObjectsAdapter::ClassMethods
base.extend ClassMethods
end
@@ -7,8 +7,11 @@ module SqlserverAdapter
DEFAULT_CHARACTER_SET = 'utf8'.freeze
+ include DataObjectsAdapter
+
# @api private
def self.included(base)
+ base.extend DataObjectsAdapter::ClassMethods
base.extend ClassMethods
end
@@ -1,4 +1,4 @@
-require 'dm-core/adapters'
+require 'dm-core'
module DataMapper
module Migrations
@@ -177,35 +177,60 @@ def auto_migrate_up!(repository_name = self.repository_name)
end
end
end # module Model
+
+ def self.include_migration_api
+ DataMapper.extend(Migrations::SingletonMethods)
+ DataMapper::Model.send(:include, Migrations::Model)
+ DataMapper::Repository.send(:include, Migrations::Repository)
+ DataMapper::Model.append_extensions(Migrations::Model)
+ DataMapper::Repository.adapters.values.each do |adapter|
+ Adapters.include_migration_api(ActiveSupport::Inflector.demodulize(adapter.class.name))
+ end
+ end
+
end
module Adapters
extend Chainable
- extendable do
+ class << self
- # @api private
- def const_added(const_name)
+ def include_migration_api(const_name)
require auto_migration_extensions(const_name)
+ adapter = const_get(const_name)
if DataMapper::Migrations.const_defined?(const_name)
- adapter = const_get(const_name)
- adapter.send(:include, DataMapper::Migrations.const_get(const_name))
+ adapter.send(:include, migration_module(const_name))
end
rescue LoadError
# do nothing
- ensure
- super
end
+ def migration_module(const_name)
+ DataMapper::Migrations.const_get(const_name)
+ end
+
+ private
+
# @api private
def auto_migration_extensions(const_name)
- name = const_name.to_s.gsub('Adapter','').downcase
- adapter_name = (name == 'dataobjects') ? 'do' : name
- "dm-migrations/adapters/dm-#{adapter_name}-adapter"
+ name = adapter_name(const_name)
+ name = 'do' if name == 'dataobjects'
+ "dm-migrations/adapters/dm-#{name}-adapter"
end
end
- end
-end
+ extendable do
+ # @api private
+ def const_added(const_name)
+ include_migration_api(const_name)
+ super
+ end
+ end
+
+ end # module Adapters
+
+ Migrations.include_migration_api
+
+end # module DataMapper
@@ -0,0 +1,26 @@
+require 'spec'
+require 'isolated/require_spec'
+require 'dm-core/spec/setup'
+
+# To really test this behavior, this spec needs to be run in isolation and not
+# as part of the typical rake spec run, which requires dm-transactions upfront
+
+describe "require 'dm-migrations' after calling DataMapper.setup" do
+
+ before(:all) do
+
+ @adapter = DataMapper::Spec.adapter
+ require 'dm-migrations'
+
+ class ::Person
+ include DataMapper::Resource
+ property :id, Serial
+ end
+
+ @model = Person
+
+ end
+
+ it_should_behave_like "require 'dm-migrations'"
+
+end
@@ -0,0 +1,26 @@
+require 'spec'
+require 'isolated/require_spec'
+require 'dm-core/spec/setup'
+
+# To really test this behavior, this spec needs to be run in isolation and not
+# as part of the typical rake spec run, which requires dm-transactions upfront
+
+describe "require 'dm-migrations' before calling DataMapper.setup" do
+
+ before(:all) do
+
+ require 'dm-migrations'
+ @adapter = DataMapper::Spec.adapter
+
+ class ::Person
+ include DataMapper::Resource
+ property :id, Serial
+ end
+
+ @model = Person
+
+ end
+
+ it_should_behave_like "require 'dm-migrations'"
+
+end
@@ -0,0 +1,25 @@
+shared_examples_for "require 'dm-migrations'" do
+
+ it "should include the migration api in the DataMapper namespace" do
+ DataMapper.respond_to?(:migrate! ).should be_true
+ DataMapper.respond_to?(:auto_migrate! ).should be_true
+ DataMapper.respond_to?(:auto_upgrade! ).should be_true
+ DataMapper.respond_to?(:auto_migrate_up!, true).should be_true
+ DataMapper.respond_to?(:auto_migrate_down!, true).should be_true
+ end
+
+ %w[Repository Model].each do |name|
+ it "should include the migration api in DataMapper::#{name}" do
+ (DataMapper.const_get(name) < DataMapper::Migrations.const_get(name)).should be_true
+ end
+ end
+
+ it "should include the migration api into the adapter" do
+ @adapter.respond_to?(:storage_exists? ).should be_true
+ @adapter.respond_to?(:field_exists? ).should be_true
+ @adapter.respond_to?(:upgrade_model_storage).should be_true
+ @adapter.respond_to?(:create_model_storage ).should be_true
+ @adapter.respond_to?(:destroy_model_storage).should be_true
+ end
+
+end

0 comments on commit 553049f

Please sign in to comment.