Skip to content

Commit

Permalink
added ModelDefinition::StubProxy so that you can relate stubs to othe…
Browse files Browse the repository at this point in the history
…r stubs that haven't been created yet.
  • Loading branch information
technoweenie committed Aug 27, 2008
1 parent 90e93a5 commit 2d72b3d
Show file tree
Hide file tree
Showing 9 changed files with 215 additions and 49 deletions.
1 change: 1 addition & 0 deletions lib/model_stubbing.rb
Expand Up @@ -2,6 +2,7 @@
require 'model_stubbing/definition'
require 'model_stubbing/model'
require 'model_stubbing/stub'
require 'model_stubbing/stub_proxy'

module ModelStubbing
# Gets a hash of all current definitions.
Expand Down
11 changes: 1 addition & 10 deletions lib/model_stubbing/model.rb
Expand Up @@ -112,16 +112,7 @@ def connection

protected
def method_missing(model_name, stub_name, *args)
named_model = @definition.models[model_name]
if named_model.nil?
raise "No #{model_name.inspect} model found when calling #{model_name}(#{stub_name})"
end
stub = named_model.stubs[stub_name]
if stub.nil?
raise "No #{stub_name.inspect} stub found in the #{model_name.inspect} model when calling #{model_name}(#{stub_name})"
else
stub
end
StubProxy.new(@definition, model_name, stub_name)
end
end
end
82 changes: 53 additions & 29 deletions lib/model_stubbing/stub.rb
Expand Up @@ -47,7 +47,13 @@ def record(attributes = {})
ModelStubbing.records[this_record_key] = instantiate(this_record_key, attributes)
end
end


# Same as #record, but does not instantiate stubs.
def record_without_stubs
this_record_key = record_key(attributes)
instantiate(this_record_key, attributes, false)
end

def inspect
"(ModelStubbing::Stub(#{@name.inspect} => #{attributes.inspect}))"
end
Expand All @@ -69,7 +75,7 @@ def insert(attributes = {})
def with(attributes)
@attributes.inject({}) do |attr, (key, value)|
attr_value = attributes[key] || value
attr_value = attr_value.record if attr_value.is_a?(Stub)
attr_value = attr_value.record if (attr_value.is_a?(Stub) || attr_value.is_a?(StubProxy))
attr.update key => attr_value
end
end
Expand All @@ -78,7 +84,7 @@ def only(*keys)
keys = Set.new Array(keys)
@attributes.inject({}) do |attr, (key, value)|
if keys.include?(key)
attr.update key => (value.is_a?(Stub) ? value.record : value)
attr.update key => ((value.is_a?(Stub) || value.is_a?(StubProxy)) ? value.record : value)
else
attr
end
Expand All @@ -91,17 +97,22 @@ def except(*keys)
if keys.include?(key)
attr
else
attr.update key => (value.is_a?(Stub) ? value.record : value)
attr.update key => ((value.is_a?(Stub) || value.is_a?(StubProxy)) ? value.record : value)
end
end
end

def connection
@connection ||= @model.connection
end


# duck typing with StubProxy
def method_name
nil
end

private
def instantiate(this_record_key, attributes)
def instantiate(this_record_key, attributes, with_stubs = true)
case attributes[:id]
when :new
is_new_record = true
Expand All @@ -128,26 +139,35 @@ def instantiate(this_record_key, attributes)
record.stubbed_attributes = stubbed_attributes.merge(:id => record.id)
stubbed_attributes.each do |key, value|
meta.send :attr_accessor, key unless record.respond_to?("#{key}=")
if value.is_a? Stub
# set foreign key
record.send("#{stubbed_attributes.column_name_for(key)}=", value.record.id)
# set association
record.send("#{key}=", value.record)
elsif value.is_a? Array
records = value.map { |v| v.is_a?(Stub) ? v.record : v }
records.compact!

