diff --git a/README.rdoc b/README.rdoc index 72119f28..0537f1f4 100644 --- a/README.rdoc +++ b/README.rdoc @@ -35,7 +35,7 @@ Install pickle either as a rails plugin, or a gem # plugin script/plugin install git://github.com/ianwhite/pickle.git - + # or, plugin as submodule git submodule add git://github.com/ianwhite/pickle.git vendor/plugins/pickle @@ -48,7 +48,7 @@ It's tested against all stable branches of 2.x rails, and edge, with the latest To run the specs do: rake spec - + To run the features (rails 2.3 only ATM): rake cucumber @@ -77,9 +77,11 @@ If you want path steps and email steps then just add 'paths' and/or 'email'. Th written to features/env/paths.rb and features/step_definitions/email_steps.rb respectively. -=== Using with plain ole Active Record +=== Using with plain ole Active Record or DataMapper -If you have an AR called 'Post', with required fields 'title', and 'body', then you can now write +Pickle comes with adapters for Active Record and DataMapper. + +If you have an AR/DM called 'Post', with required fields 'title', and 'body', then you can now write steps like this Given a post exists with title: "My Post", body: "My body" @@ -98,7 +100,7 @@ you've written, you can just do stuff like ==== Machinst: require your blueprints and reset Shams -(The latest version of pickle supports {multiple blueprints}[http://github.com/notahat/machinist/commit/d6492e6927a8aa1819926e48b22377171fd20496], for +(The latest version of pickle supports {multiple blueprints}[http://github.com/notahat/machinist/commit/d6492e6927a8aa1819926e48b22377171fd20496], for earlier versions of machinist use pickle <= 0.1.10) In your features/support/env.rb add the following lines at the bottom @@ -115,6 +117,19 @@ If that doesn't solve loading issues then require your factories.rb file directl # example features/support/factory_girl.rb require File.dirname(__FILE__) + '/../../spec/factories' +=== Using with an ORM other than ActiveRecord or DataMapper + +Pickle can be used with any Modeling library provided there is an adapter written for it. + +Adapters are very simple and exist a module or class with the name "PickleAdapter" available to the class. For example + +User.const_get(:PickleAdapter) #=> should return a pickle adapter + +The Active Record and DataMapper ones can be found at +ActiveRecord::Base::PickleAdapter and DataMapper::Resource::PickleAdapter respectively. + +See how to implement one by looking at the ones provided in the pickle source in lib/pickle/adapters/* + === Configuring Pickle You can tell pickle to use another factory adapter (see Pickle::Adapter), or @@ -129,7 +144,7 @@ In: features/support/pickle.rb config.adapters = [:machinist, YourOwnAdapterClass] config.map 'me', 'myself', 'my', 'I', :to => 'user: "me"' end - + Out of the box pickle looks for machinist, then factory-girl, then finally active-record 'factories'. If you find that your steps aren't working with your factories, it's probably the case that your factory setup is not being included in your cucumber environment (see comments above regarding machinist and factory-girl). @@ -147,29 +162,29 @@ When you run script/generate pickle you get the following steps Given a user exists Given a user: "fred" exists Given the user exists - + "Given a model exists with fields", e.g. Given a user exists with name: "Fred" Given a user exists with name: "Fred", activated: false - + You can refer to other models in the fields Given a user exists And a post exists with author: the user - + Given a person: "fred" exists And a person: "ethel" exists And a fatherhood exists with parent: user "fred", child: user "ethel" - + "Given n models exist", e.g. - + Given 10 users exist - + "Given n models exist with fields", examples: Given 10 users exist with activated: false - + "Given the following models exist:", examples: Given the following users exist @@ -188,21 +203,21 @@ You can refer to other models in the fields "Then a model should exist with fields", e.g. Then a user: "fred" should exist with name: "Fred" # we can label the found user for later use - + You can use other models, booleans, numerics, and strings as fields Then a person should exist with child: person "ethel" Then a user should exist with activated: false Then a user should exist with activated: true, email: "fred@gmail.com" - + "Then n models should exist", e.g. Then 10 events should exist - + "Then n models should exist with fields", e.g. Then 2 people should exist with father: person "fred" - + "Then the following models exist". This allows the creation of multiple models using a table syntax. Using a column with the singularized name of the model creates a referenceable model. E.g. @@ -213,30 +228,30 @@ using a table syntax. Using a column with the singularized name of the model cre Then the following users exist: | user | name | activated | | Fred | Freddy | false | - + ===== Asserting associations One-to-one assocs: "Then a model should be other model's association", e.g. Then the person: "fred" should be person: "ethel"'s father - + Many-to-one assocs: "Then a model should be [in|one of] other model's association", e.g. Then the person: "ethel" should be one of person: "fred"'s children Then the comment should be in the post's comments - + ===== Asserting predicate methods "Then a model should [be|have] [a|an] predicate", e.g. - + Then the user should have a status # => user.status.should be_present Then the user should have a stale password # => user.should have_stale_password Then the car: "batmobile" should be fast # => car.should be_fast - + "Then a model should not [be|have] [a|an] predicate", e.g. Then person: "fred" should not be childless # => fred.should_not be_childless - + === Regexps for use in your own steps By default you get some regexps available in the main namespace for use @@ -263,7 +278,7 @@ Pickle::Parser::Matchers for the methods available) post = model!(post) forum = model!(forum) forum.posts.should include(post) - end + end *capture_fields* @@ -276,4 +291,4 @@ can build up composite objects with ease # example of use Given a user exists - And a post exists with author: the user # this step will assign the above user as :author on the post \ No newline at end of file + And a post exists with author: the user # this step will assign the above user as :author on the post diff --git a/lib/pickle/adapter.rb b/lib/pickle/adapter.rb index a405395a..30fe26ef 100644 --- a/lib/pickle/adapter.rb +++ b/lib/pickle/adapter.rb @@ -4,7 +4,7 @@ module Pickle # Abstract Factory adapter class, if you have a factory type setup, you # can easily create an adaptor to make it work with Pickle. # - # The factory adaptor must have a #factories class method that returns + # The factory adaptor must have a #factories class method that returns # its instances, and each instance must respond to: # # #name : identifies the factory by name (default is attr_reader) @@ -12,88 +12,65 @@ module Pickle # #create(attrs = {}) : returns a newly created object class Adapter attr_reader :name, :klass - + def create(attrs = {}) raise NotImplementedError, "create and return an object with the given attributes" end - + if respond_to?(:class_attribute) class_attribute :model_classes else cattr_writer :model_classes end - + self.model_classes = nil - + + # Include this module into your adapter + # this will register the adapter with pickle and it will be picked up for you + # To create an adapter you should create an inner constant "PickleAdapter" + # + # e.g. ActiveRecord::Base::PickleAdapter + # + # @see pickle/adapters/active_record + # @see pickle/adapters/datamapper + module Base + def self.included(base) + adapters << base + end + + # A collection of registered adapters + def self.adapters + @@adapters ||= [] + end + end + class << self def factories raise NotImplementedError, "return an array of factory adapter objects" end def model_classes - @@model_classes ||= - if defined?(::ActiveRecord::Base) - ::ActiveRecord::Base.send(:subclasses).select {|klass| suitable_for_pickle?(klass)} - elsif defined?(::DataMapper::Model) - ::DataMapper::Model.descendants.to_a - else - [] - end - end - - # return true if a klass should be used by pickle - def suitable_for_pickle?(klass) - !klass.abstract_class? && klass.table_exists? && !framework_class?(klass) - end - - # return true if the passed class is a special framework class - def framework_class?(klass) - ((defined?(CGI::Session::ActiveRecordStore::Session) && klass == CGI::Session::ActiveRecordStore::Session)) || - ((defined?(::ActiveRecord::SessionStore::Session) && klass == ::ActiveRecord::SessionStore::Session)) + @@model_classes ||= self::Base.adapters.map{ |a| a.model_classes }.flatten end # Returns the column names for the given ORM model class. def column_names(klass) - if defined?(::ActiveRecord::Base) - klass.column_names - elsif defined?(::DataMapper::Model) - klass.properties.map(&:name) - else - [] - end + klass.const_get(:PickleAdapter).column_names(klass) end def get_model(klass, id) - if defined?(ActiveRecord::Base) - klass.find(id) - elsif defined?(DataMapper::Model) - klass.get(id) - else - nil - end + klass.const_get(:PickleAdapter).get_model(klass, id) end def find_first_model(klass, conditions) - if defined?(ActiveRecord::Base) - klass.find(:first, :conditions => conditions) - elsif defined?(DataMapper::Model) - klass.first(conditions) - else - nil - end + klass.const_get(:PickleAdapter).find_first_model(klass, conditions) end def find_all_models(klass, conditions) - if defined?(ActiveRecord::Base) - klass.find(:all, :conditions => conditions) - elsif defined?(DataMapper::Model) - klass.all(conditions) - else - [] - end + klass.const_get(:PickleAdapter).find_all_models(klass, conditions) end end - + # machinist adapter class Machinist < Adapter def self.factories @@ -105,37 +82,37 @@ def self.factories end factories end - + def initialize(klass, blueprint) @klass, @blueprint = klass, blueprint @name = @klass.name.underscore.gsub('/','_') @name = "#{@blueprint}_#{@name}" unless @blueprint == :master end - + def create(attrs = {}) @klass.send(:make, @blueprint, attrs) end end - + # factory-girl adapter class FactoryGirl < Adapter def self.factories (::Factory.factories.values rescue []).map {|factory| new(factory)} end - + def initialize(factory) @klass, @name = factory.build_class, factory.factory_name.to_s end - + def create(attrs = {}) Factory.create(@name, attrs) end end - + # fallback active record adapter class ActiveRecord < Adapter def self.factories - model_classes.map {|klass| new(klass) } + ::ActiveRecord::Base::PickleAdapter.model_classes.map{|k| new(k)} end def initialize(klass) diff --git a/lib/pickle/adapters/active_record.rb b/lib/pickle/adapters/active_record.rb new file mode 100644 index 00000000..652871ad --- /dev/null +++ b/lib/pickle/adapters/active_record.rb @@ -0,0 +1,46 @@ +begin + require 'activerecord' +rescue LoadError + require 'active_record' +end + +class ActiveRecord::Base + module PickleAdapter + include Pickle::Adapter::Base + + # Do not consider these to be part of the class list + def self.except_classes + @@except_classes ||= [ + "CGI::Session::ActiveRecordStore::Session", + "ActiveRecord::SessionStore::Session" + ] + end + + # Gets a list of the available models for this adapter + def self.model_classes + ::ActiveRecord::Base.__send__(:subclasses).select do |klass| + !klass.abstract_class? && klass.table_exists? && !except_classes.include?(klass.name) + end + end + + # get a list of column names for a given class + def self.column_names(klass) + klass.column_names + end + + # Get an instance by id of the model + def self.get_model(klass, id) + klass.find(id) + end + + # Find the first instance matching conditions + def self.find_first_model(klass, conditions) + klass.find(:first, :conditions => conditions) + end + + # Find all models matching conditions + def self.find_all_models(klass, conditions) + klass.find(:all, :conditions => conditions) + end + end +end diff --git a/lib/pickle/adapters/data_mapper.rb b/lib/pickle/adapters/data_mapper.rb new file mode 100644 index 00000000..2f2747b0 --- /dev/null +++ b/lib/pickle/adapters/data_mapper.rb @@ -0,0 +1,37 @@ +require 'dm-core' + +module DataMapper::Resource + module PickleAdapter + include Pickle::Adapter::Base + + # Do not consider these to be part of the class list + def self.except_classes + @@except_classes ||= [] + end + + # Gets a list of the available models for this adapter + def self.model_classes + ::DataMapper::Model.descendants.to_a.select{|k| !except_classes.include?(k.name)} + end + + # get a list of column names for a given class + def self.column_names(klass) + klass.properties.map(&:name) + end + + # Get an instance by id of the model + def self.get_model(klass, id) + klass.get(id) + end + + # Find the first instance matching conditions + def self.find_first_model(klass, conditions) + klass.first(conditions) + end + + # Find all models matching conditions + def self.find_all_models(klass, conditions) + klass.all(conditions) + end + end +end diff --git a/lib/pickle/email.rb b/lib/pickle/email.rb index d87340d0..ec30f07f 100644 --- a/lib/pickle/email.rb +++ b/lib/pickle/email.rb @@ -11,7 +11,7 @@ def email(ref, fields = nil) index = parse_index(match[1]) email_has_fields?(@emails[index], fields) ? @emails[index] : nil end - + def email_has_fields?(email, fields) parse_fields(fields).each do |key, val| return false unless (Array(email.send(key)) & Array(val)).any? @@ -28,7 +28,7 @@ def click_first_link_in_email(email) request_uri = URI::parse(link).request_uri visit request_uri end - + protected def open_in_browser(path) # :nodoc require "launchy" @@ -36,7 +36,7 @@ def open_in_browser(path) # :nodoc rescue LoadError warn "Sorry, you need to install launchy to open emails: `gem install launchy`" end - + # Saves the emails out to RAILS_ROOT/tmp/ and opens it in the default # web browser if on OS X. (depends on webrat) def save_and_open_emails @@ -76,4 +76,4 @@ def links_in_email(email, protos=['http', 'https']) end end -end \ No newline at end of file +end diff --git a/lib/pickle/world.rb b/lib/pickle/world.rb index 8a1fc222..ed6138de 100644 --- a/lib/pickle/world.rb +++ b/lib/pickle/world.rb @@ -1,5 +1,9 @@ require 'pickle' +# auto require for active record and datamapper +require 'pickle/adapters/active_record' if defined?(ActiveRecord::Base) +require 'pickle/adapters/data_mapper' if defined?(DataMapper::Resource) + # make cucumber world pickle aware World(Pickle::Session) diff --git a/spec/pickle/adapter_spec.rb b/spec/pickle/adapter_spec.rb index 3d01cb55..c1d600fa 100644 --- a/spec/pickle/adapter_spec.rb +++ b/spec/pickle/adapter_spec.rb @@ -3,176 +3,156 @@ require 'active_record' require 'factory_girl' require 'machinist/active_record' +require 'pickle/adapters/active_record' describe Pickle::Adapter do it ".factories should raise NotImplementedError" do lambda{ Pickle::Adapter.factories }.should raise_error(NotImplementedError) end - + it "#create should raise NotImplementedError" do lambda{ Pickle::Adapter.new.create }.should raise_error(NotImplementedError) end - - describe ".suitable_for_pickle?(klass)" do - let :klass do - mock('Class', :abstract_class? => false, :table_exists? => true) - end - - before do - Pickle::Adapter.stub!(:framework_class?).and_return(false) - end - - it "a non abstract class that has a table, and is not a framework class, is suitable_for_pickle" do - Pickle::Adapter.should be_suitable_for_pickle(klass) - end - - it "an abtract_class is not suitable_for_pickle" do - klass.stub!(:abstract_class?).and_return(true) - Pickle::Adapter.should_not be_suitable_for_pickle(klass) - end - - it "a class with no table is not suitable_for_pickle" do - klass.stub!(:table_exists?).and_return(false) - Pickle::Adapter.should_not be_suitable_for_pickle(klass) - end - - it "an frame_work class is not suitable_for_pickle" do - Pickle::Adapter.should_receive(:framework_class?).with(klass).and_return(true) - Pickle::Adapter.should_not be_suitable_for_pickle(klass) - end - end - + describe ".model_classes" do before do Pickle::Adapter.model_classes = nil end - + it "should only include #suitable_for_pickle classes" do klass1 = Class.new(ActiveRecord::Base) klass2 = Class.new(ActiveRecord::Base) - Pickle::Adapter.should_receive(:suitable_for_pickle?).with(klass1).and_return(true) - Pickle::Adapter.should_receive(:suitable_for_pickle?).with(klass2).and_return(false) - Pickle::Adapter.model_classes.should include(klass1) - Pickle::Adapter.model_classes.should_not include(klass2) + klass3 = Class.new(ActiveRecord::Base) + klass4 = Class.new(ActiveRecord::Base) + klass5 = Class.new(ActiveRecord::Base) + klass6 = Class.new(ActiveRecord::Base) + [klass1, klass2,klass3,klass4, klass5, klass6].each{|k| k.stub!(:table_exists?).and_return(true)} + + klass2.stub!(:name).and_return("CGI::Session::ActiveRecordStore::Session") + klass3.stub!(:name).and_return("ActiveRecord::SessionStore::Session") + klass4.stub!(:table_exists?).and_return(false) + klass5.stub!(:abstract_class?).and_return(true) + Pickle::Adapter.model_classes.should include(klass1, klass6) + Pickle::Adapter.model_classes.should_not include(klass2, klass3, klass4, klass5) end end - + describe "adapters: " do before do @klass1 = returning(Class.new(ActiveRecord::Base)) {|k| k.stub!(:name).and_return('One')} @klass2 = returning(Class.new(ActiveRecord::Base)) {|k| k.stub!(:name).and_return('One::Two')} @klass3 = returning(Class.new(ActiveRecord::Base)) {|k| k.stub!(:name).and_return('Three')} end - + describe 'ActiveRecord' do before do - Pickle::Adapter::ActiveRecord.stub!(:model_classes).and_return([@klass1, @klass2, @klass3]) + ActiveRecord::Base::PickleAdapter.stub!(:model_classes).and_return([@klass1, @klass2, @klass3]) end - + it ".factories should create one for each active record class" do Pickle::Adapter::ActiveRecord.should_receive(:new).with(@klass1).once Pickle::Adapter::ActiveRecord.should_receive(:new).with(@klass2).once Pickle::Adapter::ActiveRecord.should_receive(:new).with(@klass3).once Pickle::Adapter::ActiveRecord.factories end - + describe ".new(Class)" do before do @factory = Pickle::Adapter::ActiveRecord.new(@klass2) end - + it "should have underscored (s/_) name of Class as #name" do @factory.name.should == 'one_two' end - + it "#create(attrs) should call Class.create!(attrs)" do @klass2.should_receive(:create!).with({:key => "val"}) @factory.create(:key => "val") end end end - + describe 'FactoryGirl' do before do Pickle::Adapter::FactoryGirl.stub!(:model_classes).and_return([@klass1, @klass2, @klass3]) @orig_factories, Factory.factories = Factory.factories, {} - + Factory.define(:one, :class => @klass1) {} Factory.define(:two, :class => @klass2) {} @factory1 = Factory.factories[:one] @factory2 = Factory.factories[:two] end - + after do Factory.factories = @orig_factories end - + it ".factories should create one for each factory" do Pickle::Adapter::FactoryGirl.should_receive(:new).with(@factory1).once Pickle::Adapter::FactoryGirl.should_receive(:new).with(@factory2).once Pickle::Adapter::FactoryGirl.factories end - + describe ".new(factory)" do before do @factory = Pickle::Adapter::FactoryGirl.new(@factory1) end - + it "should have name of factory_name" do @factory.name.should == 'one' end - + it "should have klass of build_class" do @factory.klass.should == @klass1 end - + it "#create(attrs) should call Factory(<:key>, attrs)" do Factory.should_receive(:create).with("one", {:key => "val"}) @factory.create(:key => "val") end end end - + describe 'Machinist' do before do Pickle::Adapter::Machinist.stub!(:model_classes).and_return([@klass1, @klass2, @klass3]) - + @klass1.blueprint {} @klass3.blueprint {} @klass3.blueprint(:special) {} end - + it ".factories should create one for each master blueprint, and special case" do Pickle::Adapter::Machinist.should_receive(:new).with(@klass1, :master).once Pickle::Adapter::Machinist.should_receive(:new).with(@klass3, :master).once Pickle::Adapter::Machinist.should_receive(:new).with(@klass3, :special).once Pickle::Adapter::Machinist.factories end - + describe ".new(Class, :master)" do before do @factory = Pickle::Adapter::Machinist.new(@klass1, :master) end - + it "should have underscored (s/_) name of Class as #name" do @factory.name.should == 'one' end - + it "#create(attrs) should call Class.make(:master, attrs)" do @klass1.should_receive(:make).with(:master, {:key => "val"}) @factory.create(:key => "val") end end - + describe ".new(Class, :special)" do before do @factory = Pickle::Adapter::Machinist.new(@klass3, :special) end - + it "should have 'special_' as #name" do @factory.name.should == 'special_three' end - + it "#create(attrs) should call Class.make(:special, attrs)" do @klass3.should_receive(:make).with(:special, {:key => "val"}) @factory.create(:key => "val") @@ -180,4 +160,4 @@ end end end -end \ No newline at end of file +end diff --git a/spec/pickle/config_spec.rb b/spec/pickle/config_spec.rb index 3e203349..63ef578e 100644 --- a/spec/pickle/config_spec.rb +++ b/spec/pickle/config_spec.rb @@ -4,27 +4,27 @@ before do @config = Pickle::Config.new end - + it "#adapters should default to :machinist, :factory_girl, :active_record" do @config.adapters.should == [:machinist, :factory_girl, :active_record] end - + it "#adapter_classes should default to Adapter::Machinist, Adapter::FactoryGirl, Adapter::ActiveRecord" do @config.adapter_classes.should == [Pickle::Adapter::Machinist, Pickle::Adapter::FactoryGirl, Pickle::Adapter::ActiveRecord] end - + describe "setting adapters to [:machinist, SomeAdapter]" do class SomeAdapter; end - + before do @config.adapters = [:machinist, SomeAdapter] end - + it "#adapter_classes should be Adapter::Machinist, SomeAdapter" do @config.adapter_classes.should == [Pickle::Adapter::Machinist, SomeAdapter] end end - + describe "#factories" do it "should call adaptor.factories for each adaptor" do Pickle::Adapter::Machinist.should_receive(:factories).and_return([]) @@ -32,14 +32,14 @@ class SomeAdapter; end Pickle::Adapter::ActiveRecord.should_receive(:factories).and_return([]) @config.factories end - + it "should aggregate factories into a hash using factory name as key" do Pickle::Adapter::Machinist.should_receive(:factories).and_return([@machinist = mock('machinist', :name => 'machinist')]) Pickle::Adapter::FactoryGirl.should_receive(:factories).and_return([@factory_girl = mock('factory_girl', :name => 'factory_girl')]) Pickle::Adapter::ActiveRecord.should_receive(:factories).and_return([@active_record = mock('active_record', :name => 'active_record')]) @config.factories.should == {'machinist' => @machinist, 'factory_girl' => @factory_girl, 'active_record' => @active_record} end - + it "should give preference to adaptors first in the list" do Pickle::Adapter::Machinist.should_receive(:factories).and_return([@machinist_one = mock('one', :name => 'one')]) Pickle::Adapter::FactoryGirl.should_receive(:factories).and_return([@factory_girl_one = mock('one', :name => 'one'), @factory_girl_two = mock('two', :name => 'two')]) @@ -47,51 +47,59 @@ class SomeAdapter; end @config.factories.should == {'one' => @machinist_one, 'two' => @factory_girl_two, 'three' => @active_record_three} end end - + it "#mappings should default to []" do @config.mappings.should == [] end describe '#predicates' do it "should be list of all non object ? public instance methods + columns methods of Adapter.model_classes" do - class1 = mock('Class1', :public_instance_methods => ['nope', 'foo?', 'bar?'], :column_names => ['one', 'two']) - class2 = mock('Class2', :public_instance_methods => ['not', 'foo?', 'faz?'], :column_names => ['two', 'three']) + class1 = mock('Class1', + :public_instance_methods => ['nope', 'foo?', 'bar?'], + :column_names => ['one', 'two'], + :const_get => ::ActiveRecord::Base::PickleAdapter + ) + class2 = mock('Class2', + :public_instance_methods => ['not', 'foo?', 'faz?'], + :column_names => ['two', 'three'], + :const_get => ::ActiveRecord::Base::PickleAdapter + ) Pickle::Adapter.stub!(:model_classes).and_return([class1, class2]) - + @config.predicates.to_set.should == ['foo?', 'faz?', 'bar?', 'one', 'two', 'three'].to_set end - + it "should be overridable" do @config.predicates = %w(lame?) @config.predicates.should == %w(lame?) end end - + describe "#map 'foo', :to => 'faz'" do before do @config.map 'foo', :to => 'faz' end - + it "should create OpenStruct(search: 'foo', replace: 'faz') mapping" do @config.mappings.first.should == OpenStruct.new(:search => 'foo', :replacement => 'faz') end end - + describe "#map 'foo', 'bar' :to => 'faz'" do before do @config.map 'foo', 'bar', :to => 'faz' end - + it "should create 2 mappings" do @config.mappings.first.should == OpenStruct.new(:search => 'foo', :replacement => 'faz') @config.mappings.last.should == OpenStruct.new(:search => 'bar', :replacement => 'faz') end end - + it "#configure(&block) should execiute on self" do @config.should_receive(:foo).with(:bar) @config.configure do |c| c.foo :bar end end -end \ No newline at end of file +end diff --git a/spec/pickle/email_spec.rb b/spec/pickle/email_spec.rb index b4c8b4a4..ae60c8be 100644 --- a/spec/pickle/email_spec.rb +++ b/spec/pickle/email_spec.rb @@ -13,22 +13,25 @@ @email1 = mock("Email 1") @email2 = mock("Email 2") ActionMailer::Base.stub!(:deliveries).and_return([@email1, @email2]) + if defined?(ActiveRecord::Base) + ActiveRecord::Base::PickleAdapter.stub!(:model_classes).and_return([]) + end end - + describe "#emails" do it "should return ordered deliveries" do emails.should == [@email1, @email2] end - + describe "(after)" do before do emails end - + it "#email('the email') should return the last delivery" do email('the email').should == @email2 end - + it "#email('the 1st email') should return the first delivery" do email('the 1st email').should == @email1 end @@ -40,36 +43,36 @@ it "#email('the 2nd email') should return the second delivery" do email('the 2nd email').should == @email2 end - + it "#email('the last email') should return the second delivery" do email('the last email').should == @email2 end - + it "#email2('the 3rd email') should be nil" do email('the 3rd email').should == nil end end - + describe "when email1 is to fred & joe, and email2 is to joe" do before do @email1.stub!(:to).and_return(['fred@gmail.com', 'joe@gmail.com']) @email2.stub!(:to).and_return('joe@gmail.com') end - + it "#emails('to: \"fred@gmail.com\"') should just return email1" do emails('to: "fred@gmail.com"').should == [@email1] end - + describe "after #emails('to: \"fred@gmail.com\"')" do before do emails('to: "fred@gmail.com"') end - + it "#email('first') should be #email('last')" do email('first email').should == email('last email') email('first email').should == @email1 end - + it "#email('the email', 'to: \"blah\") should be nil" do email('the email', 'to: "blah"').should == nil end @@ -78,28 +81,28 @@ email('the email', 'to: "fred@gmail.com"').should == @email1 end end - + it "#emails('to: \"joe@gmail.com\"') should return both emails" do emails('to: "joe@gmail.com"').should == [@email1, @email2] end - + describe "and emails have subjects 'email1', 'email2'" do before do @email1.stub!(:subject).and_return('email1') @email2.stub!(:subject).and_return('email2') end - + it "#emails('to: \"joe@gmail.com\", subject: \"email1\"') should return email1" do emails('to: "joe@gmail.com", subject: "email1"').should == [@email1] end - + it "#emails('to: \"fred@gmail.com\", subject: \"email2\"') should return empty array" do emails('to: "fred@gmail.com", subject: "email2"').should == [] end end end end - + describe "#save_and_open_emails" do before do stub!(:open_in_browser) @@ -107,21 +110,21 @@ @now = "2008-01-01".to_time Time.stub!(:now).and_return(@now) end - + it "should call #emails to get emails" do should_receive(:emails).and_return([]) save_and_open_emails end - + describe "when emails have been already been found" do before { @emails = [] } - + it "should not call #emails" do should_not_receive(:emails) save_and_open_emails end end - + it "should create a file in Rails/tmp with the emails in it" do save_and_open_emails File.read("pickle-email-#{@now.to_i}.html").should == "

