blueprints 0.8.2 → 0.9.0

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.
Files changed (61) hide show
  1. data/.gitignore +4 -1
  2. data/Gemfile +2 -0
  3. data/Gemfile.lock +82 -18
  4. data/LICENSE +1 -1
  5. data/README.rdoc +38 -8
  6. data/Rakefile +11 -35
  7. data/blueprints.gemspec +29 -123
  8. data/features/support/env.rb +7 -10
  9. data/lib/blueprints.rb +68 -12
  10. data/lib/blueprints/blueprint.rb +39 -19
  11. data/lib/blueprints/buildable.rb +92 -74
  12. data/lib/blueprints/configuration.rb +18 -4
  13. data/lib/blueprints/context.rb +148 -5
  14. data/lib/blueprints/database_cleaner_fix.rb +9 -0
  15. data/lib/blueprints/dependency.rb +32 -21
  16. data/lib/blueprints/eval_context.rb +51 -0
  17. data/lib/blueprints/extensions.rb +115 -0
  18. data/lib/blueprints/extensions/rspec.rb +3 -1
  19. data/lib/blueprints/helper.rb +52 -20
  20. data/lib/blueprints/namespace.rb +31 -12
  21. data/lib/blueprints/root_namespace.rb +24 -25
  22. data/lib/blueprints/version.rb +3 -0
  23. data/spec/{active_record/blueprint.rb → blueprint.rb} +14 -17
  24. data/spec/{active_record/blueprints_spec.rb → blueprints_spec.rb} +40 -59
  25. data/spec/spec_helper.rb +34 -0
  26. data/spec/support/active_record/database.yml.example +7 -0
  27. data/spec/support/active_record/initializer.rb +15 -0
  28. data/spec/{active_record/fixtures → support/active_record}/schema.rb +0 -0
  29. data/spec/support/dm-core/initializer.rb +31 -0
  30. data/spec/support/mongo_mapper/database.yml.example +2 -0
  31. data/spec/support/mongo_mapper/initializer.rb +20 -0
  32. data/spec/support/mongoid/database.yml.example +2 -0
  33. data/spec/support/mongoid/initializer.rb +23 -0
  34. data/spec/support/none/initializer.rb +63 -0
  35. data/spec/unit/active_record_spec.rb +1 -6
  36. data/spec/unit/blueprint_spec.rb +91 -20
  37. data/spec/unit/blueprints_spec.rb +44 -0
  38. data/spec/unit/buildable_spec.rb +37 -6
  39. data/spec/unit/configuration_spec.rb +11 -0
  40. data/spec/unit/context_spec.rb +100 -0
  41. data/spec/unit/dependency_spec.rb +24 -19
  42. data/spec/unit/eval_context_spec.rb +56 -0
  43. data/spec/unit/fixtures.rb +61 -0
  44. data/spec/unit/namespace_spec.rb +59 -11
  45. data/spec/unit/spec_helper.rb +8 -16
  46. data/test/blueprints_test.rb +40 -59
  47. data/test/test_helper.rb +6 -16
  48. data/test_all.sh +45 -0
  49. metadata +178 -61
  50. data/VERSION +0 -1
  51. data/lib/blueprints/core_ext.rb +0 -69
  52. data/lib/blueprints/file_context.rb +0 -37
  53. data/spec/active_record/fixtures/database.yml.example +0 -8
  54. data/spec/active_record/fixtures/fruit.rb +0 -3
  55. data/spec/active_record/fixtures/tree.rb +0 -4
  56. data/spec/active_record/spec_helper.rb +0 -37
  57. data/spec/no_db/blueprint.rb +0 -9
  58. data/spec/no_db/blueprints_spec.rb +0 -45
  59. data/spec/no_db/fixtures/fruit.rb +0 -15
  60. data/spec/no_db/spec_helper.rb +0 -14
  61. data/spec/test_all.sh +0 -39
@@ -1,19 +1,16 @@
1
1
  require 'cucumber'
2
2
  require 'active_record'
3
- Root = File.expand_path(File.dirname(__FILE__) + '/../..')
4
- require File.expand_path(Root + '/lib/blueprints')
3
+ require 'pathname'
4
+ Root = Pathname.new(__FILE__).dirname.join('..', '..')
5
+ $: << Root.join('lib').to_s
6
+
7
+ require 'blueprints'
8
+ require File.dirname(__FILE__) + "/../../spec/support/active_record/initializer"
5
9
 
