blueprints 0.6.3 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,15 +1,15 @@
1
1
  module Blueprints
2
2
  # Class for actual blueprints. Allows building itself by executing block passed against current context.
3
- class Plan < Buildable
3
+ class Blueprint < Buildable
4
4
  # Initializes blueprint by name and block
5
5
  def initialize(name, &block)
6
6
  super(name)
7
7
  @block = block
8
8
  end
9
9
 
10
- # Builds plan and adds it to executed plan hash. Setups instance variable with same name as plan if it is not defined yet.
10
+ # Builds blueprint and adds it to executed blueprint hash. Setups instance variable with same name as blueprint if it is not defined yet.
11
11
  def build_self(build_once = true)
12
- if build_once and Namespace.root.executed_plans.include?(path)
12
+ if build_once and Namespace.root.executed_blueprints.include?(path)
13
13
  Blueprints.warn("Building with options, but blueprint was already built", @name) if Namespace.root.context.options.present?
14
14
  else
15
15
  surface_errors do
@@ -19,7 +19,7 @@ module Blueprints
19
19
  end
20
20
  end
21
21
  end
22
- Namespace.root.executed_plans << path
22
+ Namespace.root.executed_blueprints << path
23
23
  @result
24
24
  end
25
25
 
@@ -74,7 +74,7 @@ module Blueprints
74
74
  @parents.each do |p|
75
75
  parent = begin
76
76
  namespace[p]
77
- rescue PlanNotFoundError
77
+ rescue BlueprintNotFoundError
78
78
  Namespace.root[p]
79
79
  end
80
80
 
@@ -90,7 +90,7 @@ module Blueprints
90
90
  name = name.to_sym unless name == ''
91
91
  return name, []
92
92
  else
93
- raise TypeError, "Pass plan names as strings or symbols only, cannot build plan #{name.inspect}"
93
+ raise TypeError, "Pass blueprint names as strings or symbols only, cannot define blueprint #{name.inspect}"
94
94
  end
95
95
  end
96
96
  end
