manufactory 0.0.1 → 0.0.2
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.
- data/LICENSE +3 -0
- data/README.textile +287 -2
- data/lib/manufactory.rb +29 -90
- data/lib/manufactory/adapters/activerecord.rb +90 -0
- data/lib/manufactory/adapters/datamapper.rb +89 -0
- data/lib/manufactory/adapters/generic_model.rb +38 -0
- data/lib/manufactory/adapters/object.rb +2 -29
- data/lib/manufactory/adapters/sequel.rb +62 -0
- data/lib/manufactory/dsl.rb +89 -0
- data/spec/manufactory/adapters/activerecord_spec.rb +196 -0
- data/spec/manufactory/adapters/datamapper_spec.rb +134 -0
- data/spec/manufactory/adapters/generic_model_spec.rb +0 -0
- data/spec/manufactory/adapters/log/test.log +437 -0
- data/spec/manufactory/adapters/object_spec.rb +11 -0
- data/spec/manufactory/adapters/sequel_spec.rb +146 -0
- data/spec/manufactory/dsl_spec.rb +0 -0
- data/spec/manufactory_spec.rb +97 -2
- data/spec/spec_helper.rb +1 -1
- data/spec/stubs/db/schema.rb +20 -0
- metadata +14 -2
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'manufactory'
|
2
|
+
require 'manufactory/blueprints'
|
3
|
+
require 'active_record'
|
4
|
+
|
5
|
+
module Manufactory
|
6
|
+
class ActiveRecordAdapter
|
7
|
+
def self.has_association?(object, attribute)
|
8
|
+
object.class.reflect_on_association(attribute)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.class_for_association(object, attribute)
|
12
|
+
association = object.class.reflect_on_association(attribute)
|
13
|
+
association && association.klass
|
14
|
+
end
|
15
|
+
|
16
|
+
# This method takes care of converting any associated objects,
|
17
|
+
# in the hash returned by Lathe#assigned_attributes, into their
|
18
|
+
# object ids.
|
19
|
+
#
|
20
|
+
# For example, let's say we have blueprints like this:
|
21
|
+
#
|
22
|
+
# Post.blueprint { }
|
23
|
+
# Comment.blueprint { post }
|
24
|
+
#
|
25
|
+
# Lathe#assigned_attributes will return { :post => ... }, but
|
26
|
+
# we want to pass { :post_id => 1 } to a controller.
|
27
|
+
#
|
28
|
+
# This method takes care of cleaning this up.
|
29
|
+
def self.assigned_attributes_without_associations(lathe)
|
30
|
+
attributes = {}
|
31
|
+
lathe.assigned_attributes.each_pair do |attribute, value|
|
32
|
+
association = lathe.object.class.reflect_on_association(attribute)
|
33
|
+
if association && association.macro == :belongs_to && !value.nil?
|
34
|
+
attributes[association.primary_key_name.to_sym] = value.id
|
35
|
+
else
|
36
|
+
attributes[attribute] = value
|
37
|
+
end
|
38
|
+
end
|
39
|
+
attributes
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
module ActiveRecordExtensions
|
44
|
+
def make(*args, &block)
|
45
|
+
lathe = Lathe.run(Manufactory::ActiveRecordAdapter, self.new, *args)
|
46
|
+
unless Manufactory.nerfed?
|
47
|
+
lathe.object.save!
|
48
|
+
lathe.object.reload
|
49
|
+
end
|
50
|
+
lathe.object(&block)
|
51
|
+
end
|
52
|
+
|
53
|
+
def make_unsaved(*args)
|
54
|
+
object = Manufactory.with_save_nerfed { make(*args) }
|
55
|
+
yield object if block_given?
|
56
|
+
object
|
57
|
+
end
|
58
|
+
|
59
|
+
def plan(*args)
|
60
|
+
lathe = Lathe.run(Manufactory::ActiveRecordAdapter, self.new, *args)
|
61
|
+
Manufactory::ActiveRecordAdapter.assigned_attributes_without_associations(lathe)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
module ActiveRecordHasManyExtensions
|
66
|
+
def make(*args, &block)
|
67
|
+
lathe = Lathe.run(Manufactory::ActiveRecordAdapter, self.build, *args)
|
68
|
+
unless Manufactory.nerfed?
|
69
|
+
lathe.object.save!
|
70
|
+
lathe.object.reload
|
71
|
+
end
|
72
|
+
lathe.object(&block)
|
73
|
+
end
|
74
|
+
|
75
|
+
def plan(*args)
|
76
|
+
lathe = Lathe.run(Manufactory::ActiveRecordAdapter, self.build, *args)
|
77
|
+
Manufactory::ActiveRecordAdapter.assigned_attributes_without_associations(lathe)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
class ActiveRecord::Base
|
84
|
+
extend Manufactory::Blueprints
|
85
|
+
extend Manufactory::ActiveRecordExtensions
|
86
|
+
end
|
87
|
+
|
88
|
+
class ActiveRecord::Associations::HasManyAssociation
|
89
|
+
include Manufactory::ActiveRecordHasManyExtensions
|
90
|
+
end
|
@@ -1,3 +1,92 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
require "manufactory"
|
4
|
+
require "dm-core"
|
5
|
+
require "dm-validations"
|
6
|
+
|
7
|
+
module Manufactory
|
8
|
+
class DataMapperAdapter < GenericModelAdapter
|
9
|
+
def has_association?(object, attribute)
|
10
|
+
object.class.relationships.has_key?(attribute)
|
11
|
+
end
|
12
|
+
|
13
|
+
def class_for_association(object, attribute)
|
14
|
+
association = object.class.relationships[attribute]
|
15
|
+
association && association.parent_model
|
16
|
+
end
|
17
|
+
|
18
|
+
def association_is_many_to_one?(association)
|
19
|
+
if defined?(DataMapper::Associations::ManyToOne::Relationship)
|
20
|
+
# We're using the next branch of DM
|
21
|
+
association.class == DataMapper::Associations::ManyToOne::Relationship
|
22
|
+
else
|
23
|
+
# We're using the 0.9 or less branch.
|
24
|
+
association.options[:max].nil?
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# This method takes care of converting any associated objects,
|
29
|
+
# in the hash returned by Lathe#assigned_attributes, into their
|
30
|
+
# object ids.
|
31
|
+
#
|
32
|
+
# For example, let's say we have blueprints like this:
|
33
|
+
#
|
34
|
+
# Post.blueprint { }
|
35
|
+
# Comment.blueprint { post }
|
36
|
+
#
|
37
|
+
# Lathe#assigned_attributes will return { :post => ... }, but
|
38
|
+
# we want to pass { :post_id => 1 } to a controller.
|
39
|
+
#
|
40
|
+
# This method takes care of cleaning this up.
|
41
|
+
def assigned_attributes_without_associations(dsl)
|
42
|
+
attributes = {}
|
43
|
+
dsl.assigned_attributes.each_pair do |attribute, value|
|
44
|
+
association = dsl.object.class.relationships[attribute]
|
45
|
+
if association && association_is_many_to_one?(association)
|
46
|
+
# DataMapper child_key can have more than one property, but I'm not
|
47
|
+
# sure in what circumstances this would be the case. I'm assuming
|
48
|
+
# here that there's only one property.
|
49
|
+
key = association.child_key.map(&:field).first.to_sym
|
50
|
+
attributes[key] = value.id
|
51
|
+
else
|
52
|
+
attributes[attribute] = value
|
53
|
+
end
|
54
|
+
end
|
55
|
+
attributes
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
module DataMapperExtensions
|
60
|
+
include ManufactoryMixin
|
61
|
+
# Post.make
|
62
|
+
# Post.make(:named)
|
63
|
+
# Post.make(first_name: "Jakub")
|
64
|
+
# Post.make(named, first_name: "Jakub")
|
65
|
+
# Post.make(named, first_name: "Jakub") do
|
66
|
+
# self.whatever = whatever
|
67
|
+
# end
|
68
|
+
def make(name = :default, attributes = Hash.new, &block)
|
69
|
+
instance = super(DataMapperAdapter, name, attributes, &block)
|
70
|
+
unless Manufactory.nerfed?
|
71
|
+
instance.save || raise("Save failed: #{instance.errors.full_messages.join(", ")}")
|
72
|
+
instance.reload
|
73
|
+
end
|
74
|
+
return instance
|
75
|
+
end
|
76
|
+
|
77
|
+
def make_unsaved(*args)
|
78
|
+
object = Manufactory.with_save_nerfed { make(*args) }
|
79
|
+
yield object if block_given?
|
80
|
+
object
|
81
|
+
end
|
82
|
+
|
83
|
+
def plan(*args)
|
84
|
+
adapter = Manufactory::DataMapperAdapter.new(self, *args)
|
85
|
+
dsl = DSL.new(adapter, object, blueprint).run
|
86
|
+
Manufactory::DataMapperAdapter.assigned_attributes_without_associations(dsl)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
DataMapper::Validate::ClassMethods.send(:include, Manufactory::DataMapperExtensions)
|
92
|
+
# DataMapper::Model.append_extensions(Manufactory::DataMapperExtensions)
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# This is adapter for generic models,
|
4
|
+
# so it call ModelClass.new(attributes) rather than
|
5
|
+
# instance = ModelClass.new, attributes.each { |attribute, value| instance.send("#{attribute}="), value }
|
6
|
+
# like it is in object adapter
|
7
|
+
|
8
|
+
# Since this interface is generic, you have to register it before usage like:
|
9
|
+
# HashStruct.extend(Manufactory::GenericObjectMixin)
|
10
|
+
|
11
|
+
require "manufactory"
|
12
|
+
|
13
|
+
module Manufactory
|
14
|
+
module GenericModelMixin
|
15
|
+
include ManufactoryMixin
|
16
|
+
# Post.make
|
17
|
+
# Post.make(:named)
|
18
|
+
# Post.make(first_name: "Jakub")
|
19
|
+
# Post.make(named, first_name: "Jakub")
|
20
|
+
# Post.make(named, first_name: "Jakub") do
|
21
|
+
# self.whatever = whatever
|
22
|
+
# end
|
23
|
+
def make(name = :default, attributes = Hash.new, &block)
|
24
|
+
super(GenericModelAdapter, name, attributes, &block)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# ObjectAdapter.new(Object.new, *args)
|
29
|
+
class GenericModelAdapter < Adapter
|
30
|
+
def initialize_object(klass, default_attributes, attributes)
|
31
|
+
klass.new(default_attributes.merge(attributes))
|
32
|
+
end
|
33
|
+
|
34
|
+
def has_association?(object, attribute) # tohle nechceme zdedit!
|
35
|
+
false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -4,43 +4,16 @@ require "manufactory"
|
|
4
4
|
|
5
5
|
module Manufactory
|
6
6
|
module ObjectMixin
|
7
|
-
|
8
|
-
# Post.make(:named)
|
9
|
-
# Post.make(first_name: "Jakub")
|
10
|
-
# Post.make(named, first_name: "Jakub")
|
11
|
-
# Post.make(named, first_name: "Jakub") do
|
12
|
-
# self.whatever = whatever
|
13
|
-
# end
|
7
|
+
include ManufactoryMixin
|
14
8
|
def make(name = :default, attributes = Hash.new, &block)
|
15
|
-
name, attributes
|
16
|
-
callables = self.blueprints[name]
|
17
|
-
adapter = ObjectAdapter.new(self, name, callables)
|
18
|
-
instance = adapter.run(attributes)
|
19
|
-
instance.instance_eval(&block) if block_given?
|
20
|
-
return instance
|
9
|
+
super(ObjectAdapter, name, attributes, &block)
|
21
10
|
end
|
22
11
|
end
|
23
12
|
|
24
|
-
# ObjectAdapter.new(Object.new, *args)
|
25
|
-
# NOTE: first argument is an object, not a class!
|
26
13
|
class ObjectAdapter < Adapter
|
27
|
-
def has_association?(object, attribute)
|
28
|
-
false
|
29
|
-
end
|
30
|
-
|
31
|
-
protected
|
32
|
-
def initialize_object(klass, default_attributes, attributes)
|
33
|
-
attributes = default_attributes.merge(attributes)
|
34
|
-
instance = klass.new
|
35
|
-
attributes.each do |attribute, value|
|
36
|
-
instance.send("#{attribute}=", value)
|
37
|
-
end
|
38
|
-
return instance
|
39
|
-
end
|
40
14
|
end
|
41
15
|
end
|
42
16
|
|
43
17
|
class Object
|
44
|
-
extend Manufactory::Blueprints
|
45
18
|
extend Manufactory::ObjectMixin
|
46
19
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'manufactory'
|
2
|
+
require 'manufactory/blueprints'
|
3
|
+
require 'sequel'
|
4
|
+
|
5
|
+
module Manufactory
|
6
|
+
class SequelAdapter
|
7
|
+
def self.has_association?(object, attribute)
|
8
|
+
object.class.associations.include?(attribute)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.class_for_association(object, attribute)
|
12
|
+
object.class.association_reflection(attribute).associated_class
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.assigned_attributes_without_associations(lathe)
|
16
|
+
attributes = {}
|
17
|
+
lathe.assigned_attributes.each_pair do |attribute, value|
|
18
|
+
association = lathe.object.class.association_reflection(attribute)
|
19
|
+
if association && association[:type] == :many_to_one
|
20
|
+
key = association[:key] || association.default_key
|
21
|
+
attributes[key] = value.send(association.primary_key)
|
22
|
+
else
|
23
|
+
attributes[attribute] = value
|
24
|
+
end
|
25
|
+
end
|
26
|
+
attributes
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module SequelExtensions
|
31
|
+
def self.included(base)
|
32
|
+
base.extend(ClassMethods)
|
33
|
+
end
|
34
|
+
|
35
|
+
module ClassMethods
|
36
|
+
def make(*args, &block)
|
37
|
+
lathe = Lathe.run(Manufactory::SequelAdapter, self.new, *args)
|
38
|
+
unless Manufactory.nerfed?
|
39
|
+
lathe.object.save
|
40
|
+
lathe.object.refresh
|
41
|
+
end
|
42
|
+
lathe.object(&block)
|
43
|
+
end
|
44
|
+
|
45
|
+
def make_unsaved(*args)
|
46
|
+
returning(Manufactory.with_save_nerfed { make(*args) }) do |object|
|
47
|
+
yield object if block_given?
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def plan(*args)
|
52
|
+
lathe = Lathe.run(Manufactory::SequelAdapter, self.new, *args)
|
53
|
+
Manufactory::SequelAdapter.assigned_attributes_without_associations(lathe)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class Sequel::Model
|
60
|
+
include Manufactory::Blueprints
|
61
|
+
include Manufactory::SequelExtensions
|
62
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Manufactory
|
4
|
+
# DSL is used to execute the blueprint and construct an object.
|
5
|
+
# The blueprint is instance_eval'd against the Lathe.
|
6
|
+
class DSL
|
7
|
+
attr_reader :adapter, :object, :blueprint
|
8
|
+
def initialize(adapter, object, blueprint)
|
9
|
+
@adapter = adapter
|
10
|
+
@blueprint = blueprint
|
11
|
+
@object = object
|
12
|
+
end
|
13
|
+
|
14
|
+
def run
|
15
|
+
self.instance_eval(&self.blueprint)
|
16
|
+
assigned_attributes
|
17
|
+
end
|
18
|
+
|
19
|
+
def object
|
20
|
+
yield @object if block_given?
|
21
|
+
@object
|
22
|
+
end
|
23
|
+
|
24
|
+
def method_missing(symbol, *args, &block)
|
25
|
+
if attribute_assigned?(symbol)
|
26
|
+
# If we've already assigned the attribute, return that.
|
27
|
+
self.object.send(symbol)
|
28
|
+
elsif @adapter.has_association?(self.object, symbol) && !nil_or_empty?(self.object.send(symbol))
|
29
|
+
# If the attribute is an association and is already assigned, return that.
|
30
|
+
self.object.send(symbol)
|
31
|
+
else
|
32
|
+
# Otherwise generate a value and assign it.
|
33
|
+
assigned_attributes[symbol] = generate_attribute_value(symbol, *args, &block)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def assigned_attributes
|
38
|
+
@assigned_attributes ||= {}
|
39
|
+
end
|
40
|
+
|
41
|
+
# Undef a couple of methods that are common ActiveRecord attributes.
|
42
|
+
# (Both of these are deprecated in Ruby 1.8 anyway.)
|
43
|
+
undef_method :id if respond_to?(:id)
|
44
|
+
undef_method :type if respond_to?(:type)
|
45
|
+
|
46
|
+
private
|
47
|
+
def nil_or_empty?(object)
|
48
|
+
object.respond_to?(:empty?) ? object.empty? : object.nil?
|
49
|
+
end
|
50
|
+
|
51
|
+
def attribute_assigned?(key)
|
52
|
+
assigned_attributes.has_key?(key.to_sym)
|
53
|
+
end
|
54
|
+
|
55
|
+
def generate_attribute_value(attribute, *args, &block)
|
56
|
+
if block_given?
|
57
|
+
# If we've got a block, use that to generate the value.
|
58
|
+
yield
|
59
|
+
else
|
60
|
+
# Otherwise, look for an association or a sham.
|
61
|
+
if @adapter.has_association?(object, attribute)
|
62
|
+
@adapter.class_for_association(object, attribute).make(args.first || {})
|
63
|
+
elsif args.empty?
|
64
|
+
Sham.send(attribute)
|
65
|
+
else
|
66
|
+
# If we've got a constant, just use that.
|
67
|
+
args.first
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# This sets a flag that stops make from saving objects, so
|
74
|
+
# that calls to make from within a blueprint don't create
|
75
|
+
# anything inside make_unsaved.
|
76
|
+
def self.with_save_nerfed
|
77
|
+
begin
|
78
|
+
@@nerfed = true
|
79
|
+
yield
|
80
|
+
ensure
|
81
|
+
@@nerfed = false
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
@@nerfed = false
|
86
|
+
def self.nerfed?
|
87
|
+
@@nerfed
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,196 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
require 'manufactory/active_record'
|
3
|
+
require 'active_support/whiny_nil'
|
4
|
+
|
5
|
+
module ManufactoryActiveRecordSpecs
|
6
|
+
|
7
|
+
class Person < ActiveRecord::Base
|
8
|
+
attr_protected :password
|
9
|
+
end
|
10
|
+
|
11
|
+
class Admin < Person
|
12
|
+
end
|
13
|
+
|
14
|
+
class Post < ActiveRecord::Base
|
15
|
+
has_many :comments
|
16
|
+
end
|
17
|
+
|
18
|
+
class Comment < ActiveRecord::Base
|
19
|
+
belongs_to :post
|
20
|
+
belongs_to :author, :class_name => "Person"
|
21
|
+
end
|
22
|
+
|
23
|
+
describe Manufactory, "ActiveRecord adapter" do
|
24
|
+
before(:suite) do
|
25
|
+
ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/log/test.log")
|
26
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
|
27
|
+
load(File.dirname(__FILE__) + "/db/schema.rb")
|
28
|
+
end
|
29
|
+
|
30
|
+
before(:each) do
|
31
|
+
[Person, Admin, Post, Comment].each do |model|
|
32
|
+
model.blueprints.clear
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "make method" do
|
37
|
+
it "should support single-table inheritance" do
|
38
|
+
Person.blueprint { }
|
39
|
+
Admin.blueprint { }
|
40
|
+
admin = Admin.make
|
41
|
+
admin.should_not be_new_record
|
42
|
+
admin.type.should == "Admin"
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should save the constructed object" do
|
46
|
+
Person.blueprint { }
|
47
|
+
person = Person.make
|
48
|
+
person.should_not be_new_record
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should create an object through belongs_to association" do
|
52
|
+
Post.blueprint { }
|
53
|
+
Comment.blueprint { post }
|
54
|
+
Comment.make.post.class.should == Post
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should create an object through belongs_to association with a class_name attribute" do
|
58
|
+
Person.blueprint { }
|
59
|
+
Comment.blueprint { author }
|
60
|
+
Comment.make.author.class.should == Person
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should create an object through belongs_to association using a named blueprint" do
|
64
|
+
Post.blueprint { }
|
65
|
+
Post.blueprint(:dummy) { title 'Dummy Post' }
|
66
|
+
Comment.blueprint { post(:dummy) }
|
67
|
+
Comment.make.post.title.should == 'Dummy Post'
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should allow creating an object through a has_many association" do
|
71
|
+
Post.blueprint do
|
72
|
+
comments { [Comment.make] }
|
73
|
+
end
|
74
|
+
Comment.blueprint { }
|
75
|
+
Post.make.comments.should have(1).instance_of(Comment)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should allow setting a protected attribute in the blueprint" do
|
79
|
+
Person.blueprint do
|
80
|
+
password "Test"
|
81
|
+
end
|
82
|
+
Person.make.password.should == "Test"
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should allow overriding a protected attribute" do
|
86
|
+
Person.blueprint do
|
87
|
+
password "Test"
|
88
|
+
end
|
89
|
+
Person.make(:password => "New").password.should == "New"
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should allow setting the id attribute in a blueprint" do
|
93
|
+
Person.blueprint { id 12345 }
|
94
|
+
Person.make.id.should == 12345
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should allow setting the type attribute in a blueprint" do
|
98
|
+
Person.blueprint { type "Person" }
|
99
|
+
Person.make.type.should == "Person"
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "on a has_many association" do
|
103
|
+
before do
|
104
|
+
Post.blueprint { }
|
105
|
+
Comment.blueprint { post }
|
106
|
+
@post = Post.make
|
107
|
+
@comment = @post.comments.make
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should save the created object" do
|
111
|
+
@comment.should_not be_new_record
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should set the parent association on the created object" do
|
115
|
+
@comment.post.should == @post
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe "plan method" do
|
121
|
+
it "should not save the constructed object" do
|
122
|
+
person_count = Person.count
|
123
|
+
Person.blueprint { }
|
124
|
+
person = Person.plan
|
125
|
+
Person.count.should == person_count
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should create an object through a belongs_to association, and return its id" do
|
129
|
+
Post.blueprint { }
|
130
|
+
Comment.blueprint { post }
|
131
|
+
post_count = Post.count
|
132
|
+
comment = Comment.plan
|
133
|
+
Post.count.should == post_count + 1
|
134
|
+
comment[:post].should be_nil
|
135
|
+
comment[:post_id].should_not be_nil
|
136
|
+
end
|
137
|
+
|
138
|
+
describe "on a belongs_to association" do
|
139
|
+
it "should allow explicitly setting the association to nil" do
|
140
|
+
Comment.blueprint { post }
|
141
|
+
Comment.blueprint(:no_post) { post { nil } }
|
142
|
+
lambda {
|
143
|
+
@comment = Comment.plan(:no_post)
|
144
|
+
}.should_not raise_error
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
describe "on a has_many association" do
|
149
|
+
before do
|
150
|
+
Post.blueprint { }
|
151
|
+
Comment.blueprint do
|
152
|
+
post
|
153
|
+
body { "Test" }
|
154
|
+
end
|
155
|
+
@post = Post.make
|
156
|
+
@post_count = Post.count
|
157
|
+
@comment = @post.comments.plan
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should not include the parent in the returned hash" do
|
161
|
+
@comment[:post].should be_nil
|
162
|
+
@comment[:post_id].should be_nil
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should not create an extra parent object" do
|
166
|
+
Post.count.should == @post_count
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
describe "make_unsaved method" do
|
172
|
+
it "should not save the constructed object" do
|
173
|
+
Person.blueprint { }
|
174
|
+
person = Person.make_unsaved
|
175
|
+
person.should be_new_record
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should not save associated objects" do
|
179
|
+
Post.blueprint { }
|
180
|
+
Comment.blueprint { post }
|
181
|
+
comment = Comment.make_unsaved
|
182
|
+
comment.post.should be_new_record
|
183
|
+
end
|
184
|
+
|
185
|
+
it "should save objects made within a passed-in block" do
|
186
|
+
Post.blueprint { }
|
187
|
+
Comment.blueprint { }
|
188
|
+
comment = nil
|
189
|
+
post = Post.make_unsaved { comment = Comment.make }
|
190
|
+
post.should be_new_record
|
191
|
+
comment.should_not be_new_record
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
end
|
196
|
+
end
|