6
- ActiveRecord::Base.logger = Logger.new("debug.log")
7
- databases = YAML::load_file(Root + "/spec/active_record/fixtures/database.yml")
8
- db_info = databases[ENV["DB"] || "test"]
9
- ActiveRecord::Base.establish_connection(db_info)
10
10
  # Comment out the next two lines if you're not using RSpec's matchers (should / should_not) in your steps.
11
11
  #require 'cucumber/rspec'
12
12
 
13
- require Root + '/spec/active_record/fixtures/fruit'
14
- require Root + '/spec/active_record/fixtures/tree'
15
-
16
13
  Blueprints.enable do |config|
17
- config.root = Root + '/spec/active_record'
14
+ config.root = Root.join('spec')
18
15
  config.prebuild = :big_cherry
19
16
  end
@@ -1,23 +1,33 @@
1
1
  require 'active_support'
2
- require 'active_support/core_ext'
2
+ require 'active_support/core_ext/kernel/singleton_class'
3
+ require 'active_support/core_ext/object/blank'
4
+ require 'active_support/core_ext/module/delegation'
5
+ require 'active_support/core_ext/class/attribute_accessors'
6
+ require 'active_support/core_ext/hash/reverse_merge'
7
+ require 'active_support/core_ext/hash/keys'
8
+ require 'active_support/core_ext/string/inflections'
9
+ require 'active_support/core_ext/enumerable'
3
10
  require 'database_cleaner'
4
11
  require 'set'
5
12
 
6
13
  files = %w{
7
- configuration context buildable namespace root_namespace blueprint file_context helper errors dependency core_ext
14
+ configuration context eval_context buildable namespace root_namespace blueprint helper errors dependency extensions database_cleaner_fix
8
15
  }
9
- files.each { |f| require File.join(File.dirname(__FILE__), 'blueprints', f) }
16
+ files.each { |f| require "blueprints/#{f}" }
10
17
 
18
+ # Main namespace of blueprints. Contains methods for Blueprints setup.
11
19
  module Blueprints
12
20
  # Contains current configuration of blueprints
21
+ # @return [Blueprints::Configuration] Current configuration.
13
22
  def self.config
14
23
  @@config ||= Blueprints::Configuration.new
15
24
  end
16
25
 
17
26
  # Setups variables from global context and starts transaction. Should be called before every test case.
27
+ # @param current_context Object to copy instance variables for prebuilt blueprints/namespaces.
18
28
  def self.setup(current_context)
19
29
  Namespace.root.setup
20
- Namespace.root.copy_ivars(current_context)
30
+ Namespace.root.eval_context.copy_instance_variables(current_context)
21
31
  if_orm { DatabaseCleaner.start }
22
32
  end
23
33
 
@@ -28,6 +38,8 @@ module Blueprints
28
38
 
29
39
  # Enables blueprints support for RSpec or Test::Unit depending on whether (R)Spec is defined or not. Yields
30
40
  # Blueprints::Configuration object that you can use to configure blueprints.
41
+ # @yield [config] Used to configure blueprints.
42
+ # @yieldparam [Blueprints::Configuration] config Current blueprints configuration.
31
43
  def self.enable
32
44
  yield config if block_given?
33
45
  load
@@ -54,18 +66,39 @@ module Blueprints
54
66
  Namespace.root.prebuild(config.prebuild) if config.transactions
55
67
  end
56
68
 
69
+ # Returns backtrace cleaner that is used to extract lines from user application.
70
+ # @return [ActiveSupport::BacktraceCleaner] Backtrace cleaner
57
71
  def self.backtrace_cleaner
58
72
  @backtrace_cleaner ||= ActiveSupport::BacktraceCleaner.new.tap do |bc|
59
- root_sub = /^#{config.root}[\\\/]/
60
- blueprints_path = File.dirname(__FILE__).sub(root_sub, '')
73
+ root_sub = /^#{config.root}[\\\/]/
74
+ blueprints_path = File.expand_path(File.dirname(__FILE__))
61
75
 
62
76
  bc.add_filter { |line| line.sub(root_sub, '') }
63
-
64
- bc.add_silencer { |line| File.dirname(line).starts_with?(blueprints_path) }
65
- bc.add_silencer { |line| Gem.path.any? { |path| File.dirname(line).starts_with?(path) } }
77
+ bc.add_silencer { |line| [blueprints_path, *Gem.path].any? { |path| File.expand_path(File.dirname(line)).starts_with?(path) } }
66
78
  end
