diff --git a/ADAPTERS.md b/ADAPTERS.md index 9e0a8b8..f44ee0f 100644 --- a/ADAPTERS.md +++ b/ADAPTERS.md @@ -70,12 +70,12 @@ DatabaseCleaner[:orm_name].strategy = :transaction ### Strategy classes -Each strategy class can inherit from DatabaseCleaner::Strategy to get it most of the way there. If you do, you only need to define one method! +Each strategy class **must** inherit from `DatabaseCleaner::Strategy`. -Each strategy **must** have the following instance methods +Each strategy **must** have the following instance methods: * `#clean` -- where the cleaning happens -Optionally, depending on how your strategy works you may define +Optionally, depending on how your strategy works you may also need to define * `#start` -- if your strategy is transactional, this is where you would start the database transaction that `#clean` later rolls back. Given that we're creating a strategy for truncation, you may end up with something like the following class: @@ -117,7 +117,7 @@ end ### What's next -Now you should be set up to with your own database_cleaner ORM adapter. +Now you should be all set up with your very own database_cleaner ORM adapter! Also, don't forget to take a look at the already created adapters, if you encounter any problems. When you are done with your adapter gem, only a few things left to do diff --git a/lib/database_cleaner/cleaner.rb b/lib/database_cleaner/cleaner.rb index 96fc030..de5f453 100644 --- a/lib/database_cleaner/cleaner.rb +++ b/lib/database_cleaner/cleaner.rb @@ -1,6 +1,7 @@ require 'database_cleaner/deprecation' require 'database_cleaner/null_strategy' require 'database_cleaner/safeguard' +require 'database_cleaner/strategy' require 'forwardable' module DatabaseCleaner @@ -8,9 +9,15 @@ module DatabaseCleaner class Cleaner def self.available_strategies(orm_module) - # introspect publically available constants to get list of strategies, leaving out common but obviously non-strategy constants. - # if you're writing an adapter and this method is finding a constant in it that is not a valid strategy, consider making it private with Module#private_constant. - (orm_module.constants - [:Base, :VERSION]).map(&:downcase) + # introspect publically available constants for descendents of Strategy to get list of strategies + # ignore classes named Base, because its a common name for a shared base class that adds ORM access stuff to Strategy before being inherited by final concrete class + # if you're writing an adapter and this method is falsely returning an internal constant that isn't a valid strategy, consider making it private with Module#private_constant. + orm_module.constants.select do |constant_name| + ancestors = orm_module.const_get(constant_name).ancestors rescue [] + ancestors.include?(DatabaseCleaner::Strategy) + end.map do |constant_name| + underscore(constant_name).to_sym + end - [:base] end include Comparable @@ -94,6 +101,8 @@ module DatabaseCleaner DatabaseCleaner.const_get(orm_module_name) end + # copied from ActiveSupport to avoid adding it as a dependency + def camelize(term) string = term.to_s string = string.sub(/^[a-z\d]*/) { |match| match.capitalize } @@ -101,5 +110,16 @@ module DatabaseCleaner string.gsub!("/", "::") string end + + def self.underscore(camel_cased_word) + return camel_cased_word unless /[A-Z-]|::/.match?(camel_cased_word) + word = camel_cased_word.to_s.gsub("::", "/") + word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2') + word.gsub!(/([a-z\d])([A-Z])/, '\1_\2') + word.tr!("-", "_") + word.downcase! + word + end + private_class_method :underscore end end diff --git a/spec/database_cleaner/cleaner_spec.rb b/spec/database_cleaner/cleaner_spec.rb index 94f08da..ef86aca 100644 --- a/spec/database_cleaner/cleaner_spec.rb +++ b/spec/database_cleaner/cleaner_spec.rb @@ -89,8 +89,8 @@ module DatabaseCleaner describe "clean_with" do subject(:cleaner) { Cleaner.new(:active_record) } - let(:strategy_class) { Class.new } - let(:strategy) { double } + let(:strategy_class) { Class.new(DatabaseCleaner::Strategy) } + let(:strategy) { strategy_class.new } before { allow(strategy_class).to receive(:new).and_return(strategy) } before do @@ -117,7 +117,7 @@ module DatabaseCleaner describe "strategy=" do subject(:cleaner) { Cleaner.new(:active_record) } - let(:strategy_class) { Class.new } + let(:strategy_class) { Class.new(DatabaseCleaner::Strategy) } let(:orm_module) { Module.new } before do @@ -125,9 +125,10 @@ module DatabaseCleaner stub_const "DatabaseCleaner::ActiveRecord::Truncation", strategy_class # stub consts that shouldn't show up in strategy list stub_const "DatabaseCleaner::ActiveRecord::VERSION", "2.0.0" - stub_const "DatabaseCleaner::ActiveRecord::Base", Module.new stub_const "DatabaseCleaner::ActiveRecord::Helpers", Module.new - orm_module.private_constant :Helpers + stub_const "DatabaseCleaner::ActiveRecord::Base", Class.new(strategy_class) + stub_const "DatabaseCleaner::ActiveRecord::ExtendedBase", Class.new(strategy_class) + orm_module.private_constant :ExtendedBase end it "should look up and create a the named strategy for the current ORM" do