JamieFlournoy-machinist 1.0.6

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.
@@ -0,0 +1,200 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ require 'machinist/active_record'
3
+ require 'active_support/whiny_nil'
4
+
5
+ module MachinistActiveRecordSpecs
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 Machinist, "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(&:clear_blueprints!)
32
+ end
33
+
34
+ describe "make method" do
35
+ it "should support single-table inheritance" do
36
+ Person.blueprint { }
37
+ Admin.blueprint { }
38
+ admin = Admin.make
39
+ admin.should_not be_new_record
40
+ admin.type.should == "Admin"
41
+ end
42
+
43
+ it "should save the constructed object" do
44
+ Person.blueprint { }
45
+ person = Person.make
46
+ person.should_not be_new_record
47
+ end
48
+
49
+ it "should create an object through belongs_to association" do
50
+ Post.blueprint { }
51
+ Comment.blueprint { post }
52
+ Comment.make.post.class.should == Post
53
+ end
54
+
55
+ it "should create an object through belongs_to association with a class_name attribute" do
56
+ Person.blueprint { }
57
+ Comment.blueprint { author }
58
+ Comment.make.author.class.should == Person
59
+ end
60
+
61
+ it "should create an object through belongs_to association using a named blueprint" do
62
+ Post.blueprint { }
63
+ Post.blueprint(:dummy) do
64
+ title { 'Dummy Post' }
65
+ end
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 do
94
+ id { 12345 }
95
+ end
96
+ Person.make.id.should == 12345
97
+ end
98
+
99
+ it "should allow setting the type attribute in a blueprint" do
100
+ Person.blueprint do
101
+ type { "Person" }
102
+ end
103
+ Person.make.type.should == "Person"
104
+ end
105
+
106
+ describe "on a has_many association" do
107
+ before do
108
+ Post.blueprint { }
109
+ Comment.blueprint { post }
110
+ @post = Post.make
111
+ @comment = @post.comments.make
112
+ end
113
+
114
+ it "should save the created object" do
115
+ @comment.should_not be_new_record
116
+ end
117
+
118
+ it "should set the parent association on the created object" do
119
+ @comment.post.should == @post
120
+ end
121
+ end
122
+ end
123
+
124
+ describe "plan method" do
125
+ it "should not save the constructed object" do
126
+ person_count = Person.count
127
+ Person.blueprint { }
128
+ person = Person.plan
129
+ Person.count.should == person_count
130
+ end
131
+
132
+ it "should create an object through a belongs_to association, and return its id" do
133
+ Post.blueprint { }
134
+ Comment.blueprint { post }
135
+ post_count = Post.count
136
+ comment = Comment.plan
137
+ Post.count.should == post_count + 1
138
+ comment[:post].should be_nil
139
+ comment[:post_id].should_not be_nil
140
+ end
141
+
142
+ describe "on a belongs_to association" do
143
+ it "should allow explicitly setting the association to nil" do
144
+ Comment.blueprint { post }
145
+ Comment.blueprint(:no_post) { post { nil } }
146
+ lambda {
147
+ @comment = Comment.plan(:no_post)
148
+ }.should_not raise_error
149
+ end
150
+ end
151
+
152
+ describe "on a has_many association" do
153
+ before do
154
+ Post.blueprint { }
155
+ Comment.blueprint do
156
+ post
157
+ body { "Test" }
158
+ end
159
+ @post = Post.make
160
+ @post_count = Post.count
161
+ @comment = @post.comments.plan
162
+ end
163
+
164
+ it "should not include the parent in the returned hash" do
165
+ @comment[:post].should be_nil
166
+ @comment[:post_id].should be_nil
167
+ end
168
+
169
+ it "should not create an extra parent object" do
170
+ Post.count.should == @post_count
171
+ end
172
+ end
173
+ end
174
+
175
+ describe "make_unsaved method" do
176
+ it "should not save the constructed object" do
177
+ Person.blueprint { }
178
+ person = Person.make_unsaved
179
+ person.should be_new_record
180
+ end
181
+
182
+ it "should not save associated objects" do
183
+ Post.blueprint { }
184
+ Comment.blueprint { post }
185
+ comment = Comment.make_unsaved
186
+ comment.post.should be_new_record
187
+ end
188
+
189
+ it "should save objects made within a passed-in block" do
190
+ Post.blueprint { }
191
+ Comment.blueprint { }
192
+ comment = nil
193
+ post = Post.make_unsaved { comment = Comment.make }
194
+ post.should be_new_record
195
+ comment.should_not be_new_record
196
+ end
197
+ end
198
+
199
+ end
200
+ end
@@ -0,0 +1,136 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ require 'machinist/data_mapper'
3
+ require 'dm-validations'
4
+
5
+ module MachinistDataMapperSpecs
6
+
7
+ class Person
8
+ include DataMapper::Resource
9
+ property :id, Serial
10
+ property :name, String, :length => (0..10)
11
+ property :type, Discriminator
12
+ property :password, String
13
+ property :admin, Boolean, :default => false
14
+ end
15
+
16
+ class Admin < Person
17
+ end
18
+
19
+ class Post
20
+ include DataMapper::Resource
21
+ property :id, Serial
22
+ property :title, String
23
+ property :body, Text
24
+ property :published, Boolean, :default => true
25
+ has n, :comments
26
+ end
27
+
28
+ class Comment
29
+ include DataMapper::Resource
30
+ property :id, Serial
31
+ property :post_id, Integer
32
+ property :author_id, Integer
33
+ belongs_to :post
34
+ belongs_to :author, :model => "Person", :child_key => [:author_id]
35
+ end
36
+
37
+ describe Machinist, "DataMapper adapter" do
38
+ before(:suite) do
39
+ DataMapper::Logger.new(File.dirname(__FILE__) + "/log/test.log", :debug)
40
+ DataMapper.setup(:default, "sqlite3::memory:")
41
+ DataMapper.auto_migrate!
42
+ end
43
+
44
+ before(:each) do
45
+ [Person, Admin, Post, Comment].each(&:clear_blueprints!)
46
+ end
47
+
48
+ describe "make method" do
49
+ it "should support inheritance" do
50
+ Person.blueprint {}
51
+ Admin.blueprint {}
52
+
53
+ admin = Admin.make
54
+ admin.should_not be_new
55
+ admin.type.should_not be_nil
56
+ end
57
+
58
+ it "should save the constructed object" do
59
+ Person.blueprint { }
60
+ person = Person.make
61
+ person.should_not be_new
62
+ end
63
+
64
+ it "should create an object through a belongs_to association" do
65
+ Post.blueprint { }
66
+ Comment.blueprint { post }
67
+ Comment.make.post.class.should == Post
68
+ end
69
+
70
+
71
+ it "should create an object through a belongs_to association with a class_name attribute" do
72
+ Person.blueprint { }
73
+ Comment.blueprint { author }
74
+ Comment.make.author.class.should == Person
75
+ end
76
+
77
+ it "should raise an exception if the object can't be saved" do
78
+ Person.blueprint { }
79
+ lambda { Person.make(:name => "More than ten characters") }.should raise_error(RuntimeError)
80
+ end
81
+ end
82
+
83
+ describe "plan method" do
84
+ it "should not save the constructed object" do
85
+ person_count = Person.all.length
86
+ Person.blueprint { }
87
+ person = Person.plan
88
+ Person.all.length.should == person_count
89
+ end
90
+
91
+ it "should return a regular attribute in the hash" do
92
+ Post.blueprint do
93
+ title { "Test" }
94
+ end
95
+ post = Post.plan
96
+ post[:title].should == "Test"
97
+ end
98
+
99
+ it "should create an object through a belongs_to association, and return its id" do
100
+ Post.blueprint { }
101
+ Comment.blueprint { post }
102
+ post_count = Post.all.length
103
+ comment = Comment.plan
104
+ Post.all.length.should == post_count + 1
105
+ comment[:post].should be_nil
106
+ comment[:post_id].should_not be_nil
107
+ end
108
+ end
109
+
110
+ describe "make_unsaved method" do
111
+ it "should not save the constructed object" do
112
+ Person.blueprint { }
113
+ person = Person.make_unsaved
114
+ person.should be_new
115
+ end
116
+
117
+ it "should not save associated objects" do
118
+ Post.blueprint { }
119
+ Comment.blueprint { post }
120
+ comment = Comment.make_unsaved
121
+ comment.post.should be_new
122
+ end
123
+
124
+ it "should save objects made within a passed-in block" do
125
+ Post.blueprint { }
126
+ Comment.blueprint { }
127
+ comment = nil
128
+ post = Post.make_unsaved { comment = Comment.make }
129
+ post.should be_new
130
+ comment.should_not be_new
131
+ end
132
+ end
133
+
134
+ end
135
+ end
136
+
@@ -0,0 +1 @@
1
+ *.sqlite3
data/spec/db/schema.rb ADDED
@@ -0,0 +1,20 @@
1
+ ActiveRecord::Schema.define(:version => 0) do
2
+ create_table :people, :force => true do |t|
3
+ t.column :name, :string
4
+ t.column :type, :string
5
+ t.column :password, :string
6
+ t.column :admin, :boolean, :default => false
7
+ end
8
+
9
+ create_table :posts, :force => true do |t|
10
+ t.column :title, :string
11
+ t.column :body, :text
12
+ t.column :published, :boolean, :default => true
13
+ end
14
+
15
+ create_table :comments, :force => true do |t|
16
+ t.column :post_id, :integer
17
+ t.column :author_id, :integer
18
+ t.column :body, :text
19
+ end
20
+ end
@@ -0,0 +1 @@
1
+ *.log
@@ -0,0 +1,208 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ require 'machinist/object'
3
+
4
+ module MachinistSpecs
5
+
6
+ class Person
7
+ attr_accessor :name, :admin
8
+ end
9
+
10
+ class Post
11
+ attr_accessor :title, :body, :published
12
+ end
13
+
14
+ class Grandpa
15
+ attr_accessor :name
16
+ end
17
+
18
+ class Dad < Grandpa
19
+ attr_accessor :name
20
+ end
21
+
22
+ class Son < Dad
23
+ attr_accessor :name
24
+ end
25
+
26
+ describe Machinist do
27
+ before(:each) do
28
+ [Person, Post, Grandpa, Dad, Son].each(&:clear_blueprints!)
29
+ end
30
+
31
+ it "should raise for make on a class with no blueprint" do
32
+ lambda { Person.make }.should raise_error(RuntimeError, "No blueprint for class MachinistSpecs::Person")
33
+ end
34
+
35
+ it "should set an attribute on the constructed object from a constant in the blueprint" do
36
+ Person.blueprint do
37
+ name "Fred"
38
+ end
39
+ Person.make.name.should == "Fred"
40
+ end
41
+
42
+ it "should set an attribute on the constructed object from a block in the blueprint" do
43
+ Person.blueprint do
44
+ name { "Fred" }
45
+ end
46
+ Person.make.name.should == "Fred"
47
+ end
48
+
49
+ it "should default to calling Sham for an attribute in the blueprint" do
50
+ Sham.clear
51
+ Sham.name { "Fred" }
52
+ Person.blueprint { name }
53
+ Person.make.name.should == "Fred"
54
+ end
55
+
56
+ it "should let the blueprint override an attribute with a default value" do
57
+ Post.blueprint do
58
+ published { false }
59
+ end
60
+ Post.make.published.should be_false
61
+ end
62
+
63
+ it "should allow overridden attribute names to be strings" do
64
+ Person.blueprint do
65
+ name { "Fred" }
66
+ end
67
+ Person.make("name" => "Bill").name.should == "Bill"
68
+ end
69
+
70
+ it "should override an attribute from the blueprint with a passed-in attribute" do
71
+ block_called = false
72
+ Person.blueprint do
73
+ name { block_called = true; "Fred" }
74
+ end
75
+ Person.make(:name => "Bill").name.should == "Bill"
76
+ block_called.should be_false
77
+ end
78
+
79
+ it "should call a passed-in block with the object being constructed" do
80
+ Person.blueprint { }
81
+ block_called = false
82
+ Person.make do |person|
83
+ block_called = true
84
+ person.class.should == Person
85
+ end
86
+ block_called.should be_true
87
+ end
88
+
89
+ it "should provide access to the object being constructed from within the blueprint" do
90
+ person = nil
91
+ Person.blueprint { person = object }
92
+ Person.make
93
+ person.class.should == Person
94
+ end
95
+
96
+ it "should allow reading of a previously assigned attribute from within the blueprint" do
97
+ Post.blueprint do
98
+ title { "Test" }
99
+ body { title }
100
+ end
101
+ Post.make.body.should == "Test"
102
+ end
103
+
104
+ describe "named blueprints" do
105
+ before do
106
+ @block_called = false
107
+ Person.blueprint do
108
+ name { "Fred" }
109
+ admin { @block_called = true; false }
110
+ end
111
+ Person.blueprint(:admin) do
112
+ admin { true }
113
+ end
114
+ @person = Person.make(:admin)
115
+ end
116
+
117
+ it "should override an attribute from the parent blueprint in the child blueprint" do
118
+ @person.admin.should == true
119
+ end
120
+
121
+ it "should not call the block for an attribute from the parent blueprint if that attribute is overridden in the child" do
122
+ @block_called.should be_false
123
+ end
124
+
125
+ it "should set an attribute defined in the parent blueprint" do
126
+ @person.name.should == "Fred"
127
+ end
128
+
129
+ it "should return the correct list of named blueprints" do
130
+ Person.blueprint(:foo) { }
131
+ Person.blueprint(:bar) { }
132
+ Person.named_blueprints.to_set.should == [:admin, :foo, :bar].to_set
133
+ end
134
+
135
+ it "should raise exception for make if named blueprint is not defined" do
136
+ lambda { Person.make(:bogus) }.should raise_error(
137
+ RuntimeError, "No blueprint named 'bogus' defined for class MachinistSpecs::Person"
138
+ )
139
+ end
140
+
141
+ it "should raise a sensible error when calling a named blueprint with no master" do
142
+ Post.blueprint(:foo) { }
143
+ lambda { Post.make(:foo) }.should raise_error(
144
+ RuntimeError, "Can't construct an object from a named blueprint without a default blueprint for class MachinistSpecs::Post"
145
+ )
146
+ end
147
+ end
148
+
149
+ describe "blueprint inheritance" do
150
+ it "should inherit blueprinted attributes from the parent class" do
151
+ Dad.blueprint do
152
+ name { "Fred" }
153
+ end
154
+ Son.blueprint { }
155
+ Son.make.name.should == "Fred"
156
+ end
157
+
158
+ it "should override blueprinted attributes in the child class" do
159
+ Dad.blueprint do
160
+ name { "Fred" }
161
+ end
162
+ Son.blueprint do
163
+ name { "George" }
164
+ end
165
+ Dad.make.name.should == "Fred"
166
+ Son.make.name.should == "George"
167
+ end
168
+
169
+ it "should inherit from blueprinted attributes in ancestor class" do
170
+ Grandpa.blueprint do
171
+ name { "Fred" }
172
+ end
173
+ Son.blueprint { }
174
+ Grandpa.make.name.should == "Fred"
175
+ lambda { Dad.make }.should raise_error(RuntimeError)
176
+ Son.make.name.should == "Fred"
177
+ end
178
+
179
+ it "should follow inheritance for named blueprints correctly" do
180
+ Dad.blueprint do
181
+ name { "John" }
182
+ end
183
+ Dad.blueprint(:special) do
184
+ name { "Paul" }
185
+ end
186
+ Son.blueprint { }
187
+ Son.blueprint(:special) { }
188
+ Son.make(:special).name.should == "John"
189
+ end
190
+ end
191
+
192
+ describe "clear_blueprints! method" do
193
+ it "should clear the list of blueprints" do
194
+ Person.blueprint(:foo){}
195
+ Person.clear_blueprints!
196
+ Person.named_blueprints.should == []
197
+ end
198
+
199
+ it "should clear master blueprint too" do
200
+ Person.blueprint(:foo) {}
201
+ Person.blueprint {} # master
202
+ Person.clear_blueprints!
203
+ lambda { Person.make }.should raise_error(RuntimeError)
204
+ end
205
+ end
206
+
207
+ end
208
+ end