67
79
  end
68
80
 
81
+ # Returns array of blueprints that have not been used until now.
82
+ # @return [Array<String>] List of unused blueprints.
83
+ def self.unused
84
+ each_blueprint.select { |blueprint| blueprint.uses.zero? }.collect(&:full_name)
85
+ end
86
+
87
+ # Returns array of most used blueprints.
88
+ # @param [Hash] options Options on what blueprints to return.
89
+ # @option options [Integer] :count Max amount of most used blueprints to return.
90
+ # @option options [Integer] :at_least Only blueprints that have at least specified number of uses will be returned.
91
+ # @return [Array<Array<String, Integer>>] List of most used blueprints together with their use counts.
92
+ def self.most_used(options = {})
93
+ blueprints = each_blueprint.collect { |blueprint| [blueprint.full_name, blueprint.uses] }.sort { |a, b| b[1] <=> a[1] }
94
+ blueprints = blueprints.take(options[:count]) if options[:count]
95
+ blueprints.reject! { |blueprint| blueprint[1] < options[:at_least] } if options[:at_least]
96
+ blueprints
97
+ end
98
+
99
+ # Warns a user (often about deprecated feature).
100
+ # @param [String] message Message to output.
101
+ # @param [Blueprints::Blueprint] blueprint Name of blueprint that this occurred in.
69
102
  def self.warn(message, blueprint)
70
103
  $stderr.puts("**WARNING** #{message}: '#{blueprint.name}'")
71
104
  $stderr.puts(backtrace_cleaner.clean(blueprint.backtrace(caller)).first)
@@ -73,12 +106,14 @@ module Blueprints
73
106
 
74
107
  protected
75
108
 
76
- # Loads blueprints file and creates blueprints from data it contains. Is run by setup method
109
+ # Loads blueprints file and creates blueprints from data it contains. Is run by setup method.
110
+ # @param [Array<String>] patterns List of filesystem patterns to search blueprint files in.
111
+ # @raise [RuntimeError] If no blueprints file can be found.
77
112
  def self.load_scenarios_files(patterns)
78
113
  patterns.each do |pattern|
79
114
  pattern = config.root.join(pattern)
80
- files = Dir[pattern.to_s]
81
- files.each { |f| FileContext.new f }
115
+ files = Dir[pattern.to_s]
116
+ files.each { |file| Context.eval_within_context(:file => file, :namespace => Namespace.root) }
82
117
  return if files.size > 0
83
118
  end
84
119
 
@@ -87,6 +122,27 @@ module Blueprints
87
122
 
88
123
  private
89
124
 
125
+ def self.each_blueprint(from = Namespace.root)
126
+ enumerator_class.new do |enum|
127
+ from.children.values.collect do |child|
128
+ if child.is_a?(Blueprints::Blueprint)
129
+ enum.yield child
130
+ else
131
+ each_blueprint(child).each { |blueprint| enum.yield blueprint }
132
+ end
133
+ end
134
+ end
135
+ end
136
+
137
+ def self.enumerator_class
138
+ @enumerator_class ||= if defined?(Enumerator)
139
+ Enumerator
140
+ else
141
+ require 'generator'
142
+ Generator
143
+ end
144
+ end
145
+
90
146
  def self.if_orm
91
147
  yield
92
148
  rescue DatabaseCleaner::NoORMDetected, DatabaseCleaner::NoStrategySetError
@@ -1,48 +1,68 @@
1
1
  module Blueprints
2
2
  # Class for actual blueprints. Allows building itself by executing block passed against current context.
3
3
  class Blueprint < Buildable
4
- attr_reader :file
5
- # Initializes blueprint by name and block
6
- def initialize(name, file, &block)
7
- @file = file
8
- super(name)
4
+ # Holds how many times this particular blueprint was built
5
+ attr_reader :uses
6
+
7
+ # Initializes blueprint by name, context and block. Also sets default demolish and update blocks.
8
+ # @param name (see Buildable#initialize)
9
+ # @param context (see Buildable#initialize)
10
+ def initialize(name, context, &block)
11
+ super(name, context)
9
12
 
10
13
  ivname = variable_name
11
14
  @block = block
