Skip to content
Browse files

Added initial shop support.

  • Loading branch information...
1 parent f73711e commit 3d758eac0377b01a98586058510725ac22a3e6d2 @notahat notahat committed Jun 10, 2010
View
1 .gitignore
@@ -1 +1,2 @@
coverage
+spec/log/*
View
4 Rakefile
@@ -1,13 +1,17 @@
require 'rake'
require 'spec/rake/spectask'
+# require 'rspec/core/rake_task'
+
desc 'Run the specs.'
+# RSpec::Core::RakeTask.new
Spec::Rake::SpecTask.new(:spec) do |spec|
spec.libs << 'lib' << 'spec'
spec.spec_files = FileList['spec/**/*_spec.rb']
end
desc 'Run the specs with rcov.'
+# RSpec::Core::RakeTask.new(:rcov) do |spec|
Spec::Rake::SpecTask.new(:rcov) do |spec|
spec.libs << 'lib' << 'spec'
spec.pattern = 'spec/**/*_spec.rb'
View
34 lib/machinist/adapters/active_record.rb
@@ -0,0 +1,34 @@
+require 'active_record'
+
+module Machinist
+ module Adapters
+ module ActiveRecord
+
+ def self.serialize(klass, object)
+ object.id
+ end
+
+ def self.instantiate(klass, id)
+ klass.find(id)
+ end
+
+ def self.outside_transaction
+ thread = Thread.new do
+ begin
+ yield
+ ensure
+ ::ActiveRecord::Base.connection_pool.checkin(::ActiveRecord::Base.connection)
+ end
+ end
+ thread.value
+ end
+
+ end
+ end
+end
+
+class ActiveRecord::Base #:nodoc:
+ def self.machinist_adapter
+ Machinist::Adapters::ActiveRecord
+ end
+end
View
34 lib/machinist/adapters/object.rb
@@ -0,0 +1,34 @@
+require 'machinist/blueprints'
+
+module Machinist
+ module Adapters
+ module Object
+
+ def self.serialize(klass, object)
+ object.dup
+ end
+
+ def self.instantiate(klass, object)
+ object.dup
+ end
+
+ def self.outside_transaction
+ yield
+ end
+
+ end
+ end
+end
+
+class Object #:nodoc:
+ include Machinist::Blueprints
+
+ def self.machinist_adapter
+ Machinist::Adapters::Object
+ end
+
+ def make(attributes = {}, &block)
+ object = Machinist::Lathe.make(self, attributes)
+ block_given? ? yield(object) : object
+ end
+end
View
5 lib/machinist/blueprint.rb
@@ -1,3 +1,5 @@
+require 'ostruct'
+
module Machinist
class Blueprint
@@ -6,9 +8,12 @@ def initialize(klass = OpenStruct, &block)
@block = block
end
+ attr_reader :klass
+
def make(attributes = {})
lathe = Lathe.new(@klass.new, attributes)
lathe.instance_eval(&@block)
+ lathe.object.save! if lathe.object.respond_to?(:save!) # FIXME: This is a hack.
block_given? ? yield(lathe.object) : lathe.object
end
View
39 lib/machinist/shop.rb
@@ -0,0 +1,39 @@
+require 'machinist/warehouse'
+
+module Machinist
+ class Shop
+
+ def self.instance
+ @instance ||= Shop.new
+ end
+
+ def initialize
+ reset_warehouse
+ end
+
+ def reset_warehouse
+ @warehouse = Warehouse.new
+ reset
+ end
+
+ def reset
+ @back_room = @warehouse.clone
+ end
+
+ # TODO: This should work with sqlite too.
+ def buy(blueprint, attributes = {})
+ klass = blueprint.klass
+ adapter = klass.machinist_adapter
+
+ shelf = @back_room[blueprint, attributes]
+ if shelf.empty?
+ item = adapter.outside_transaction { blueprint.make(attributes) }
+ @warehouse[blueprint, attributes] << adapter.serialize(klass, item)
+ item
+ else
+ adapter.instantiate(klass, shelf.shift)
+ end
+ end
+
+ end
+end
View
80 spec/blueprint_spec.rb
@@ -0,0 +1,80 @@
+require File.dirname(__FILE__) + '/spec_helper'
+require 'machinist/blueprint'
+require 'machinist/lathe'
+
+module BlueprintSpecs
+ class Post
+ attr_accessor :title, :body
+ end
+end
+
+describe Machinist::Blueprint do
+
+ it "should make an OpenStruct by default" do
+ blueprint = Machinist::Blueprint.new { }
+ blueprint.make.should be_an(OpenStruct)
+ end
+
+ it "should make a provided class" do
+ blueprint = Machinist::Blueprint.new(BlueprintSpecs::Post) { }
+ blueprint.make.should be_a(BlueprintSpecs::Post)
+ end
+
+ it "should set attributes in the blueprint" do
+ blueprint = Machinist::Blueprint.new do
+ name { "Fred" }
+ end
+ blueprint.make.name.should == "Fred"
+ end
+
+ it "should allow passing in attributes to override the blueprint" do
+ block_called = false
+ blueprint = Machinist::Blueprint.new do
+ name { block_called = true; "Fred" }
+ end
+ blueprint.make(:name => "Bill").name.should == "Bill"
+ block_called.should be_false
+ end
+
+ it "should allow reading previously assigned attributes within the blueprint" do
+ blueprint = Machinist::Blueprint.new do
+ title { "Test" }
+ body { title }
+ end
+ blueprint.make.body.should == "Test"
+ end
+
+ it "should provide access to the object being constructed in the blueprint" do
+ post = nil
+ blueprint = Machinist::Blueprint.new(BlueprintSpecs::Post) { post = object }
+ blueprint.make
+ post.should be_a(BlueprintSpecs::Post)
+ end
+
+ it "should pass the constructed object to a block given to make" do
+ blueprint = Machinist::Blueprint.new(BlueprintSpecs::Post) { }
+ block_called = false
+ blueprint.make do |post|
+ block_called = true
+ post.should be_a(BlueprintSpecs::Post)
+ end
+ block_called.should be_true
+ end
+
+ it "should allow overridden attribute names to be strings" do
+ blueprint = Machinist::Blueprint.new do
+ name { "Fred" }
+ end
+ blueprint.make("name" => "Bill").name.should == "Bill"
+ end
+
+ it "should raise if you try to read an unassigned attribute" do
+ blueprint = Machinist::Blueprint.new do
+ body { title }
+ end
+ lambda {
+ blueprint.make
+ }.should raise_error("Attribute not assigned.")
+ end
+
+end
View
0 spec/log/.gitkeep
No changes.
View
82 spec/machinist_spec.rb
@@ -1,82 +0,0 @@
-require File.dirname(__FILE__) + '/spec_helper'
-require 'machinist/blueprint'
-require 'machinist/lathe'
-
-module MachinistSpecs
-
- class Post
- attr_accessor :title, :body
- end
-
- describe Machinist::Blueprint do
-
- it "should make an OpenStruct by default" do
- blueprint = Machinist::Blueprint.new { }
- blueprint.make.should be_an(OpenStruct)
- end
-
- it "should make a provided class" do
- blueprint = Machinist::Blueprint.new(Post) { }
- blueprint.make.should be_a(Post)
- end
-
- it "should set attributes in the blueprint" do
- blueprint = Machinist::Blueprint.new do
- name { "Fred" }
- end
- blueprint.make.name.should == "Fred"
- end
-
- it "should allow passing in attributes to override the blueprint" do
- block_called = false
- blueprint = Machinist::Blueprint.new do
- name { block_called = true; "Fred" }
- end
- blueprint.make(:name => "Bill").name.should == "Bill"
- block_called.should be_false
- end
-
- it "should allow reading previously assigned attributes within the blueprint" do
- blueprint = Machinist::Blueprint.new do
- title { "Test" }
- body { title }
- end
- blueprint.make.body.should == "Test"
- end
-
- it "should provide access to the object being constructed in the blueprint" do
- post = nil
- blueprint = Machinist::Blueprint.new(Post) { post = object }
- blueprint.make
- post.should be_a(Post)
- end
-
- it "should pass the constructed object to a block given to make" do
- blueprint = Machinist::Blueprint.new(Post) { }
- block_called = false
- blueprint.make do |post|
- block_called = true
- post.should be_a(Post)
- end
- block_called.should be_true
- end
-
- it "should allow overridden attribute names to be strings" do
- blueprint = Machinist::Blueprint.new do
- name { "Fred" }
- end
- blueprint.make("name" => "Bill").name.should == "Bill"
- end
-
- it "should raise if you try to read an unassigned attribute" do
- blueprint = Machinist::Blueprint.new do
- body { title }
- end
- lambda {
- blueprint.make
- }.should raise_error("Attribute not assigned.")
- end
-
- end
-
-end
View
125 spec/shop_spec.rb
@@ -0,0 +1,125 @@
+require File.dirname(__FILE__) + '/spec_helper'
+require 'machinist/blueprint'
+require 'machinist/lathe'
+require 'machinist/shop'
+require 'machinist/adapters/active_record'
+require 'logger'
+
+module ShopSpecs
+ def self.setup_db
+ # FIXME: All this logging doesn't seem to work.
+ #logger = Logger.new(File.dirname(__FILE__) + "/log/test.log")
+ logger = Logger.new(STDOUT)
+ logger.level = Logger::INFO
+ ActiveRecord::Base.logger = logger
+
+ # FIXME: Can we test against sqlite?
+ # ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
+ ActiveRecord::Base.establish_connection(
+ :adapter => "mysql",
+ :database => "machinist",
+ :host => "localhost",
+ :username => "root",
+ :password => ""
+ )
+
+ ActiveRecord::Schema.define(:version => 0) do
+ create_table :posts, :force => true do |t|
+ t.column :title, :string
+ t.column :body, :text
+ end
+ end
+ end
+
+ class Post < ActiveRecord::Base
+ end
+end
+
+describe Machinist::Shop do
+ before(:all) do
+ ShopSpecs.setup_db
+ end
+
+ before(:each) do
+ @shop = Machinist::Shop.new
+ # @shop.reset_warehouse
+ end
+
+ def fake_test
+ ActiveRecord::Base.transaction do
+ @shop.reset
+ yield
+ raise ActiveRecord::Rollback
+ end
+ end
+
+ it "should cache an object" do
+ blueprint = Machinist::Blueprint.new(ShopSpecs::Post) { }
+
+ post_a, post_b = nil, nil
+ fake_test { post_a = @shop.buy(blueprint) }
+ fake_test { post_b = @shop.buy(blueprint) }
+
+ post_b.should == post_a
+ end
+
+ it "should cache an object with attributes" do
+ blueprint = Machinist::Blueprint.new(ShopSpecs::Post) { }
+
+ post_a, post_b = nil, nil
+ fake_test { post_a = @shop.buy(blueprint, :title => "Test Title") }
+ fake_test { post_b = @shop.buy(blueprint, :title => "Test Title") }
+
+ post_b.should == post_a
+ end
+
+ it "should ensure future copies of a cached object do not reflect changes to the original" do
+ blueprint = Machinist::Blueprint.new(ShopSpecs::Post) { }
+
+ post_a, post_b = nil, nil
+ fake_test do
+ post_a = @shop.buy(blueprint, :title => "Test Title")
+ post_a.title = "Changed Title"
+ post_a.save!
+ end
+ fake_test { post_b = @shop.buy(blueprint, :title => "Test Title") }
+
+ post_b.title.should == "Test Title"
+ end
+
+ # it "should cache multiple objects with the same class and attributes" do
+ # post_a = Post.make(:title => "Test Title")
+ # post_b = Post.make(:title => "Test Title")
+ #
+ # @shop.reset
+ # post_c = Post.make(:title => "Test Title")
+ # post_c.duped_from.should == post_a.duped_from
+ # post_c.title.should == "Test Title"
+ # post_d = Post.make(:title => "Test Title")
+ # post_d.duped_from.should == post_b.duped_from
+ # post_d.title.should == "Test Title"
+ # end
+ #
+ # it "should not confuse objects with different attributes" do
+ # post_a = Post.make(:title => "Title A")
+ # post_a.should be_a(Post)
+ # post_a.title.should == "Title A"
+ #
+ # @shop.reset
+ # post_b = Post.make(:title => "Title B")
+ # post_b.duped_from.should_not == post_a.duped_from
+ # post_b.title.should == "Title B"
+ # end
+ #
+ # it "should not confuse objects of different classes" do
+ # post = Post.make(:title => "Test Title")
+ # post.should be_a(Post)
+ # post.title.should == "Test Title"
+ #
+ # @shop.reset
+ # comment = Comment.make(:author => "Test Author")
+ # comment.should be_a(Comment)
+ # comment.author.should == "Test Author"
+ # end
+
+end

0 comments on commit 3d758ea

Please sign in to comment.
Something went wrong with that request. Please try again.