# when assigning has_many instantiated stubs, temporarily act as new
# otherwise AR inserts rows
nr, record.new_record = record.new_record?, true
record.send("#{key}=", records)
record.new_record = nr
else
duped_value = case value
when TrueClass, FalseClass, Fixnum, Float, NilClass, Symbol then value
else value.dup
end
record.send("#{key}=", duped_value)
case value
when StubProxy, Stub
if with_stubs
if value.method_name
record.send("#{key}=", value.record_without_stubs)
else
# set foreign key
record.send("#{stubbed_attributes.column_name_for(key)}=", value.record_without_stubs.id)
# set association
record.send("#{key}=", value.record)
end
end
when Array
if with_stubs
records = value.map { |v| (v.is_a?(Stub) || v.is_a?(StubProxy)) ? v.record : v }
records.compact!

# when assigning has_many instantiated stubs, temporarily act as new
# otherwise AR inserts rows
nr, record.new_record = record.new_record?, true
record.send("#{key}=", records)
record.new_record = nr
end
else
duped_value = case value
when TrueClass, FalseClass, Fixnum, Float, NilClass, Symbol then value
else value.dup
end
record.send("#{key}=", duped_value)
end
end
record
Expand Down Expand Up @@ -182,7 +202,11 @@ def value_list
list = inject([]) do |fixtures, (key, value)|
column_name = column_name_for key
column = column_for column_name
value = value.record.id if value.is_a?(Stub)
case value
when Stub then value = value.record_without_stubs.id
when StubProxy
value = value.method_name ? value.record : value.record_without_stubs.id
end
quoted = @stub.connection ? @stub.connection.quote(value, column) : %("#{value.to_s}")
fixtures << quoted.gsub('[^\]\\n', "\n").gsub('[^\]\\r', "\r")
end.join(", ")
Expand All @@ -191,12 +215,12 @@ def value_list
def column_name_for(key)
(@keys ||= {})[key] ||= begin
value = self[key]
if value.is_a? Stub
if value.is_a?(Stub) || value.is_a?(StubProxy)
if defined?(ActiveRecord)
if reflection = model_class.reflect_on_association(key)
reflection.primary_key_name
else
raise "No reflection '#{key}' found for #{model_class.name} while guessing column_name"
"#{key}_id".sub(/_id_id/, '_id')
end
else
"#{key}_id"
Expand Down
74 changes: 74 additions & 0 deletions lib/model_stubbing/stub_proxy.rb
@@ -0,0 +1,74 @@
module ModelStubbing
# Used when attaching stubs to other stubs in definitions. The stub doesn't
# actually have to exist until the Definition is being inserted into the database.
#
# model Post do
# stub :title => 'foo', :author => users(:default)
# end
#
# You can also call specific methods from the stub.
#
# model Blog do
# stub :latest_post => posts(:default), :latest_post_title => posts(:default).title
# end
#
class StubProxy
attr_reader :method_name, :proxy_definition, :proxy_model_name, :proxy_stub_name

def initialize(definition, model_name, stub_name, method_name = nil)
@proxy_definition = definition
@proxy_model_name = model_name
@proxy_stub_name = stub_name
@method_name = method_name
@model = @stub = nil
end

def record
@stub ||= fetch_stub
@method_name ? @stub.record_without_stubs.send(@method_name) : @stub.record_without_stubs
end

alias_method :record_without_stubs, :record

def ==(other)
if other.is_a?(StubProxy)
other.proxy_definition == @proxy_definition && other.proxy_model_name == @proxy_model_name && other.proxy_stub_name == @proxy_stub_name && other.method_name == @method_name
elsif !@method_name && other.is_a?(Stub)
other.model.definition == @proxy_definition && other.model.name == @proxy_model_name && @proxy_stub_name == other.name
else
super
end
end

def id
method_missing(:id)
end

def inspect
"(ModelStubbing::StubProxy[#{@proxy_model_name}(#{@proxy_stub_name.inspect})#{".#{@method_name}" if @method_name}]"
end

protected
def fetch_stub
@model = @proxy_definition.models[@proxy_model_name]
if @model.nil?
raise "No #{@proxy_model_name.inspect} model found when calling #{@proxy_model_name}(#{@proxy_stub_name.inspect})"
end
@stub = @model.stubs[@proxy_stub_name]
if @stub.nil?
raise "No #{@proxy_stub_name.inspect} stub found in the #{@proxy_model_name.inspect} model when calling #{@proxy_model_name}(#{@proxy_stub_name})"
else
@stub
end
end