12
- @demolish_block = lambda { instance_variable_get(ivname).destroy }
13
- @update_block = lambda { instance_variable_get(ivname).blueprint(options) }
15
+ @demolish_block = Proc.new { instance_variable_get(ivname).destroy }
16
+ @update_block = Proc.new { instance_variable_get(ivname).blueprint(options) }
17
+ @uses = 0
14
18
  end
15
19
 
16
- # Builds blueprint and adds it to executed blueprint hash. Setups instance variable with same name as blueprint if it is not defined yet.
17
- def build_self(build_once = true)
20
+ # Builds blueprint and adds it to executed blueprint array. Setups instance variable with same name as blueprint if it is not defined yet.
21
+ # Marks blueprint as used.
22
+ # @param eval_context (see Buildable#build)
23
+ # @param build_once (see Buildable#build)
24
+ # @param options (see Buildable#build)
25
+ def build_self(eval_context, build_once, options)
26
+ @uses += 1 unless built?
18
27
  surface_errors do
19
28
  if built? and build_once
20
- Namespace.root.context.instance_eval(&@update_block) if RootNamespace.root.context.options.present?
29
+ eval_context.instance_eval(@context, options, &@update_block) if options.present?
21
30
  elsif @block
22
- self.result = Namespace.root.context.instance_eval(&@block)
31
+ result(eval_context) { eval_context.instance_eval(@context, options, &@block) }
23
32
  end
24
33
  end
25
34
  end
26
35
 
27
36
  # Changes blueprint block to build another blueprint by passing additional options to it. Usually used to dry up
28
37
  # blueprints that are often built with some options.
38
+ # @example Extending blueprints
39
+ # Post.blueprint :post, :title => 'hello blueprints'
40
+ # blueprint(:published_post).extends(:post, :published_at => Time.now)
41
+ # @param [Symbol, String] parent Name of parent blueprint.
42
+ # @param [Hash] options Options to be passed when building parent.
29
43
  def extends(parent, options)
30
44
  attributes(options)
31
45
  @block = Proc.new { build parent => attributes }
32
46
  end
33
47
 
48
+ # Changes backtrace to include what blueprint was being built.
49
+ # @param [Array<String>] trace Current trace
50
+ # @return [Array<String>] Changed trace with included currently built blueprint name.
34
51
  def backtrace(trace)
