lib/blueprints/context.rb in blueprints-0.8.2 vs lib/blueprints/context.rb in blueprints-0.9.0
- old
+ new
@@ -1,11 +1,154 @@
module Blueprints
- # Class that blueprint blocks are evaluated against. Allows you to access options that were passed to build method.
+ # Class that blueprint files are evaluated against. Has methods for setting and retrieving attributes and dependencies.
+ # Allows defining new blueprints and namespaces.
class Context
- attr_accessor :options, :attributes
+ @@chain = []
- # Method that allows building one blueprint inside of another. Simply delegates to root namespace.
- def build(*names)
- Namespace.root.build(*names)
+ attr_reader :dependencies, :file
+
+ # Initializes new context with passed parent, attributes, dependencies, file and namespace.
+ # Attributes and dependencies are automatically merged with parents' attributes and dependencies.
+ # File and namespace are automatically set to parent counterparts unless they are explicitly changed.
+ # @param [Hash] options Options for new context.
+ # @option options [Hash] :attributes ({}) List of attributes, merged with parent attributes.
+ # @option options [Array<String, Symbol>] :dependencies ([]) List of dependencies, merged with parent dependencies.
+ # @option options [Pathname] :file File this context is evaluated in. Should be passed for top level contexts only.
+ # @option options [Blueprints::Namespace] :namespace Namespace that new blueprints and namespaces should be children of.
+ # @option options [Blueprints::Context] :parent Parent context that is used to retrieve unchanged values.
+ def initialize(options = {})
+ options.assert_valid_keys(:dependencies, :attributes, :file, :parent, :namespace)
+ @dependencies = (options[:dependencies] || []).collect(&:to_sym)
+ @attributes = options[:attributes] || {}
+ @file = options[:file]
+ @namespace = options[:namespace]
+
+ if parent = options[:parent]
+ @attributes.reverse_merge!(parent.attributes)
+ @dependencies = (parent.dependencies + @dependencies).uniq
+ @file ||= parent.file
+ @namespace ||= parent.namespace
+ end
+ end
+
+ # Checks if two contexts are equal by comparing attributes, dependencies, namespace and file
+ # @param [Blueprints::Context] context Context to compare this one to.
+ # @return [true, false] Whether contexts are equal or not.
+ def ==(context)
+ @dependencies == context.dependencies and @attributes == context.attributes and @file == context.file and @namespace == context.namespace
+ end
+
+ # Defines a new blueprint by name and block passed.
+ # @example Define blueprint.
+ # blueprint :user do
+ # User.blueprint :name => 'User'
+ # end
+ # @param name (see Buildable#initialize)
+ # @return [Blueprints::Blueprint] Newly defined blueprint.
+ def blueprint(name = nil, &block)
+ Blueprint.new(name, self, &block)
+ end
+
+ # @overload namespace(name, &block)
+ # Defines new namespace by name, and evaluates block against it.
+ # @example Define namespace and blueprint in it.
+ # namespace :banned do
+ # blueprint :user do
+ # User.blueprint :name => 'User'
+ # end
+ # end
+ # @param [String, Symbol] name Name of namespace.
+ # @return [Blueprints::Namespace] Newly defined namespace.
+ # @overload namespace
+ # Returns namespace for this context.
+ # @return [Blueprints::Namespace] Namespace for this context.
+ def namespace(name = nil, &block)
+ if name
+ Namespace.new(name, self).tap do |namespace|
+ with_context(:namespace => namespace, &block)
+ end
+ else
+ @namespace
+ end
+ end
+
+ # @overload attributes(new_attributes, &block)
+ # Yields and returns child context that has new attributes set.
+ # @example Define blueprint with attributes
+ # attributes(:name => 'User').blueprint(:user) do
+ # User.blueprint attributes
+ # end
+ # @example Define multiple blueprints with same attributes
+ # attributes(:name => 'User') do
+ # blueprint(:user1) do
+ # User.blueprint attributes
+ # end
+ #
+ # blueprint(:user2) do
+ # User.blueprint attributes
+ # end
+ # end
+ # @param [Hash] new_attributes Attributes for child context.
+ # @return [Blueprints::Context] Child context
+ # @overload attributes
+ # Returns attributes of context.
+ # @return [Hash] Attributes of context.
+ def attributes(new_attributes = nil, &block)
+ if new_attributes
+ with_context(:attributes => new_attributes, &block)
+ else
+ @attributes
+ end
+ end
+
+ # Yields and returns child context that has dependencies set.
+ # @example Define blueprint with dependencies
+ # depends_on(:user, :admin).blueprint(:user_and_admin)
+ # @example Define multiple blueprints with same dependencies.
+ # depends_on :user, :admin do
+ # blueprint :user_and_admin
+ # blueprint :admin_and_user
+ # end
+ # @param [Array<Symbol, String>] new_dependencies Dependencies for child context.
+ # @return [Blueprints::Context] Child context
+ def depends_on(*new_dependencies, &block)
+ with_context(:dependencies => new_dependencies, &block)
+ end
+
+ # Yields and returns child context that has new options set.
+ # @param options (see Context#initialize)
+ # @return [Blueprints::Context] Child context
+ def with_context(options, &block)
+ Context.eval_within_context(options.merge(:parent => self), &block)
+ end
+
+ # Initializes new Blueprints::Dependency object.
+ # @overload d(name, options = {})
+ # @param name (see Dependency#initialize)
+ # @param options (see Dependency#initialize)
+ # @overload d(name, instance_variable_name, options = {})
+ # @param name (see Dependency#initialize)
+ # @param instance_variable_name (see Dependency#initialize)
+ # @param options (see Dependency#initialize)
+ def d(*args)
+ Dependency.new(*args)
+ end
+
+ # Return current context.
+ # @return [Blueprints::Context] Current context.
+ def self.current
+ @@chain.last
+ end
+
+ # Creates child context and sets it as current. Evaluates block and file within child context if any are passed.
+ # @param [Hash] new_options Options for child context.
+ def self.eval_within_context(new_options, &block)
+ @@chain << context = new(new_options)
+
+ file = new_options[:file]
+ context.instance_eval(File.read(file), file) if file
+ context.instance_eval(&block) if block
+
+ @@chain.pop
end
end
end