def method_missing(name, *args)
if args.empty?
@method_name = name.to_sym
self
else
super
end
end
end
end
6 changes: 3 additions & 3 deletions spec/definition_spec.rb
Expand Up @@ -81,11 +81,11 @@ module ModelStubbing

it "dups each model" do
@defn.models.each do |name, model|
duped_model = @copy.models[name]
duped_model = @copy.models[name]
model.should == duped_model
model.should_not be_equal(duped_model)
model.stubs.each do |key, stub|
duped_stub = @copy.models[name].stubs[key]
duped_stub = @copy.models[name].stubs[key]
stub.should == duped_stub
stub.should_not be_equal(duped_stub)
end
Expand All @@ -94,7 +94,7 @@ module ModelStubbing

it "dups each stub" do
@defn.stubs.each do |name, stub|
duped_stub = @copy.stubs[name]
duped_stub = @copy.stubs[name]
stub.should == duped_stub
stub.should_not be_equal(duped_stub)
end
Expand Down
6 changes: 3 additions & 3 deletions spec/models.rb
Expand Up @@ -135,13 +135,13 @@ def User.table_name
end

model User do
stub :admin, :admin => true # inherits from default fixture
stub :admin, :admin => true, :edited_post => model_stubbing_posts(:default) # inherits from default fixture
end

model Post do
# uses admin user fixture above
stub :title => 'initial', :user => all_stubs(:admin_model_stubbing_user), :published_at => current_time + 5.days
stub :nice_one, :title => 'nice one', :tags => [all_stubs(:foo_model_stubbing_tag), all_stubs(:bar_model_stubbing_tag)]
stub :title => 'initial', :user => model_stubbing_users(:admin), :published_at => current_time + 5.days, :editor_id => model_stubbing_users(:admin).id
stub :nice_one, :title => 'nice one', :tags => [model_stubbing_tags(:foo), model_stubbing_tags(:bar)]
end
end

Expand Down
7 changes: 7 additions & 0 deletions spec/spec_helper.rb
Expand Up @@ -12,4 +12,11 @@
require 'spec'
end

begin
require 'ruby-debug'
Debugger.start
rescue LoadError
# no debugger
end

require File.join(File.dirname(__FILE__), 'models')
68 changes: 68 additions & 0 deletions spec/stub_proxy_spec.rb
@@ -0,0 +1,68 @@
require File.join(File.dirname(__FILE__), 'spec_helper')

module ModelStubbing
describe StubProxy do
before :all do
@definition = ModelStubbing.definitions[:default]
@model = @definition.models[:model_stubbing_tags]
@stub = @model.stubs[:foo]
end

describe "with model name and stub name" do
before :all do
@stub_proxy = StubProxy.new(@definition, @model.name, @stub.name)
end

it "has no #method_name" do
@stub_proxy.method_name.should be_nil
end

it "returns @stub.record for #record" do
@stub_proxy.record.should == @stub.record
end

it "equals the stub" do
@stub_proxy.should == @stub
end
end

describe "with model name, stub name, and method name" do
before :all do
@stub_proxy = StubProxy.new(@definition, @model.name, @stub.name)
@stub_proxy.name
end

it "has #method_name" do
@stub_proxy.method_name.should == :name
end

it "returns @stub.record.name for #record" do
@stub_proxy.record.should == @stub.record.name
end

it "does not equal the stub" do
@stub_proxy.should_not == @stub
end
end

describe "missing valid stub name" do
before :all do
@stub_proxy = StubProxy.new(@definition, @model.name, :abc)
end

it "raises exception with #record" do
lambda { @stub_proxy.record }.should raise_error
end
end

describe "missing valid model name" do
before :all do
@stub_proxy = StubProxy.new(@definition, :abc, @stub.name)
end

it "raises exception with #record" do
lambda { @stub_proxy.record }.should raise_error
end
end
end
end

0 comments on commit 2d72b3d

Please sign in to comment.