Email 1

Contents of Email 1

" @@ -132,13 +135,13 @@ save_and_open_emails end end - + describe "following links in emails" do before do stub!(:open_in_browser) @email1.stub!(:body).and_return('some text example page more text') end - + it "should find a link for http://example.com/page" do should_receive(:visit).with('/page') visit_in_email(@email1, 'http://example.com/page') @@ -148,7 +151,7 @@ should_receive(:visit).with('/page') visit_in_email(@email1, 'example page') end - + it "should follow the first link in an email" do should_receive(:visit).with('/page') click_first_link_in_email(@email1) diff --git a/spec/pickle/session_spec.rb b/spec/pickle/session_spec.rb index d1af27d2..bc646728 100644 --- a/spec/pickle/session_spec.rb +++ b/spec/pickle/session_spec.rb @@ -3,7 +3,7 @@ # TODO: remove this and push AR stuff into ORM adapter module ActiveRecord class Base - end + end end module DataMapper @@ -13,7 +13,7 @@ class Model describe Pickle::Session do include Pickle::Session - + let :user_class do mock("User class", :name => 'User') end @@ -21,15 +21,15 @@ class Model let :user do mock("user", :class => user_class, :id => 1) end - + let :user_factory do Pickle::Adapter::ActiveRecord.new(user_class) end - + before do config.stub(:factories).and_return('user' => user_factory) end - + describe "Pickle::Session proxy missing methods to parser", :shared => true do it "should forward to pickle_parser it responds_to them" do subject.pickle_parser.should_receive(:parse_model) @@ -45,17 +45,17 @@ class Model subject do self end - + it_should_behave_like "Pickle::Session proxy missing methods to parser" end describe "extending Pickle::Session" do - subject do + subject do returning Object.new do |object| object.extend Pickle::Session end end - + it_should_behave_like "Pickle::Session proxy missing methods to parser" end @@ -88,11 +88,11 @@ class Model before do Pickle::Adapter.stub!(:get_model).with(user_class, 100).and_return(user_from_db) end - + it "models('user') should be array containing user" do models('user').should == [user_from_db] end - + describe "user should be retrievable with" do it "model('the user')" do model('the user').should == user_from_db @@ -105,79 +105,79 @@ class Model it "model('last user')" do model('last user').should == user_from_db end - + it "model!('last user')" do model('last user').should == user_from_db end end end end - + describe "#create_model" do before do user_factory.stub!(:create).and_return(user) end - + describe "('a user')" do it "should call user_factory.create({})" do user_factory.should_receive(:create).with({}) create_model('a user') end - + describe "after create," do before { create_model('a user') } - + it_should_behave_like "after storing a single user" end end - + describe "('1 user', 'foo: \"bar\", baz: \"bing bong\"')" do it "should call user_factory.create({'foo' => 'bar', 'baz' => 'bing bong'})" do user_factory.should_receive(:create).with({'foo' => 'bar', 'baz' => 'bing bong'}) create_model('1 user', 'foo: "bar", baz: "bing bong"') end - + describe "after create," do before { create_model('1 user', 'foo: "bar", baz: "bing bong"') } - + it_should_behave_like "after storing a single user" end - end + end describe "('an user: \"fred\")" do it "should call user_factory.create({})" do user_factory.should_receive(:create).with({}) create_model('an user: "fred"') end - + describe "after create," do before { create_model('an user: "fred"') } - + it_should_behave_like "after storing a single user" - + it "created_model('the user: \"fred\"') should retrieve the user" do created_model('the user: "fred"').should == user end - + it "created_model?('the user: \"shirl\"') should be false" do created_model?('the user: "shirl"').should == false end - + it "model?('the user: \"shirl\"') should be false" do model?('the user: "shirl"').should == false end end end - + describe "with hash" do it "should call user_factory.create({'foo' => 'bar'})" do user_factory.should_receive(:create).with({'foo' => 'bar'}) create_model('a user', {'foo' => 'bar'}).should == user end - + describe "after create," do before { create_model('a user', {'foo' => 'bar'}) } - + it_should_behave_like "after storing a single user" end end @@ -187,60 +187,60 @@ class Model before do Pickle::Adapter.stub!(:find_first_model).with(user_class, anything).and_return(user) end - + it "should call user_class.find :first, :conditions => {}" do find_model('a user', 'hair: "pink"').should == user end - + describe "after find," do before { find_model('a user', 'hair: "pink"') } - + it_should_behave_like "after storing a single user" end - + describe "with hash" do it "should call user_class.find('user', {'foo' => 'bar'})" do find_model('a user', {'foo' => 'bar'}) end - + describe "after find," do before { find_model('a user', {'foo' => 'bar'}) } - + it_should_behave_like "after storing a single user" end end end - + describe "create and find using plural_factory and table" do context "when given a table without a matching pickle ref column" do let :table do mock(:hashes => [{'name' => 'Fred'}, {'name' => 'Betty'}]) end - + it "#create_models_from_table(, ) should call create_model for each of the table hashes with plain factory name and return the models" do should_receive(:create_model).with("user", 'name' => "Fred").once.ordered.and_return(:fred) should_receive(:create_model).with("user", 'name' => "Betty").once.ordered.and_return(:betty) create_models_from_table("users", table).should == [:fred, :betty] end - + it "#find_models_from_table(,
) should call find_model for each of the table hashes with plain factory name and return the models" do should_receive(:find_model).with("user", 'name' => "Fred").once.ordered.and_return(:fred) should_receive(:find_model).with("user", 'name' => "Betty").once.ordered.and_return(:betty) find_models_from_table("users", table).should == [:fred, :betty] end end - + context "when given a table with a matching pickle ref column" do let :table do mock(:hashes => [{'user' => "fred", 'name' => 'Fred'}, {'user' => "betty", 'name' => 'Betty'}]) end - + it "#create_models_from_table(,
) should call create_model for each of the table hashes with labelled pickle ref" do should_receive(:create_model).with("user \"fred\"", 'name' => "Fred").once.ordered.and_return(:fred) should_receive(:create_model).with("user \"betty\"", 'name' => "Betty").once.ordered.and_return(:betty) create_models_from_table("users", table).should == [:fred, :betty] end - + it "#find_models_from_table(,
) should call find_model for each of the table hashes with labelled pickle ref" do should_receive(:find_model).with("user \"fred\"", 'name' => "Fred").once.ordered.and_return(:fred) should_receive(:find_model).with("user \"betty\"", 'name' => "Betty").once.ordered.and_return(:betty) @@ -248,7 +248,7 @@ class Model end end end - + describe "#find_model!" do it "should call find_model" do should_receive(:find_model).with('name', 'fields').and_return(user) @@ -260,7 +260,7 @@ class Model lambda { find_model!('name', 'fields') }.should raise_error(RuntimeError, "Can't find pickle model: 'name' in this scenario") end end - + describe "#find_models" do before do Pickle::Adapter.stub!(:find_all_models).with(user_class, anything).and_return([user]) @@ -276,47 +276,47 @@ class Model it_should_behave_like "after storing a single user" end end - + describe 'creating \'a super admin: "fred"\', then \'a user: "shirl"\', \'then 1 super_admin\' (super_admin is factory that returns users)' do let(:fred) { mock("fred", :class => user_class, :id => 2) } let(:shirl) { mock("shirl", :class => user_class, :id => 3) } let(:noname) { mock("noname", :class => user_class, :is => 4) } - + let(:super_admin_factory) do Pickle::Adapter::FactoryGirl.new(mock(:build_class => user_class, :factory_name => :super_admin)) end - + before do config.stub(:factories).and_return(user_factory.name => user_factory, super_admin_factory.name => super_admin_factory) user_factory.stub(:create).and_return(shirl) super_admin_factory.stub(:create).and_return(fred, noname) end - + def do_create_users create_model('a super admin: "fred"') create_model('a user: "shirl"') create_model('1 super_admin') end - + it "should call Factory.create with <'super_admin'>, <'user'>, <'super_admin'>" do super_admin_factory.should_receive(:create).with({}).twice user_factory.should_receive(:create).with({}).once do_create_users end - + describe "after create," do before do do_create_users end - + it "created_models('user') should == [fred, shirl, noname]" do created_models('user').should == [fred, shirl, noname] end - + it "created_models('super_admin') should == [fred, noname]" do created_models('super_admin').should == [fred, noname] end - + describe "#created_model" do it "'that user' should be noname (the last user created - as super_admins are users)" do created_model('that user').should == noname @@ -325,27 +325,27 @@ def do_create_users it "'the super admin' should be noname (the last super admin created)" do created_model('that super admin').should == noname end - + it "'the 1st super admin' should be fred" do created_model('the 1st super admin').should == fred end - + it "'the first user' should be fred" do created_model('the first user').should == fred end - + it "'the 2nd user' should be shirl" do created_model('the 2nd user').should == shirl end - + it "'the last user' should be noname" do created_model('the last user').should == noname end - + it "'the user: \"fred\" should be fred" do created_model('the user: "fred"').should == fred end - + it "'the user: \"shirl\" should be shirl" do created_model('the user: "shirl"').should == shirl end @@ -361,7 +361,7 @@ def do_create_users user_factory.stub!(:create).and_return(user) create_model('the user: "me"') end - + it 'model("I") should return the user' do model('I').should == user end @@ -369,11 +369,11 @@ def do_create_users it 'model("myself") should return the user' do model('myself').should == user end - + it "#parser.parse_fields 'author: user \"JIM\"' should raise Error, as model deos not refer" do lambda { pickle_parser.parse_fields('author: user "JIM"') }.should raise_error end - + it "#parser.parse_fields 'author: the user' should return {\"author\" => }" do pickle_parser.parse_fields('author: the user').should == {"author" => user} end @@ -381,34 +381,34 @@ def do_create_users it "#parser.parse_fields 'author: myself' should return {\"author\" => }" do pickle_parser.parse_fields('author: myself').should == {"author" => user} end - + it "#parser.parse_fields 'author: the user, approver: I, rating: \"5\"' should return {'author' => , 'approver' => , 'rating' => '5'}" do pickle_parser.parse_fields('author: the user, approver: I, rating: "5"').should == {'author' => user, 'approver' => user, 'rating' => '5'} end - + it "#parser.parse_fields 'author: user: \"me\", approver: \"\"' should return {'author' => , 'approver' => \"\"}" do pickle_parser.parse_fields('author: user: "me", approver: ""').should == {'author' => user, 'approver' => ""} end end - + describe "convert_models_to_attributes(ar_class, :user => )" do before do user.stub(:is_a?).with(ActiveRecord::Base).and_return(true) end - + describe "(when ar_class has column 'user_id')" do let :ar_class do - mock('ActiveRecord', :column_names => ['user_id']) + mock('ActiveRecord', :column_names => ['user_id'], :const_get => ActiveRecord::Base::PickleAdapter) end - + it "should return {'user_id' => }" do convert_models_to_attributes(ar_class, :user => user).should == {'user_id' => user.id} end end - + describe "(when ar_class has columns 'user_id', 'user_type')" do let :ar_class do - mock('ActiveRecord', :column_names => ['user_id', 'user_type']) + mock('ActiveRecord', :column_names => ['user_id', 'user_type'], :const_get => ActiveRecord::Base::PickleAdapter) end it "should return {'user_id' => , 'user_type' => }" do @@ -417,7 +417,7 @@ def do_create_users end end end - + it "#model!('unknown') should raise informative error message" do lambda { model!('unknown') }.should raise_error("Can't find pickle model: 'unknown' in this scenario") end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 26832b4b..26dc9de8 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,6 +1,8 @@ require 'rubygems' require 'spec' require 'active_support' +require 'active_record' $:.unshift(File.dirname(__FILE__) + '/../lib') require 'pickle' +require 'pickle/adapters/active_record'