machinist 1.0.6 → 2.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.gitignore +3 -2
  2. data/Gemfile +8 -0
  3. data/MIT-LICENSE +2 -1
  4. data/README.markdown +39 -271
  5. data/Rakefile +22 -14
  6. data/lib/generators/machinist/install/USAGE +2 -0
  7. data/lib/generators/machinist/install/install_generator.rb +48 -0
  8. data/lib/generators/machinist/install/templates/blueprints.rb +9 -0
  9. data/lib/generators/machinist/install/templates/machinist.rb.erb +10 -0
  10. data/lib/generators/machinist/model/model_generator.rb +13 -0
  11. data/lib/machinist.rb +11 -105
  12. data/lib/machinist/active_record.rb +8 -93
  13. data/lib/machinist/active_record/blueprint.rb +41 -0
  14. data/lib/machinist/active_record/lathe.rb +24 -0
  15. data/lib/machinist/blueprint.rb +89 -0
  16. data/lib/machinist/exceptions.rb +32 -0
  17. data/lib/machinist/lathe.rb +69 -0
  18. data/lib/machinist/machinable.rb +97 -0
  19. data/lib/machinist/shop.rb +52 -0
  20. data/lib/machinist/warehouse.rb +36 -0
  21. data/spec/active_record_spec.rb +100 -169
  22. data/spec/blueprint_spec.rb +74 -0
  23. data/spec/exceptions_spec.rb +20 -0
  24. data/spec/inheritance_spec.rb +104 -0
  25. data/spec/machinable_spec.rb +101 -0
  26. data/spec/shop_spec.rb +94 -0
  27. data/spec/spec_helper.rb +4 -6
  28. data/spec/support/active_record_environment.rb +65 -0
  29. data/spec/warehouse_spec.rb +24 -0
  30. metadata +52 -40
  31. data/.autotest +0 -7
  32. data/FAQ.markdown +0 -18
  33. data/VERSION +0 -1
  34. data/init.rb +0 -2
  35. data/lib/machinist/blueprints.rb +0 -25
  36. data/lib/machinist/data_mapper.rb +0 -83
  37. data/lib/machinist/object.rb +0 -30
  38. data/lib/machinist/sequel.rb +0 -62
  39. data/lib/sham.rb +0 -77
  40. data/machinist.gemspec +0 -72
  41. data/spec/data_mapper_spec.rb +0 -134
  42. data/spec/db/.gitignore +0 -1
  43. data/spec/db/schema.rb +0 -20
  44. data/spec/log/.gitignore +0 -1
  45. data/spec/machinist_spec.rb +0 -190
  46. data/spec/sequel_spec.rb +0 -146
  47. data/spec/sham_spec.rb +0 -95
