diff --git a/README.markdown b/README.markdown index 914cdd1..ca3b33d 100644 --- a/README.markdown +++ b/README.markdown @@ -244,6 +244,28 @@ You can also call plan on has\_many associations, making it easy to test nested end +### Named Blueprints + +Named blueprints let you define variations on an object. For example, suppose some of your Users are administrators: + + User.blueprint do + name + email + end + + User.blueprint(:admin) do + name { Sham.name + " (admin)" } + admin { true } + end + +Calling: + + User.make(:admin) + +will use the `:admin` blueprint. + +Named blueprints call the default blueprint to set any attributes not specifically provided, so in this example the `email` attribute will still be generated even for an admin user. + FAQ --- @@ -264,33 +286,6 @@ This will result in Machinist attempting to run ruby's open command. To work aro self.open { Time.now } end -### I'm a factory_girl user, and I like having multiple factories for a single model. Can Machinist do the same thing? - -Short answer: no. - -Machinist blueprints are a little different to factory_girl's factories. Your blueprint should only specify how to generate values for attributes that you don't care about. If you care about an attribute's value, then it doesn't belong in the blueprint. - -If you have want to construct objects with similar attributes in a number of tests, just make a test helper. For example: - - User.blueprint do - login - password - end - - def make_admin_user(attributes = {}) - User.make(attributes.merge(:role => :admin)) - end - -This keeps the blueprint very clean and generic, and also makes it clear what differentiates an admin user from a generic user. - -If you want to make this look a bit cleaner in your tests, you can try the following in your blueprint: - - class User - def self.make_admin(attributes = {}) - make(attributes.merge(:role => :admin) - end - end - Credits ------- diff --git a/lib/machinist.rb b/lib/machinist.rb index 090221e..e3677f4 100644 --- a/lib/machinist.rb +++ b/lib/machinist.rb @@ -9,10 +9,13 @@ module Machinist # # The blueprint is instance_eval'd against the Lathe. class Lathe - def self.run(object, attributes = {}) - blueprint = object.class.blueprint + def self.run(object, *args) + blueprint = object.class.blueprint + named_blueprint = object.class.blueprint(args.shift) if args.first.is_a?(Symbol) + attributes = args.pop || {} raise "No blueprint for class #{object.class}" if blueprint.nil? returning self.new(object, attributes) do |lathe| + lathe.instance_eval(&named_blueprint) if named_blueprint lathe.instance_eval(&blueprint) end end diff --git a/lib/machinist/active_record.rb b/lib/machinist/active_record.rb index 9bb4b5e..2587ac7 100644 --- a/lib/machinist/active_record.rb +++ b/lib/machinist/active_record.rb @@ -51,13 +51,14 @@ def self.included(base) end module ClassMethods - def blueprint(&blueprint) - @blueprint = blueprint if block_given? - @blueprint + def blueprint(name = :master, &blueprint) + @blueprints ||= {} + @blueprints[name] = blueprint if block_given? + @blueprints[name] end - def make(attributes = {}, &block) - lathe = Lathe.run(self.new, attributes) + def make(*args, &block) + lathe = Lathe.run(self.new, *args) unless Machinist::ActiveRecord.nerfed? lathe.object.save! lathe.object.reload @@ -65,22 +66,22 @@ def make(attributes = {}, &block) lathe.object(&block) end - def make_unsaved(attributes = {}) - returning(Machinist::ActiveRecord.with_save_nerfed { make(attributes) }) do |object| + def make_unsaved(*args) + returning(Machinist::ActiveRecord.with_save_nerfed { make(*args) }) do |object| yield object if block_given? end end - def plan(attributes = {}) - lathe = Lathe.run(self.new, attributes) + def plan(*args) + lathe = Lathe.run(self.new, *args) Machinist::ActiveRecord.assigned_attributes_without_associations(lathe) end end end module BelongsToExtensions - def make(attributes = {}, &block) - lathe = Lathe.run(self.build, attributes) + def make(*args, &block) + lathe = Lathe.run(self.build, *args) unless Machinist::ActiveRecord.nerfed? lathe.object.save! lathe.object.reload @@ -88,8 +89,8 @@ def make(attributes = {}, &block) lathe.object(&block) end - def plan(attributes = {}) - lathe = Lathe.run(self.build, attributes) + def plan(*args) + lathe = Lathe.run(self.build, *args) Machinist::ActiveRecord.assigned_attributes_without_associations(lathe) end end diff --git a/spec/db/schema.rb b/spec/db/schema.rb index 24d3b18..e449a3f 100644 --- a/spec/db/schema.rb +++ b/spec/db/schema.rb @@ -3,6 +3,7 @@ t.column :name, :string t.column :type, :string t.column :password, :string + t.column :admin, :boolean, :default => false end create_table :posts, :force => true do |t| diff --git a/spec/machinist_spec.rb b/spec/machinist_spec.rb index 90c32a3..b2a3195 100644 --- a/spec/machinist_spec.rb +++ b/spec/machinist_spec.rb @@ -26,7 +26,7 @@ class Comment < ActiveRecord::Base end Person.make.name.should == "Fred" end - + it "should set an attribute on the constructed object from a block in the blueprint" do Person.blueprint do name { "Fred" } @@ -119,6 +119,32 @@ class Comment < ActiveRecord::Base Person.blueprint { type "Person" } Person.make.type.should == "Person" end + + describe "for named blueprints" do + before do + @block_called = false + Person.blueprint do + name { "Fred" } + admin { block_called = true; false } + end + Person.blueprint(:admin) do + admin { true } + end + @person = Person.make(:admin) + end + + it "should override an attribute from the parent blueprint in the child blueprint" do + @person.admin.should == true + end + + it "should not call the block for an attribute from the parent blueprint if that attribute is overridden in the child" do + @block_called.should be_false + end + + it "should set an attribute defined in the parent blueprint" do + @person.name.should == "Fred" + end + end end # make method