35
- trace.collect { |line| line.sub(/^\(eval\):(\d+).*/, "#{@file}:\\1:in blueprint '#{@name}'") }
52
+ trace.collect! { |line| line.sub(/^#{@context.file}:(\d+).*/, "#{@context.file}:\\1:in blueprint '#{@name}'") }
36
53
  end
37
54
 
38
- # If block is passed then sets custom demolish block for this blueprint.
39
- # If no block is passed then calls demolish block and marks blueprint as not built.
40
- # Raises DemolishError if blueprints has not been built.
41
- def demolish(&block)
55
+ # @overload demolish(&block)
56
+ # Sets custom block for demolishing this blueprint.
57
+ # @overload demolish(eval_context)
58
+ # Demolishes blueprint by calling demolish block.
59
+ # @param [Blueprints::EvalContext] eval_context Context where blueprint was built in.
60
+ # @raise [Blueprints::DemolishError] If blueprint has not been built yet.
61
+ def demolish(eval_context = nil, &block)
42
62
  if block
43
63
  @demolish_block = block
44
- elsif built?
45
- Namespace.root.context.instance_eval(&@demolish_block)
64
+ elsif eval_context and built?
65
+ eval_context.instance_eval(@context, {}, &@demolish_block)
46
66
  undo!
47
67
  else
48
68
  raise DemolishError, @name
@@ -59,7 +79,7 @@ module Blueprints
59
79
  def surface_errors
60
80
  yield
61
81
  rescue StandardError => error
62
- error.set_backtrace(backtrace(error.backtrace))
82
+ backtrace(error.backtrace)
63
83
  raise error
64
84
  end
65
85
  end
@@ -1,60 +1,72 @@
1
1
  module Blueprints
2
2
  class Buildable
3
+ delegate :namespace, :dependencies, :to => :@context
3
4
  attr_reader :name
4
- attr_accessor :namespace
5
5
 
6
- # Initializes new Buildable object by name.
7
- # Name can be Symbol, String or Hash. If Hash is passed, then first key is assumed name, and value(s) of that key
8
- # are assumed as dependencies. Raises error class of name parameter is not what is expected.
9
- # Warns if name has already been taken.
10
- def initialize(name)
6
+ # Initializes new Buildable object by name and context which it belongs to.
7
+ # @param [#to_sym, Hash] name Name of buildable. If hash is passed then first key is assumed name, and
8
+ # value(s) of that key are assumed as dependencies.
9
+ # @param [Blueprints::Context] context Context of buildable that later might get updated.
10
+ # @raise [TypeError] If name is invalid.
11
+ def initialize(name, context)
12
+ @context = context
13
+
14
+ if name.nil?
15
+ default_attribute = Blueprints.config.default_attributes.detect { |attribute| attributes.has_key?(attribute) }
16
+ name = attributes[default_attribute].parameterize('_') if default_attribute
17
+ end
11
18
  @name, parents = parse_name(name)
12
19
  depends_on(*parents)
13
20
 
14
- Blueprints.warn("Overwriting existing blueprint", self) if Namespace.root and Namespace.root.children[@name]
15
- Namespace.root.add_child(self) if Namespace.root
21
+ namespace.add_child(self) if namespace
16
22
  end
17
23
 
18
- # Defines blueprint dependencies. Used internally, but can be used externally too.
19
- def depends_on(*scenarios)
20
- @parents = (@parents || []) + scenarios.map { |s| s.to_sym }
21
- self
24
+ # Returns class, name, attributes and dependencies of buildable in nice formatted string.
25
+ # @return [String] Inspected properties of buildable.
26
+ def inspect
27
+ "<##{self.class} name: #{full_name.inspect}, attributes: #{attributes.inspect}, dependencies: #{dependencies.inspect}>"
28
+ end
29
+
30
+ # Defines dependencies of buildable by updating it's context.
31
+ # @param [Array<String, Symbol>] dependencies List of dependencies.
32
+ def depends_on(*dependencies)
33
+ update_context(:dependencies => dependencies)
34
+ end
35
+
36
+ # @overload attributes
37
+ # Returns attributes of buildable
38
+ # @return [Hash] Attributes of buildable
39
+ # @overload attributes(value)
40
+ # Merges attributes of buildable with new attributes by updating context
41
+ # @param [Hash] Updated attributes
42
+ def attributes(value = nil)
43
+ if value
44
+ update_context(:attributes => value)
45
+ else
46
+ @context.attributes
47
+ end
22
48
  end
23
49
 
24
50
  # Builds dependencies of blueprint and then blueprint itself.
25
- #
26
- # +build_once+ - pass false if you want to build blueprint again instead of updating old one.
27
- #
28
- # +options+ - list of options to be accessible in the body of a blueprint. Defaults to empty Hash.
29
- def build(build_once = true, options = {})
30
- return result if @building or (built? and build_once and options.blank?)
51
+ # @param [Blueprints::EvalContext] eval_context Context to build buildable object in.
52
+ # @param [true, false] build_once Used if buildable is already built. If true then old one is updated else buildable is built again.
53
+ # @param [Hash] options List of options to be accessible in the body of a blueprint.
54
+ def build(eval_context, build_once = true, options = {})
55
+ return result(eval_context) if @building or (built? and build_once and options.blank?)
31
56
  @building = true
32
57
 
33
- each_namespace {|namespace| namespace.build_parents }
34
- build_parents
58
+ each_namespace { |namespace| namespace.build_parents(eval_context) }
59
+ build_parents(eval_context)
35
60
 
36
- old_options, old_attributes = Namespace.root.context.options, Namespace.root.context.attributes
37
- Namespace.root.context.options, Namespace.root.context.attributes = options, normalized_attributes.merge(options)
38
- each_namespace {|namespace| Namespace.root.context.attributes.reverse_merge! namespace.normalized_attributes }
39
-
40
- build_self(build_once)
41
- Namespace.root.context.options, Namespace.root.context.attributes = old_options, old_attributes
61
+ result = build_self(eval_context, build_once, options)
42
62
  Namespace.root.executed_blueprints << self
63
+
43
64
  @building = false
44
65
  result
45
66
  end
46
67
 
47
- # Returns the result of blueprint
48
- def result
49
- Namespace.root.context.instance_variable_get(variable_name)
50
- end
51
-
52
- # Sets the result of blueprint
53
- def result=(value)
54
- Namespace.root.add_variable(variable_name, value)
55
- end
56
-
57
68
  # Returns if blueprint has been built
69
+ # @return [true, false] true if was built, false otherwise
58
70
  def built?
59
71
  Namespace.root.executed_blueprints.include?(self)
60
72
  end
@@ -64,40 +76,31 @@ module Blueprints
64
76
  Namespace.root.executed_blueprints.delete self
65
77
  end
66
78
 
67
- # If value is passed then it sets attributes for this buildable object.
68
- # Otherwise returns attributes (defaulting to empty Hash)
69
- def attributes(value)
70
- @attributes = value
71
- self
72
- end
73
-
74
- # Returns normalized attributes for that particular blueprint.
75
- def normalized_attributes
76
- Buildable.normalize_attributes(@attributes ||= {})
79
+ # Returns full path to this buildable
80
+ # @param [String] join_with Separator used to join names of parent namespaces and buildable itself.
81
+ # @return [String] full path to this buildable joined with separator
82
+ def path(join_with = '_')
83
+ (namespace.path(join_with) + join_with unless namespace.nil? or namespace.path.empty?).to_s + @name.to_s
77
84
  end
78
85
 
79
- # Normalizes attributes by changing all :@var to values of @var, and all dependencies to the result of that blueprint.
80
- def self.normalize_attributes(attributes)
81
- attributes = attributes.dup
82
- attributes.each do |attr, value|
83
- attributes[attr] = value.blueprint_value if value.respond_to?(:blueprint_value)
84
- if value.is_a? Symbol and value.to_s =~ /^@.+$/
85
- STDERR.puts "DEPRECATION WARNING: :@variables are deprecated in favor of `d` method"
86
- attributes[attr] = Blueprints::Namespace.root.context.instance_variable_get(value)
87
- end
88
- end
89
- attributes
86
+ # Returns full name for this buildable
87
+ # @return [String] full buildable name
88
+ def full_name
89
+ path('.')
90
90
  end
91
91
 
92
- def build_parents
93
- @parents.each do |p|
92
+ # Builds all dependencies. Should be called before building itself. Searches dependencies first in parent then in root namespace.
93
+ # @param [Blueprints::EvalContext] eval_context Context to build parents against.
94
+ # @raise [Blueprints::BlueprintNotFoundError] If one of dependencies can't be found.
95
+ def build_parents(eval_context)
96
+ @context.dependencies.each do |name|
94
97
  parent = begin
95
- namespace[p]
98
+ namespace[name]
96
99
  rescue BlueprintNotFoundError
97
- Namespace.root[p]
100
+ Namespace.root[name]
98
101
  end
99
102
 
100
- parent.build
103
+ parent.build(eval_context)
101
104
  end
102
105
  end
103
106
 
@@ -108,23 +111,38 @@ module Blueprints
108
111
  yield(namespace) while namespace = namespace.namespace
109
112
  end
110
113
 
111
- def path
112
- @path = (namespace.path + "_" unless namespace.nil? or namespace.path.empty?).to_s + @name.to_s
113
- end
114
-
115
114
  def variable_name
116
115
  :"@#{path}"
117
116
  end
118
117
 
119
118
  def parse_name(name)
120
- case name
121
- when Hash
122
- return name.keys.first.to_sym, [name.values.first].flatten.map { |sc| parse_name(sc).first }
123
- when Symbol, String
124
- name = name.to_sym unless name == ''
125
- return name, []
126
- else
127
- raise TypeError, "Pass blueprint names as strings or symbols only, cannot define blueprint #{name.inspect}"
119
+ if name.is_a?(Hash)
120
+ return name.keys.first.to_sym, [name.values.first].flatten.map { |sc| parse_name(sc).first }
121
+ elsif name.respond_to?(:to_sym)
122
+ name = name.to_sym unless name == ''
123
+ return name, []
124
+ else
125
+ raise TypeError, "Pass blueprint names as strings or symbols only, cannot define blueprint #{name.inspect}"
126
+ end
127
+ end
128
+
129
+ def update_context(options)
130
+ @context = @context.with_context(options)
131
+ self
132
+ end
133
+
134
+ private
135
+
136
+ def result(eval_context)
137
+ if block_given?
138
+ yield.tap do |result|
139
+ if @auto_variable or not eval_context.instance_variable_defined?(variable_name)
140
+ eval_context.instance_variable_set(variable_name, result)
141
+ @auto_variable = true
142
+ end
143
+ end
144
+ else
145
+ eval_context.instance_variable_get(variable_name)
128
146
  end
129
147
  end
130
148
  end