module Blueprints # Extensions for active record class module ActiveRecordExtensions module ClassMethods # Two forms of this method can be used. First one is typically used inside blueprint block. Essentially it does # same as create!, except it does bypass attr_protected and attr_accessible. It accepts only a hash or attributes, # same as create! does. # blueprint :post => :user do # @user.posts.blueprint(:title => 'first post', :text => 'My first post') # end # The second form is used when you want to define new blueprint. It takes first argument as name of blueprint # and second one as hash of attributes. As you cannot use instance variables outside of blueprint block, you need # to prefix them with colon. So the example above could be rewritten like this: # Post.blueprint(:post, :title => 'first post', :text => 'My first post', :user => d(:user)) # or like this: # Post.blueprint(:post, :title => 'first post', :text => 'My first post', :user => :@user).depends_on(:user) # or like this: # Post.blueprint({:post => :user}, :title => 'first post', :text => 'My first post', :user => :@user) def blueprint(name_or_attrs, attrs = {}) if Blueprints::FileContext.evaluating klass = self blueprint = Blueprints::Blueprint.new(name_or_attrs) { klass.blueprint attributes } blueprint.depends_on(*attrs.values.select {|attr| attr.is_a?(Blueprints::Buildable::Dependency) }).attributes(attrs) blueprint else if name_or_attrs.is_a?(Array) name_or_attrs.collect { |attrs| blueprint(attrs) } else object = new object.blueprint(name_or_attrs) object end end end end module InstanceMethods # Updates attributes of object and calls save!. Bypasses attr_protected and attr_accessible. def blueprint(attributes) attributes.each do |attr, value| iv_name = value.iv_name if value.is_a?(Blueprints::Buildable::Dependency) iv_name = value if value.is_a? Symbol and value.to_s =~ /^@.+$/ attributes[attr] = Blueprints::Namespace.root.context.instance_variable_get(iv_name) if iv_name end send(:attributes=, attributes, false) save! end end end end ::ActiveRecord::Base.send(:include, Blueprints::ActiveRecordExtensions::InstanceMethods) ::ActiveRecord::Base.extend(Blueprints::ActiveRecordExtensions::ClassMethods)