Permalink
Browse files

Initial commit

  • Loading branch information...
0 parents commit d1e81b093bf53ba11a0170ab8b1280cacd5c66a5 Andrius Chamentauskas committed Sep 19, 2009
@@ -0,0 +1,4 @@
+.idea
+*.gem
+spec/db/database.yml
+debug.log
20 LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2007 Lachie Cox
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,150 @@
+= Blueprints
+
+Another replacement for factories and fixtures that focuses on being DRY and making developers type as little as possible.
+
+== Usage
+
+Plans look like:
+
+ plan :apple do
+ Fruit.create! :species => 'apple'
+ end
+
+ plan :orange do
+ Fruit.create! :species => 'orange'
+ end
+
+ plan :fruitbowl => [:apple, :orange] do
+ FruitBowl.create! :fruit => [@apple,@orange]
+ end
+
+ plan :kitchen => :fruitbowl do
+ Kitchen.create! :fruitbowl => @fruitbowl
+ end
+
+...and you use them in specs like:
+
+ describe Fruit, "@apple" do
+ before do
+ build :apple
+ end
+
+ it "should be an apple" do
+ @apple.species.should == 'apple'
+ end
+ end
+
+ describe FruitBowl, "with and apple and an orange" do
+ before do
+ build :fruitbowl
+ end
+
+ it "should have 2 fruits" do
+ @fruitbowl.should have(2).fruit
+ end
+ end
+
+Result of 'plan' block is evaluated and assigned to instance variable with the same name. You can also assign your own instance variables
+inside 'plan' block and they will be accessible in tests that build this plan.
+
+All scenarios are run only once, no matter how many times they were called, meaning that you don't need to worry about
+duplicating data.
+
+There's also a possibility to delete preloaded data with demolish. When called without arguments it will drop all data.
+You can also pass it table names, that will be cleared of any data. Beware that any scenarios already executed will still be
+marked as executed, so you won't be able to execute them again. If you want to execute those scenarios later in test, you
+can pass :undo option with list of scenarios to mark as not executed or :all if you want to mark that no scenario has been executed.
+
+ demolish :fruits, :trees # Deletes trees and fruits tables
+ demolish # Deletes all data except tables that are defined by Hornsby.skip_tables
+ demolish :fruits, :undo => :apples # Deletes fruits table and marks :apples scenario as not executed
+ demolish :undo => :all # Deletes all tables and marks all scenario as not executed (fresh start)
+
+Blueprints searches for scenario files in this particular order in Rails (Merb) root:
+* blueprints.rb
+* blueprints/*.rb
+* blueprint.rb
+* blueprint/*.rb
+* spec/blueprints.rb
+* spec/blueprints/*.rb
+* spec/blueprint.rb
+* spec/blueprint/*.rb
+* test/blueprints.rb
+* test/blueprints/*.rb
+* test/blueprint.rb
+* test/blueprint/*.rb
+You can pass :root option to override framework root and :filename option to pass custom filename pattern
+
+== Setup
+
+The easiest way to install this gem for Ruby on Rails is just add this line to config/environment.rb (or config/environments/test.rb):
+
+ config.gem 'sinsiliux-blueprints', :lib => 'blueprints', :source => 'http://gems.github.com'
+
+If you’re not using rails, then you can install it through command line
+
+ gem sources -a http://gems.github.com
+ sudo gem install sinsiliux-blueprints
+
+Lastly you could use it as plugin:
+
+ ruby script/plugin install git://github.com/sinsiliux/blueprints.git
+
+Hornsby scenarios is activated by calling enable_blueprints. For specifics on how to call that in your testing framework see a little lower.
+enable_blueprintssupports these parameters:
+* root - custom framework root if automatic detection fails for some reason (eg. not rails/merb project)
+* filename - custom files pattern with blueprints plans
+* prebuild - list of blueprints plans that should be preloaded (available in all tests, never reloaded so they're much faster)
+
+=== RSpec
+
+Add the following to spec_helper.rb
+
+ Spec::Runner.configure do |config|
+ ...
+
+ config.enable_blueprints :filename => 'scenarios.rb', :prebuild => :preloaded_scenario
+ end
+
+=== Test::Unit
+
+Add the following lines to test_helper.rb
+
+ class ActiveSupport::TestCase
+ ...
+
+ enable_blueprints
+ end
+
+== Advanced Usage
+
+Its just ruby, right? So go nuts:
+
+ 1.upto(9) do |i|
+ plan("user_#{i}") do
+ user = User.create! :name => "user#{i}"
+ instance_variable_set("@user_#{i}",user)
+ end
+ end
+
+== Transactions
+
+Blueprints is transactional, meaning that after every test transaction is dropped and database is reset to
+starting point. Starting point is empty database + any scenarios that you specify in enable_blueprints.
+
+== TODO
+
+* Add plan namespaces for better organisation.
+* Add ability to revert one plan.
+* Add preloading plans for whole block of tests.
+* Fix rake tasks
+
+== Credits
+
+Andrius Chamentauskas <sinsiliux@gmail.com>
+
+The code is based on hornsby scenario plugin by Lachie Cox, which is based on Err's code found in this post: http://errtheblog.com/post/7708
+
+== License
+
+MIT, see LICENCE
@@ -0,0 +1,37 @@
+GEM_NAME = "blueprints"
+GEM_VERSION = "0.1.0"
+
+Gem::Specification.new do |s|
+ s.name = GEM_NAME
+ s.version = GEM_VERSION
+ s.authors = ["Andrius Chamentauskas"]
+ s.email = "sinsiliux@gmail.com"
+ s.homepage = "http://github.com/sinsiliux/blueprints"
+ s.platform = Gem::Platform::RUBY
+ s.summary = "Another replacement for factories and fixtures"
+ s.files = %w{
+ lib/blueprints.rb
+ lib/blueprints/errors.rb
+ lib/blueprints/file_context.rb
+ lib/blueprints/helper.rb
+ lib/blueprints/plan.rb
+ lib/blueprints/rspec_extensions.rb
+ lib/blueprints/test_unit_extensions.rb
+ lib/tasks/blueprints_tasks.rake
+ README.rdoc
+ LICENSE
+ }
+ s.require_path = "lib"
+ s.test_files = %w{
+ spec/spec_helper.rb
+ spec/blueprints_spec.rb
+ spec/blueprints.rb
+ spec/db/fruit.rb
+ spec/db/database.yml.example
+ spec/db/schema.rb
+ test/test_helper.rb
+ test/blueprints_test.rb
+ }
+ s.has_rdoc = false
+ s.add_dependency("activerecord", ">= 2.0.0")
+end
@@ -0,0 +1 @@
+require 'blueprints' if RAILS_ENV == 'test'
@@ -0,0 +1 @@
+# Install hook code here
@@ -0,0 +1,75 @@
+require File.join(File.dirname(__FILE__), 'blueprints/plan')
+require File.join(File.dirname(__FILE__), 'blueprints/file_context')
+require File.join(File.dirname(__FILE__), 'blueprints/helper')
+require File.join(File.dirname(__FILE__), 'blueprints/errors')
+if defined? Spec
+ require File.join(File.dirname(__FILE__), 'blueprints/rspec_extensions')
+else
+ require File.join(File.dirname(__FILE__), 'blueprints/test_unit_extensions')
+end
+
+module Blueprints
+ PLAN_FILES = [nil, "spec", "test"].map do |dir|
+ ["blueprint", "blueprints"].map do |file|
+ path = File.join([dir, file].compact)
+ ["#{path}.rb", File.join(path, "*.rb")]
+ end
+ end.flatten
+
+ DELETE_POLICIES = {:delete => "DELETE FROM %s", :truncate => "TRUNCATE %s"}
+
+ def self.framework_root
+ @@framework_root ||= RAILS_ROOT rescue Rails.root rescue Merb.root rescue nil
+ end
+
+ def self.setup(current_context)
+ Plan.setup
+ Plan.copy_ivars(current_context, true)
+ ActiveRecord::Base.connection.increment_open_transactions
+ ActiveRecord::Base.connection.transaction_joinable = false
+ ActiveRecord::Base.connection.begin_db_transaction
+ end
+
+ def self.teardown
+ ActiveRecord::Base.connection.rollback_db_transaction
+ ActiveRecord::Base.connection.decrement_open_transactions
+ end
+
+ def self.load(options = {})
+ return unless Plan.plans.empty?
+
+ @@delete_sql = DELETE_POLICIES[options[:delete_policy]] || DELETE_POLICIES[:delete]
+ delete_tables
+ @@framework_root = options[:root] if options[:root]
+ load_scenarios_files(options[:filename] || PLAN_FILES)
+
+ Plan.prebuild(options[:prebuild])
+ end
+
+ 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
+
+ def self.delete_tables(*args)
+ args = tables if args.blank?
+ args.each { |t| ActiveRecord::Base.connection.delete(@@delete_sql % t) }
+ end
+
+ def self.tables
+ ActiveRecord::Base.connection.tables - skip_tables
+ end
+
+ def self.skip_tables
+ %w( schema_info schema_migrations )
+ end
+end
@@ -0,0 +1,11 @@
+module Blueprints
+ class PlanNotFoundError < NameError
+ def initialize(*args)
+ @plans = args
+ end
+
+ def to_s
+ "Plan(s) not found '#{@plans.join(',')}'"
+ end
+ end
+end
@@ -0,0 +1,7 @@
+module Blueprints
+ module FileContext
+ def self.plan(plan, &block)
+ Plan.new(plan, &block)
+ end
+ end
+end
@@ -0,0 +1,25 @@
+module Blueprints
+ module Helper
+ def build_plan(*names)
+ Plan.build(*names)
+ Plan.copy_ivars(self)
+ end
+
+ alias :build :build_plan
+
+ def demolish(*args)
+ options = args.extract_options!
+ Blueprints.delete_tables(*args)
+
+ if options[:undo] == :all
+ Plan.executed_plans.clear
+ else
+ undo = [options[:undo]].flatten.compact
+ unless (not_found = undo - Plan.executed_plans.to_a).blank?
+ raise(ArgumentError, "Scenario(s) #{not_found} not found")
+ end
+ Plan.executed_plans -= undo
+ end
+ end
+ end
+end
Oops, something went wrong.

0 comments on commit d1e81b0

Please sign in to comment.