blueprints 0.6.3 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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