@@ -0,0 +1,74 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ require 'ostruct'
3
+
4
+ describe Machinist::Blueprint do
5
+
6
+ it "should make an object of the given class" do
7
+ blueprint = Machinist::Blueprint.new(OpenStruct) { }
8
+ blueprint.make.should be_an(OpenStruct)
9
+ end
10
+
11
+ it "should construct an attribute in the blueprint" do
12
+ blueprint = Machinist::Blueprint.new(OpenStruct) do
13
+ name { "Fred" }
14
+ end
15
+ blueprint.make.name.should == "Fred"
16
+ end
17
+
18
+ it "should construct an array for an attribute in the blueprint" do
19
+ blueprint = Machinist::Blueprint.new(OpenStruct) do
20
+ things(3) { Object.new }
21
+ end
22
+ things = blueprint.make.things
23
+ things.should be_an(Array)
24
+ things.should have(3).elements
25
+ things.each {|thing| thing.should be_an(Object) }
26
+ things.uniq.should == things
27
+ end
28
+
29
+ it "should allow passing in attributes to override the blueprint" do
30
+ block_called = false
31
+ blueprint = Machinist::Blueprint.new(OpenStruct) do
32
+ name { block_called = true; "Fred" }
33
+ end
34
+ blueprint.make(:name => "Bill").name.should == "Bill"
35
+ block_called.should be_false
36
+ end
37
+
38
+ it "should provide a serial number within the blueprint" do
39
+ blueprint = Machinist::Blueprint.new(OpenStruct) do
40
+ name { "Fred #{sn}" }
41
+ end
42
+ blueprint.make.name.should == "Fred 0001"
43
+ blueprint.make.name.should == "Fred 0002"
44
+ end
45
+
46
+ it "should provide access to the object being constructed within the blueprint" do
47
+ blueprint = Machinist::Blueprint.new(OpenStruct) do
48
+ title { "Test" }
49
+ body { object.title }
50
+ end
51
+ blueprint.make.body.should == "Test"
52
+ end
53
+
54
+ it "should allow attribute names to be strings" do
55
+ blueprint = Machinist::Blueprint.new(OpenStruct) do
56
+ name { "Fred" }
57
+ end
58
+ blueprint.make("name" => "Bill").name.should == "Bill"
59
+ end
60
+
61
+ it "should work with type and id attributes" do
62
+ klass = Class.new do
63
+ attr_accessor :id, :type
64
+ end
65
+ blueprint = Machinist::Blueprint.new(klass) do
66
+ id { "custom id" }
67
+ type { "custom type" }
68
+ end
69
+ object = blueprint.make
70
+ object.id.should == "custom id"
71
+ object.type.should == "custom type"
72
+ end
73
+
74
+ end
@@ -0,0 +1,20 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Machinist, "exceptions" do
4
+
5
+ describe Machinist::BlueprintCantSaveError do
6
+ it "should present the right message" do
7
+ blueprint = Machinist::Blueprint.new(String) { }
8
+ exception = Machinist::BlueprintCantSaveError.new(blueprint)
9
+ exception.message.should == "make! is not supported by blueprints for class String"
10
+ end
11
+ end
12
+
13
+ describe Machinist::NoBlueprintError do
14
+ it "should present the right message" do
15
+ exception = Machinist::NoBlueprintError.new(String, :master)
16
+ exception.message.should == "No master blueprint defined for class String"
17
+ end
18
+ end
19
+
20
+ end
@@ -0,0 +1,104 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ require 'ostruct'
3
+
4
+ module InheritanceSpecs
5
+ class Grandpa
6
+ extend Machinist::Machinable
7
+ attr_accessor :name, :age
8
+ end
9
+
10
+ class Dad < Grandpa
11
+ extend Machinist::Machinable
12
+ attr_accessor :name, :age
13
+ end
14
+
15
+ class Son < Dad
16
+ extend Machinist::Machinable
17
+ attr_accessor :name, :age
18
+ end
19
+ end
20
+
21
+ describe Machinist::Blueprint do
22
+
23
+ describe "explicit inheritance" do
24
+ it "should inherit attributes from the parent blueprint" do
25
+ parent_blueprint = Machinist::Blueprint.new(OpenStruct) do
26
+ name { "Fred" }
27
+ age { 97 }
28
+ end
29
+
30
+ child_blueprint = Machinist::Blueprint.new(OpenStruct, :parent => parent_blueprint) do
31
+ name { "Bill" }
32
+ end
33
+
34
+ child = child_blueprint.make
35
+ child.name.should == "Bill"
36
+ child.age.should == 97
37
+ end
38
+
39
+ it "should take the serial number from the parent" do
40
+ parent_blueprint = Machinist::Blueprint.new(OpenStruct) do
41
+ parent_serial { sn }
42
+ end
43
+
44
+ child_blueprint = Machinist::Blueprint.new(OpenStruct, :parent => parent_blueprint) do
45
+ child_serial { sn }
46
+ end
47
+
48
+ parent_blueprint.make.parent_serial.should == "0001"
49
+ child_blueprint.make.child_serial.should == "0002"
50
+ parent_blueprint.make.parent_serial.should == "0003"
51
+ end
52
+ end
53
+
54
+ describe "class inheritance" do
55
+ before(:each) do
56
+ [InheritanceSpecs::Grandpa, InheritanceSpecs::Dad, InheritanceSpecs::Son].each(&:clear_blueprints!)
57
+ end
58
+
59
+ it "should inherit blueprinted attributes from the parent class" do
60
+ InheritanceSpecs::Dad.blueprint do
61
+ name { "Fred" }
62
+ end
63
+ InheritanceSpecs::Son.blueprint { }
64
+ InheritanceSpecs::Son.make.name.should == "Fred"
65
+ end
66
+
67
+ it "should override blueprinted attributes in the child class" do
68
+ InheritanceSpecs::Dad.blueprint do
69
+ name { "Fred" }
70
+ end
71
+ InheritanceSpecs::Son.blueprint do
72
+ name { "George" }
73
+ end
74
+ InheritanceSpecs::Dad.make.name.should == "Fred"
75
+ InheritanceSpecs::Son.make.name.should == "George"
76
+ end
77
+
78
+ it "should inherit from blueprinted attributes in ancestor class" do
79
+ InheritanceSpecs::Grandpa.blueprint do
80
+ name { "Fred" }
81
+ end
82
+ InheritanceSpecs::Son.blueprint { }
83
+ InheritanceSpecs::Grandpa.make.name.should == "Fred"
84
+ lambda { InheritanceSpecs::Dad.make }.should raise_error(RuntimeError)
85
+ InheritanceSpecs::Son.make.name.should == "Fred"
86
+ end
87
+
88
+ it "should follow inheritance for named blueprints correctly" do
89
+ InheritanceSpecs::Dad.blueprint do
90
+ name { "John" }
91
+ age { 56 }
92
+ end
93
+ InheritanceSpecs::Dad.blueprint(:special) do
94
+ name { "Paul" }
95
+ end
96
+ InheritanceSpecs::Son.blueprint(:special) do
97
+ age { 37 }
98
+ end
99
+ InheritanceSpecs::Son.make(:special).name.should == "John"
100
+ InheritanceSpecs::Son.make(:special).age.should == 37
101
+ end
102
+ end
103
+
104
+ end
@@ -0,0 +1,101 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ module MachinableSpecs
4
+ class Post
5
+ extend Machinist::Machinable
6
+ attr_accessor :title, :body, :comments
7
+ end
8
+
9
+ class Comment
10
+ extend Machinist::Machinable
11
+ attr_accessor :post, :title
12
+ end
13
+ end
14
+
15
+ describe Machinist::Machinable do
16
+
17
+ before(:each) do
18
+ MachinableSpecs::Post.clear_blueprints!
19
+ end
20
+
21
+ it "should make an object" do
22
+ MachinableSpecs::Post.blueprint do
23
+ title { "First Post" }
24
+ end
25
+
26
+ post = MachinableSpecs::Post.make
27
+ post.should be_a(MachinableSpecs::Post)
28
+ post.title.should == "First Post"
29
+ end
30
+
31
+ it "should make an object from a named blueprint" do
32
+ MachinableSpecs::Post.blueprint do
33
+ title { "First Post" }
34
+ body { "Woot!" }
35
+ end
36
+
37
+ MachinableSpecs::Post.blueprint(:extra) do
38
+ title { "Extra!" }
39
+ end
40
+
41
+ post = MachinableSpecs::Post.make(:extra)
42
+ post.should be_a(MachinableSpecs::Post)
43
+ post.title.should == "Extra!"
44
+ post.body.should == "Woot!"
45
+ end
46
+
47
+ it "should make an array of objects" do
48
+ MachinableSpecs::Post.blueprint do
49
+ title { "First Post" }
50
+ end
51
+
52
+ posts = MachinableSpecs::Post.make(3)
53
+ posts.should be_an(Array)
54
+ posts.should have(3).elements
55
+ posts.each do |post|
56
+ post.should be_a(MachinableSpecs::Post)
57
+ post.title.should == "First Post"
58
+ end
59
+ end
60
+
61
+ it "should make array attributes from the blueprint" do
62
+ MachinableSpecs::Comment.blueprint { }
63
+ MachinableSpecs::Post.blueprint do
64
+ comments(3) { MachinableSpecs::Comment.make }
65
+ end
66
+
67
+ post = MachinableSpecs::Post.make
68
+ post.comments.should be_a(Array)
69
+ post.comments.should have(3).elements
70
+ post.comments.each do |comment|
71
+ comment.should be_a(MachinableSpecs::Comment)
72
+ end
73
+ end
74
+
75
+ it "should fail without a blueprint" do
76
+ expect do
77
+ MachinableSpecs::Post.make
78
+ end.should raise_error(Machinist::NoBlueprintError) do |exception|
79
+ exception.klass.should == MachinableSpecs::Post
80
+ exception.name.should == :master
81
+ end
82
+
83
+ expect do
84
+ MachinableSpecs::Post.make(:some_name)
85
+ end.should raise_error(Machinist::NoBlueprintError) do |exception|
86
+ exception.klass.should == MachinableSpecs::Post
87
+ exception.name.should == :some_name
88
+ end
89
+ end
90
+
91
+ it "should fail when calling make! on an unsavable object" do
92
+ MachinableSpecs::Post.blueprint { }
93
+
94
+ expect do
95
+ MachinableSpecs::Post.make!
96
+ end.should raise_error(Machinist::BlueprintCantSaveError) do |exception|
97
+ exception.blueprint.klass.should == MachinableSpecs::Post
98
+ end
99
+ end
100
+
101
+ end
data/spec/shop_spec.rb ADDED
@@ -0,0 +1,94 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ require 'support/active_record_environment'
3
+
4
+ describe Machinist::Shop do
5
+
6
+ before(:each) do
7
+ @shop = Machinist::Shop.new
8
+ end
9
+
10
+ def fake_a_test
11
+ ActiveRecord::Base.transaction do
12
+ @shop.restock
13
+ yield
14
+ raise ActiveRecord::Rollback
15
+ end
16
+ end
17
+
18
+ it "should cache an object" do
19
+ blueprint = Machinist::ActiveRecord::Blueprint.new(Post) { }
20
+
21
+ post_a, post_b = nil, nil
22
+ fake_a_test { post_a = @shop.buy(blueprint) }
23
+ fake_a_test { post_b = @shop.buy(blueprint) }
24
+
25
+ post_b.should == post_a
26
+ post_b.should_not equal(post_a)
27
+ end
28
+
29
+ it "should cache an object with attributes" do
30
+ blueprint = Machinist::ActiveRecord::Blueprint.new(Post) { }
31
+
32
+ post_a, post_b = nil, nil
33
+ fake_a_test { post_a = @shop.buy(blueprint, :title => "Test Title") }
34
+ fake_a_test { post_b = @shop.buy(blueprint, :title => "Test Title") }
35
+
36
+ post_b.should == post_a
37
+ post_b.should_not equal(post_a)
38
+ end
39
+
40
+ it "should not confuse objects with different blueprints" do
41
+ blueprint_a = Machinist::ActiveRecord::Blueprint.new(Post) { }
42
+ blueprint_b = Machinist::ActiveRecord::Blueprint.new(Post) { }
43
+
44
+ post_a, post_b = nil, nil
45
+ fake_a_test { post_a = @shop.buy(blueprint_a) }
46
+ fake_a_test { post_b = @shop.buy(blueprint_b) }
47
+
48
+ post_b.should_not == post_a
49
+ end
50
+
51
+ it "should not confuse objects with the same blueprint but different attributes" do
52
+ blueprint = Machinist::ActiveRecord::Blueprint.new(Post) { }
53
+
54
+ post_a, post_b = nil, nil
55
+ fake_a_test { post_a = @shop.buy(blueprint, :title => "A Title") }
56
+ fake_a_test { post_b = @shop.buy(blueprint, :title => "Not A Title") }
57
+
58
+ post_b.should_not == post_a
59
+ end
60
+
61
+ it "should cache multiple similar objects" do
62
+ blueprint = Machinist::ActiveRecord::Blueprint.new(Post) { }
63
+
64
+ post_a, post_b = nil, nil
65
+ fake_a_test do
66
+ post_a = @shop.buy(blueprint, :title => "Test Title")
67
+ post_b = @shop.buy(blueprint, :title => "Test Title")
68
+ post_b.should_not == post_a
69
+ end
70
+
71
+ fake_a_test do
72
+ @shop.buy(blueprint, :title => "Test Title").should == post_a
73
+ @shop.buy(blueprint, :title => "Test Title").should == post_b
74
+ post_c = @shop.buy(blueprint, :title => "Test Title")
75
+ post_c.should_not == post_a
76
+ post_c.should_not == post_b
77
+ end
78
+ end
79
+
80
+ it "should ensure future copies of a cached object do not reflect changes to the original" do
81
+ blueprint = Machinist::ActiveRecord::Blueprint.new(Post) { }
82
+
83
+ post_a, post_b = nil, nil
84
+ fake_a_test do
85
+ post_a = @shop.buy(blueprint, :title => "Test Title")
86
+ post_a.title = "Changed Title"
87
+ post_a.save!
88
+ end
89
+ fake_a_test { post_b = @shop.buy(blueprint, :title => "Test Title") }
90
+
91
+ post_b.title.should == "Test Title"
92
+ end
93
+
94
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,9 +1,7 @@
1
1
  $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
