require 'active_support' files = %w{ context buildable namespace root_namespace plan file_context helper errors database_backends/abstract database_backends/active_record database_backends/none } files << if defined? Spec or $0 =~ /script.spec$/ 'extensions/rspec' else 'extensions/test_unit' end files.each {|f| require File.join(File.dirname(__FILE__), 'blueprints', f) } module Blueprints PLAN_FILES = [nil, "spec", "test"].map do |dir| ["blueprint"].map do |file| path = File.join([dir, file].compact) ["#{path}.rb", File.join(path, "*.rb")] end end.flatten # Returns a list of supported ORMs. For now it supports ActiveRecord and None. def self.supported_orms DatabaseBackends.constants.collect {|class_name| class_name.to_s.underscore.to_sym } - [:abstract] end # Returns root of project blueprints is used in. Automatically detected for rails and merb. Can be overwritten by using # :root options when loading blueprints. If root can't be determined, returns nil which means that current # directory is asumed as root. def self.framework_root @@framework_root ||= RAILS_ROOT rescue Rails.root rescue Merb.root rescue nil end # Setups variables from global context and starts transaction. Should be called before every test case. def self.setup(current_context) Namespace.root.setup Namespace.root.copy_ivars(current_context) @@orm.start_transaction end # Rollbacks transaction returning everything to state before test. Should be called after every test case. def self.teardown @@orm.rollback_transaction end # Sets up configuration, clears database, runs scenarios that have to be prebuilt. Should be run before all test cases and before setup. # Accepts following options: # * :delete_policy - allows changing how tables in database should be cleared. By default simply uses delete statement. Supports :delete and :truncate options. # * :filename - Allows passing custom filename pattern in case blueprints are held in place other than spec/blueprint, test/blueprint, blueprint. # * :prebuild - Allows passing scenarios that should be prebuilt and available in all tests. Works similarly to fixtures. # * :root - Allows passing custom root folder to use in case of non rails and non merb project. # * :orm - Allows specifying what orm should be used. Default to :active_record, also allows :none def self.load(options = {}) options.assert_valid_keys(:delete_policy, :filename, :prebuild, :root, :orm) options.symbolize_keys! return unless Namespace.root.empty? orm = (options.delete(:orm) || :active_record).to_sym raise ArgumentError, "Unsupported ORM #{orm}. Blueprints supports only #{supported_orms.join(', ')}" unless supported_orms.include?(orm) @@orm = DatabaseBackends.const_get(orm.to_s.classify).new @@orm.delete_tables(@@delete_policy = options[:delete_policy]) @@framework_root = options[:root] if options[:root] load_scenarios_files(options[:filename] || PLAN_FILES) Namespace.root.prebuild(options[:prebuild]) end # Clears all tables in database. Also accepts a list of tables in case not all tables should be cleared. def self.delete_tables(*tables) @@orm.delete_tables(@@delete_policy, *tables) end def self.warn(message, blueprint) $stderr.puts("**WARNING** #{message}: '#{blueprint}'") end protected # Loads blueprints file and creates blueprints from data it contains. Is run by setup method def self.load_scenarios_files(*patterns) patterns.flatten! patterns.collect! {|pattern| File.join(framework_root, pattern)} if framework_root patterns.each do |pattern| unless (files = Dir.glob(pattern)).empty? files.each{|f| FileContext.module_eval File.read(f)} return end end raise "Plans file not found! Put plans in #{patterns.join(' or ')} or pass custom filename pattern with :filename option" end end