@@ -0,0 +1,42 @@
1
+ module Blueprints
2
+ class Configuration
3
+ SUPPORTED_ORMS = [nil, :active_record]
4
+ # Allows passing custom filename pattern in case blueprints are held in place other than spec/blueprint, test/blueprint, blueprint.
5
+ attr_accessor :filename
6
+ # Allows passing scenarios that should be prebuilt and available in all tests. Works similarly to fixtures.
7
+ attr_accessor :prebuild
8
+ # Allows passing custom root folder to use in case of non rails project. Defaults to RAILS_ROOT or current folder if RAILS_ROOT is not defined.
9
+ attr_accessor :root
10
+ # By default blueprints runs each test in it's own transaction. This may sometimes be not desirable so this options allows to turn this off.
11
+ attr_accessor :transactions
12
+ # Returns ORM that is used, default is :active_record
13
+ attr_reader :orm
14
+
15
+ # Sets default attributes for all attributes
16
+ def initialize
17
+ @filename = [nil, "spec", "test"].map do |dir|
18
+ ["blueprint"].map do |file|
19
+ path = File.join([dir, file].compact)
20
+ ["#{path}.rb", File.join(path, "*.rb")]
21
+ end
22
+ end.flatten
23
+ @orm = :active_record
24
+ @prebuild = []
25
+ @transactions = true
26
+ @root = if defined?(RAILS_ROOT)
27
+ RAILS_ROOT
28
+ else
29
+ nil
30
+ end
31
+ end
32
+
33
+ # Allows specifying what ORM should be used. See SUPPORTED_ORMS to check what values it can contain.
34
+ def orm=(value)
35
+ if SUPPORTED_ORMS.include?(value)
36
+ @orm = value
37
+ else
38
+ raise ArgumentError, "Unsupported ORM #{value.inspect}. Blueprints supports only #{SUPPORTED_ORMS.collect(&:inspect).join(', ')}"
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,30 @@
1
+ module Blueprints
2
+ module Convertable
3
+ def persist(str)
4
+ File.open(@output_file, 'a+') do |f|
5
+ f.write(str)
6
+ end
7
+ end
8
+
9
+ def process!
10
+ persist "#{banner_start}#{convert}#{banner_end}"
11
+ end
12
+
13
+ def banner_start
14
+ "### blueprints from #{@format}\n\n"
15
+ end
16
+
17
+ def banner_end
18
+ "\n###\n"
19
+ end
20
+ end
21
+
22
+ class Converter
23
+ def self.for(format, options = {})
24
+ require "blueprints/convertable/#{format}"
25
+ Class.new("Blueprints::#{format.capitalize}Converter".constantize) do |options|
26
+ include Convertable
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,43 @@
1
+ module Blueprints
2
+ class FixturesConverter
3
+ def initialize(options = {})
4
+ @format = "fixtures"
5
+ @source_files = options[:source_files]
6
+ @output_file = options[:output_file]
7
+ @blueprints_data = ""
8
+
9
+ raise "No source files given" unless @source_files
10
+ raise "No output file given" unless @output_file
11
+ end
12
+
13
+ def convert
14
+ @source_files.each do |fixture_file|
15
+ klass = File.basename(fixture_file, '.yml').singularize.capitalize
16
+
17
+ loaded_yaml = YAML.load(File.read(fixture_file))
18
+
19
+ @blueprints_data = loaded_yaml.collect do |title,yaml_obj|
20
+ params = yaml_obj.collect do |k,v|
21
+ ":#{k} => #{parameterize(v)}"
22
+ end.join(', ')
23
+
24
+ "#{klass}.blueprint(:#{title}, {#{params}})\n"
25
+ end
26
+ end
27
+
28
+ @blueprints_data
29
+ end
30
+
31
+ def parameterize(object)
32
+ if object =~ /<%=\s+?(.+)\s+%>/
33
+ '('+$1+')'
34
+ elsif object.is_a?(String)
35
+ (%Q(#{object})).inspect
36
+ elsif object.nil?
37
+ 'nil'
38
+ else
39
+ object.to_s
40
+ end
41
+ end
42
+ end
43
+ end
@@ -1,94 +1,53 @@
1
1
  module Blueprints
2
- module DatabaseBackends
3
- class ActiveRecord
4
- DELETE_POLICIES = {:delete => "DELETE FROM %s", :truncate => "TRUNCATE %s"}
5
-
6
- # Extends active record with blueprint method
7
- def initialize
8
- ::ActiveRecord::Base.send(:include, ActiveRecordExtensions::Instance)
9
- ::ActiveRecord::Base.extend(ActiveRecordExtensions::Class)
10
- end
11
-
12
- # Starts new transaction and marks it as unjoinable so that test case could use transactions too.
13
- def start_transaction
14
- ::ActiveRecord::Base.connection.increment_open_transactions
15
- ::ActiveRecord::Base.connection.transaction_joinable = false
16
- ::ActiveRecord::Base.connection.begin_db_transaction
17
- end
18
-
19
- # Rollbacks transaction
20
- def rollback_transaction
21
- ::ActiveRecord::Base.connection.rollback_db_transaction
22
- ::ActiveRecord::Base.connection.decrement_open_transactions
23
- end
24
-
25
- # Clears all tables using delete policy specified. Also accepts list of tables to delete.
26
- def delete_tables(delete_policy, *args)
27
- delete_policy ||= :delete
28
- raise ArgumentError, "Unknown delete policy #{delete_policy}" unless DELETE_POLICIES.keys.include?(delete_policy)
29
- args = tables if args.blank?
30
- args.each { |t| ::ActiveRecord::Base.connection.delete(DELETE_POLICIES[delete_policy] % t) }
31
- end
32
-
33
- # Returns all tables without skipped ones.
34
- def tables
35
- ::ActiveRecord::Base.connection.tables - skip_tables
36
- end
37
-
38
- # Returns tables that should never be cleared (those that contain migrations information).
39
- def skip_tables
40
- %w( schema_info schema_migrations )
41
- end
42
-
43
- # Extensions for active record class
44
- module ActiveRecordExtensions
45
- module Class
46
- # Two forms of this method can be used. First one is typically used inside blueprint block. Essentially it does
47
- # same as <tt>create!</tt>, except it does bypass attr_protected and attr_accessible. It accepts only a hash or attributes,
48
- # same as <tt>create!</tt> does.
49
- # blueprint :post => :user do
50
- # @user.posts.blueprint(:title => 'first post', :text => 'My first post')
51
- # end
52
- # The second form is used when you want to define new blueprint. It takes first argument as name of blueprint
53
- # and second one as hash of attributes. As you cannot use instance variables outside of blueprint block, you need
54
- # to prefix them with colon. So the example above could be rewritten like this:
55
- # Post.blueprint(:post, :title => 'first post', :text => 'My first post', :user => d(:user))
56
- # or like this:
57
- # Post.blueprint(:post, :title => 'first post', :text => 'My first post', :user => :@user).depends_on(:user)
58
- # or like this:
59
- # Post.blueprint({:post => :user}, :title => 'first post', :text => 'My first post', :user => :@user)
60
- def blueprint(name_or_attrs, attrs = {})
61
- if Blueprints::FileContext.evaluating
62
- klass = self
63
- blueprint = Blueprints::Plan.new(name_or_attrs) { klass.blueprint attributes }
64
- blueprint.depends_on(*attrs.values.select {|attr| attr.is_a?(Blueprints::Buildable::Dependency) }).attributes(attrs)
65
- blueprint
66
- else
67
- if name_or_attrs.is_a?(Array)
68
- name_or_attrs.collect { |attrs| blueprint(attrs) }
69
- else
70
- object = new
71
- object.blueprint(name_or_attrs)
72
- object
73
- end
74
- end
2
+ # Extensions for active record class
3
+ module ActiveRecordExtensions
4
+ module ClassMethods
5
+ # Two forms of this method can be used. First one is typically used inside blueprint block. Essentially it does
6
+ # same as <tt>create!</tt>, except it does bypass attr_protected and attr_accessible. It accepts only a hash or attributes,
7
+ # same as <tt>create!</tt> does.
8
+ # blueprint :post => :user do
9
+ # @user.posts.blueprint(:title => 'first post', :text => 'My first post')
10
+ # end
11
+ # The second form is used when you want to define new blueprint. It takes first argument as name of blueprint
12
+ # and second one as hash of attributes. As you cannot use instance variables outside of blueprint block, you need
13
+ # to prefix them with colon. So the example above could be rewritten like this:
14
+ # Post.blueprint(:post, :title => 'first post', :text => 'My first post', :user => d(:user))
15
+ # or like this:
16
+ # Post.blueprint(:post, :title => 'first post', :text => 'My first post', :user => :@user).depends_on(:user)
17
+ # or like this:
18
+ # Post.blueprint({:post => :user}, :title => 'first post', :text => 'My first post', :user => :@user)
19
+ def blueprint(name_or_attrs, attrs = {})
20
+ if Blueprints::FileContext.evaluating
21
+ klass = self
22
+ blueprint = Blueprints::Blueprint.new(name_or_attrs) { klass.blueprint attributes }
23
+ blueprint.depends_on(*attrs.values.select {|attr| attr.is_a?(Blueprints::Buildable::Dependency) }).attributes(attrs)
24
+ blueprint
25
+ else
26
+ if name_or_attrs.is_a?(Array)
27
+ name_or_attrs.collect { |attrs| blueprint(attrs) }
28
+ else
29
+ object = new
30
+ object.blueprint(name_or_attrs)
31
+ object
75
32
  end
76
33
  end
34
+ end
35
+ end
77
36
 
78
- module Instance
79
- # Updates attributes of object and calls save!. Bypasses attr_protected and attr_accessible.
80
- def blueprint(attributes)
81
- attributes.each do |attr, value|
82
- iv_name = value.iv_name if value.is_a?(Blueprints::Buildable::Dependency)
83
- iv_name = value if value.is_a? Symbol and value.to_s =~ /^@.+$/
84
- attributes[attr] = Blueprints::Namespace.root.context.instance_variable_get(iv_name) if iv_name
85
- end
86
- send(:attributes=, attributes, false)
87
- save!
88
- end
37
+ module InstanceMethods
38
+ # Updates attributes of object and calls save!. Bypasses attr_protected and attr_accessible.
39
+ def blueprint(attributes)
40
+ attributes.each do |attr, value|
41
+ iv_name = value.iv_name if value.is_a?(Blueprints::Buildable::Dependency)
42
+ iv_name = value if value.is_a? Symbol and value.to_s =~ /^@.+$/
43
+ attributes[attr] = Blueprints::Namespace.root.context.instance_variable_get(iv_name) if iv_name
89
44
  end
45
+ send(:attributes=, attributes, false)
46
+ save!
90
47
  end
91
-
92
48
  end
93
49
  end
94
50
  end
51
+
52
+ ::ActiveRecord::Base.send(:include, Blueprints::ActiveRecordExtensions::InstanceMethods)
53
+ ::ActiveRecord::Base.extend(Blueprints::ActiveRecordExtensions::ClassMethods)
@@ -1,12 +1,12 @@
1
1
  module Blueprints
2
2
  # Is raised when blueprint or namespace is not found.
3
- class PlanNotFoundError < NameError
3
+ class BlueprintNotFoundError < NameError
4
4
  def initialize(*args)
5
- @plans = args
5
+ @blueprints = args
6
6
  end
7
7
 
8
8
  def to_s
9
- "Plan/namespace not found '#{@plans.join(',')}'"
9
+ "Blueprint/namespace not found '#{@blueprints.join(',')}'"
10
10
  end
11
11
  end
12
12
  end
@@ -0,0 +1,22 @@
1
+ module EnableBlueprints #:nodoc:
2
+ def enable_blueprints(options = {})
3
+ STDERR.puts "DEPRECATION WARNING: enable_blueprints is deprecated. Use Blueprints.enable"
4
+ Blueprints.enable do |config|
5
+ options.each {|option, value| config.send("#{option}=", value) }
6
+ end
7
+ end
8
+ end
9
+
10
+ module ActiveSupport #:nodoc:all
11
+ class TestCase
12
+ include EnableBlueprints
13
+ end
14
+ end if defined? ActiveSupport::TestCase
15
+
16
+ module Spec #:nodoc:all
17
+ module Runner
18
+ class Configuration
19
+ include EnableBlueprints
20
+ end
21
+ end
22
+ end if defined? Spec or $0 =~ /script.spec$/
@@ -1,35 +1,10 @@
1
- # Module that gets included into RSpec Configuration class to provide enable_blueprints method.
2
- module Blueprints::RspecMixin
3
- # Enables blueprints in rspec. Is automatically added if <tt>Spec</tt> is defined at loading time or <tt>script/spec</tt>
4
- # is used. You might need to require it manually in certain case (eg. running specs from metrics).
5
- # Accepts options hash. For supported options please check Blueprints.load.
6
- def enable_blueprints(options = {})
7
- Blueprints.load(options)
8
-
9
- include(Blueprints::Helper)
10
- before do
11
- Blueprints.setup(self)
12
- end
13
- after do
14
- Blueprints.teardown
15
- end
1
+ config_class = defined?(RSpec) ? RSpec : Spec::Runner
2
+ config_class.configure do |config|
3
+ config.include(Blueprints::Helper)
4
+ config.before do
5
+ Blueprints.setup(self)
16
6
  end
17
- end
18
-
19
- if defined?(RSpec)
20
- module RSpec #:nodoc:
21
- module Core #:nodoc:
22
- class Configuration
23
- include Blueprints::RspecMixin
24
- end
25
- end
26
- end
27
- else
28
- module Spec #:nodoc:
29
- module Runner #:nodoc:
30
- class Configuration
31
- include Blueprints::RspecMixin
32
- end
33
- end
7
+ config.after do
8
+ Blueprints.teardown
34
9
  end
35
10
  end
@@ -1,21 +1,12 @@
1
- module Test #:nodoc:
2
- module Unit #:nodoc:
3
- class TestCase
4
- # Runs tests with blueprints support
5
- def run_with_blueprints(result, &progress_block)
6
- Blueprints.setup(self)
7
- run_without_blueprints(result, &progress_block)
8
- Blueprints.teardown
9
- end
10
-
11
- # Enables blueprints in test/unit. Is automatically added if <tt>Spec</tt> is not defined at loading time.
12
- # You might need to require it manually in certain case (eg. using both rspec and test/unit).
13
- # Accepts options hash. For supported options please check Blueprints.load.
14
- def self.enable_blueprints(options = {})
15
- include Blueprints::Helper
16
- Blueprints.load(options)
17
- alias_method_chain :run, :blueprints
18
- end
1
+ module ActiveSupport #:nodoc:all
2
+ class TestCase
3
+ include Blueprints::Helper
4
+ # Runs tests with blueprints support
5
+ def run_with_blueprints(result, &progress_block)
6
+ Blueprints.setup(self)
7
+ run_without_blueprints(result, &progress_block)
8
+ Blueprints.teardown
19
9
  end
10
+ alias_method_chain :run, :blueprints
20
11
  end
21
12
  end
@@ -3,9 +3,9 @@ module Blueprints
3
3
  module FileContext
4
4
  mattr_accessor :evaluating
5
5
 
6
- # Creates a new plan by name and block passed
7
- def self.blueprint(plan, &block)
8
- Plan.new(plan, &block)
6
+ # Creates a new blueprint by name and block passed
7
+ def self.blueprint(name, &block)
8
+ Blueprint.new(name, &block)
9
9
  end
10
10
 
11
11
  # Creates new namespace by name, and evaluates block against it.
@@ -37,17 +37,19 @@ module Blueprints
37
37
  #
38
38
  # TODO: add sample usage
39
39
  def demolish(*args)
40
+ STDERR.puts "DEPRECATION WARNING: demolish is deprecated and will be changed to support per blueprint demolishing in blueprints 0.8.0"
40
41
  options = args.extract_options!
41
- Blueprints.delete_tables(*args)
42
+ args = (ActiveRecord::Base.connection.tables - ['schema_migrations']) if args.blank?
43
+ args.each {|table| ActiveRecord::Base.connection.execute("DELETE FROM #{table}") }
42
44
 
43
45
  if options[:undo] == :all
44
- Namespace.root.executed_plans.clear
46
+ Namespace.root.executed_blueprints.clear
45
47
  else
46
48
  undo = [options[:undo]].flatten.compact.collect {|bp| bp.to_s }
47
- unless (not_found = undo - Namespace.root.executed_plans.to_a).blank?
48
- raise(PlanNotFoundError, not_found)
49
+ unless (not_found = undo - Namespace.root.executed_blueprints.to_a).blank?
50
+ raise(BlueprintNotFoundError, not_found)
49
51
  end
50
- Namespace.root.executed_plans -= undo
52
+ Namespace.root.executed_blueprints -= undo
51
53
  end
52
54
  end
53
55
  end