2
+ $LOAD_PATH.unshift File.dirname(__FILE__)
3
+
2
4
  require 'rubygems'
3
5
  require 'test/unit'
4
- require 'spec'
5
- require 'sham'
6
-
7
- Spec::Runner.configure do |config|
8
- config.before(:each) { Sham.reset }
9
- end
6
+ require 'rspec'
7
+ require 'machinist'
@@ -0,0 +1,65 @@
1
+ require 'active_record'
2
+ require 'machinist/active_record'
3
+
4
+ ActiveRecord::Base.establish_connection(
5
+ :adapter => "mysql",
6
+ :database => "machinist",
7
+ :username => "root",
8
+ :password => ""
9
+ )
10
+
11
+ ActiveRecord::Schema.define(:version => 0) do
12
+ create_table :users, :force => true do |t|
13
+ t.column :username, :string
14
+ end
15
+
16
+ create_table :posts, :force => true do |t|
17
+ t.column :title, :string
18
+ t.column :author_id, :integer
19
+ t.column :body, :text
20
+ end
21
+
22
+ create_table :comments, :force => true do |t|
23
+ t.column :post_id, :integer
24
+ t.column :body, :text
25
+ end
26
+
27
+ create_table :tags, :force => true do |t|
28
+ t.column :name, :string
29
+ end
30
+
31
+ create_table :posts_tags, :id => false, :force => true do |t|
32
+ t.column :post_id, :integer
33
+ t.column :tag_id, :integer
34
+ end
35
+ end
36
+
37
+ class User < ActiveRecord::Base
38
+ validates_presence_of :username
39
+ validates_uniqueness_of :username
40
+ end
41
+
42
+ class Post < ActiveRecord::Base
43
+ has_many :comments
44
+ belongs_to :author, :class_name => "User"
45
+ has_and_belongs_to_many :tags
46
+ end
47
+
48
+ class Comment < ActiveRecord::Base
49
+ belongs_to :post
50
+ end
51
+
52
+ class Tag < ActiveRecord::Base
53
+ has_and_belongs_to_many :posts
54
+ end
55
+
56
+ module ActiveRecordEnvironment
57
+
58
+ def empty_database!
59
+ [User, Post, Comment].each do |klass|
60
+ klass.delete_all
61
+ klass.clear_blueprints!
62
+ end
63
+ end
64
+
65
+ end