Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

upgrading object daddy plugin

  • Loading branch information...
commit 6473198a8dbafee843a9b22af30a3676d7842a06 1 parent 9c0f7e6
@rick authored
View
3  vendor/plugins/object_daddy/.gitignore
@@ -0,0 +1,3 @@
+log
+vendor
+spec/tmp/*.db
View
20 vendor/plugins/object_daddy/MIT-LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2007 Flawed Logic, OG Consulting, Rick Bradley, Yossef Mendelssohn
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
View
314 vendor/plugins/object_daddy/README.markdown
@@ -0,0 +1,314 @@
+Object Daddy
+============
+_Version 0.4.2 (July 28, 2009)_
+
+__Authors:__ [Rick Bradley](mailto:blogicx@rickbradley.com), [Yossef Mendelssohn](mailto:ymendel@pobox.com)
+
+__Copyright:__ Copyright (c) 2007, Flawed Logic, OG Consulting, Rick Bradley, Yossef Mendelssohn
+
+__License:__ MIT License. See MIT-LICENSE file for more details.
+
+Object Daddy is a library (as well as a Ruby on Rails plugin) designed to
+assist in automating testing of large collections of objects, especially webs
+of ActiveRecord models. It is a descendent of the "Object Mother" pattern for
+creating objects for testing, and is related to the concept of an "object
+exemplar" or _stereotype_.
+
+**WARNING** This code is very much at an _alpha_ development stage. Usage, APIs,
+etc., are all subject to change.
+
+See [http://b.logi.cx/2007/11/26/object-daddy](http://b.logi.cx/2007/11/26/object-daddy) for inspiration, historical drama, and too much reading.
+
+## Installation
+
+Presuming your version of Rails has git plugin installation support:
+
+ script/plugin install git://github.com/flogic/object_daddy.git
+
+Otherwise, you can install object_daddy by hand:
+
+1. Unpack the object_daddy directory into vendor/plugins/ in your rails project.
+2. Run the object_daddy/install.rb Ruby script.
+
+
+## Testing
+
+Install the rspec gem and cd into the object_daddy directory. Type `spec
+spec/` and you should see all specs run successfully. If you have autotest
+from the ZenTest gem installed you can run autotest in that directory.
+
+## Using Object Daddy
+
+
+Object Daddy adds a `.generate` method to every ActiveRecord model which can be
+called to generate a valid instance object of that model class, for use in
+testing:
+
+ it "should have a comment for every forum the user posts to" do
+ @user = User.generate
+ @post = Post.generate
+ @post.comments << Comment.generate
+ @user.should have(1).comments
+ end
+
+This allows us to generate custom model objects without relying on fixtures,
+and without knowing, in our various widespread tests and specs, the details of
+creating a User, Post, Comment, etc. Not having to know this information means
+the information isn't coded into dozens (or hundreds) of tests, and won't need
+to be changed when the User (Post, Comment, ...) model is refactored later.
+
+Object Daddy will identify associated classes that need to be instantiated to
+make the main model valid. E.g., given the following models:
+
+ class User < ActiveRecord::Base
+ belongs_to :login
+ validates_presence_of :login
+ end
+
+ class Login < ActiveRecord::Base
+ has_one :user
+ end
+
+A call to `User.generate` will also make a call to `Login.generate` so that
+`User#login` is present, and therefore valid.
+
+If all models were able to be created in a valid form by the default Model.new
+call with no knowledge of the model itself, there'd be no need for Object
+Daddy. So, when we deal with models which have validity requirements,
+requiring fields which have format constraints, we need a means of expressing
+how to create those models -- how to satisfy those validity constraints.
+
+Object Daddy provides a `generator_for` method which allows the developer to
+specify, for a specific model attribute, how to make a valid value. Note that
+`validates_uniqueness_of` can require that, even if we make 100,000 instances
+of a model that unique attributes cannot have the same values.
+
+Object Daddy's `generator_for` method can take three main forms corresponding to
+the means of finding a value for the associated attribute: a block, a method
+call, or using a generator class.
+
+ class User < ActiveRecord::Base
+ validates_presence_of :email
+ validates_uniqueness_of :email
+ validates_format_of :email,
+ :with => /^[-a-z_+0-9.]+@(?:[-a-z_+0-9.]\.)+[a-z]+$/i
+ validates_presence_of :username
+ validates_format_of :username, :with => /^[a-z0-9_]{4,12}$/i
+
+ generator_for :email, :start => 'test@domain.com' do |prev|
+ user, domain = prev.split('@')
+ user.succ + '@' + domain
+ end
+
+ generator_for :username, :method => :next_user
+
+ generator_for :ssn, :class => SSNGenerator
+
+ def self.next_user
+ @last_username ||= 'testuser'
+ @last_username.succ
+ end
+ end
+
+ class SSNGenerator
+ def self.next
+ @last ||= '000-00-0000'
+ @last = ("%09d" % (@last.gsub('-', '').to_i + 1)).sub(/^(\d{3})(\d{2})(\d{4})$/, '\1-\2-\3')
+ end
+ end
+
+Note that the block method of invocation (as used with _:email_ above) takes an
+optional _:start_ argument, to specify the value of that attribute on the first
+run. The block will be called thereafter with the previous value of the
+attribute and will generate the next attribute value to be used.
+
+A simple default block is provided for any generator with a :start value.
+
+ class User < ActiveRecord::Base
+ generator_for :name, :start => 'Joe' do |prev|
+ prev.succ
+ end
+
+ generator_for :name, :start => 'Joe' # equivalent to the above
+ end
+
+The _:method_ form takes a symbol naming a class method in the model class to be
+called to generate a new value for the attribute in question. If the method
+takes a single argument, it will act much like the block method of invocation,
+being called with the previous value and generating the next.
+
+The _:class_ form calls the .next class method on the named class to generate a
+new value for the attribute in question.
+
+The argument (previous value) to the block invocation form can be omitted if
+it's going to be ignored, and simple invocation forms are provided for literal
+values.
+
+ class User < ActiveRecord::Base
+ generator_for(:start_time) { Time.now }
+ generator_for :name, 'Joe'
+ generator_for :age => 25
+ end
+
+The developer would then simply call `User.generate` when testing.
+
+If some attribute values are known (or are being controlled during testing)
+then these can simply be passed in to `.generate`:
+
+ @bad_login = Login.generate(:expiry => 1.week.ago)
+ @expired_user = User.generate(:login => @bad_login)
+
+A `.generate!` method is also provided. The _generate/generate!_ pair of methods
+can be thought of as analogs to create/create!, one merely providing an instance
+that may or may not be valid and the other raising an exception if any
+problem comes up.
+
+Finally, a `.spawn` method is provided that only gives a new, unsaved object. Note
+that this is the only method of the three that is available if you happen to be
+using Object Daddy outside of Rails.
+
+## Exemplars
+
+In the examples given above we are using `generator_for` in the bodies of the
+models themselves. Given that Object Daddy is primarily geared towards
+annotating models with information useful for testing, we anticipate that
+`generator_for` should not normally be included inline in models. Rather, we
+will provide a place where model classes can be re-opened and `generator_for`
+calls (and support methods) can be written without polluting the model files
+with Object Daddy information.
+
+Object Daddy, when installed as a Rails plugin, will create
+*RAILS_ROOT/spec/exemplars/* as a place to hold __exemplar__ files for Rails model
+classes. (We are seeking perhaps some better terminology)
+
+An __exemplar__ for the User model would then be found in
+*RAILS_ROOT/spec/exemplars/user_exemplar.rb* (when you are using a testing tool
+which works from *RAILS_ROOT/test*, Object Daddy will create
+*RAILS_ROOT/test/exemplars* and look for your exemplars in that directory
+instead). Exemplar files are completely optional, and no model need have
+exemplar files. The `.generate` method will still exist and be callable, and
+`generator_for` can be declared in the model files themselves. If an exemplar
+file is available when `.generate` is called on a model, the exemplar file will
+be loaded and used. An example *user_exemplar.rb* appears below:
+
+ require 'ssn_generator'
+
+ class User < ActiveRecord::Base
+ generator_for :email, :start => 'test@domain.com' do |prev|
+ user, domain = prev.split('@')
+ user.succ + '@' + domain
+ end
+
+ generator_for :username, :method => :next_user
+
+ generator_for :ssn, :class => SSNGenerator
+
+ def self.next_user
+ @last_username ||= 'testuser'
+ @last_username.succ
+ end
+ end
+
+## Blocks
+
+The `spawn`, `generate` and `generate!` methods can all accept a block, to which
+they'll yield the generated object. This provides a nice scoping mechanism in
+your code examples. Consider:
+
+ describe "admin user" do
+ it "should be authorized to create company profiles"
+ admin_user = User.generate!
+ admin_user.activate!
+ admin_user.add_role("admin")
+
+ admin_user.should be_authorized(:create, Company)
+ end
+ end
+
+This could be refactored to:
+
+ describe "admin user" do
+ it "should be authorized to create company profiles" do
+ admin_user = User.generate! do |user|
+ user.activate!
+ user.add_role("admin")
+ end
+
+ admin_user.should be_authorized(:create, Company)
+ end
+ end
+
+Or:
+
+ describe "admin user" do
+ it "should be authorized to create company profiles"
+ User.generate! do |user|
+ user.activate!
+ user.add_role("admin")
+ end.should be_authorized(:create, Company)
+ end
+ end
+
+Or even:
+
+ describe "admin user" do
+ def admin_user
+ @admin_user ||= User.generate! do |user|
+ user.activate!
+ user.add_role("admin")
+ end
+ end
+
+ it "should be authorized to create company profiles"
+ admin_user.should be_authorized(:create, Company)
+ end
+ end
+
+This last refactoring allows you to reuse the admin_user method across
+multiple code examples, balancing DRY with local data.
+
+## Object Daddy and Fixtures
+
+While Object Daddy is meant to obviate the hellish devilspawn that are test
+fixtures, Object Daddy should work alongside fixtures just fine. To each his
+own, I suppose.
+
+## Known Issues
+
+The simple invocation forms for `generator_for` when using literal values do not
+work if the literal value is a Hash. Don't do that.
+
+ class User < ActiveRecord::Base
+ generator_for :thing_hash, { 'some key' => 'some value' }
+ generator_for :other_hash => { 'other key' => 'other value' }
+ end
+
+I'm not sure why this would even ever come up, but seriously, don't.
+
+Required `belongs_to` associations are automatically generated when generating an instance,
+but only if necessary.
+
+ class Category < ActiveRecord::Base
+ has_many :items
+ end
+
+ class Item < ActiveRecord::Base
+ belongs_to :category
+ validates_presence_of :category
+ end
+
+`Item.generate` will generate a new category, but `some_category.items.generate` will not.
+Unless, of course, you are foolish enough to define a generator in the exemplar.
+
+ class Item
+ generator_for(:category) { Category.generate }
+ end
+
+Once again, don't do that.
+
+## Rails _surprises_
+
+Due to the way Rails handles associations, cascading generations (as a result of
+required associations) are always generated-and-saved, even if the original generation
+call was a mere `spawn` (`new`). This may come as a surprise, but it would probably be more
+of a surprise if `User.spawn.save` and `User.generate` weren't comparable.
View
12 vendor/plugins/object_daddy/init.rb
@@ -0,0 +1,12 @@
+unless ActiveRecord::Base.respond_to? :inherited_with_object_daddy
+ class ActiveRecord::Base
+ def self.inherited_with_object_daddy(subclass)
+ self.inherited_without_object_daddy(subclass)
+ subclass.send(:include, ObjectDaddy) unless subclass < ObjectDaddy
+ end
+
+ class << self
+ alias_method_chain :inherited, :object_daddy
+ end
+ end
+end
View
28 vendor/plugins/object_daddy/install.rb
@@ -0,0 +1,28 @@
+require 'fileutils'
+
+def readme_contents
+ IO.read(File.join(File.dirname(__FILE__), 'README.markdown'))
+end
+
+rails_root = File.dirname(__FILE__) + '/../../../'
+
+if File.directory?(rails_root + 'spec')
+ unless File.directory?(rails_root + 'spec/exemplars')
+ puts "Creating directory [#{rails_root + 'spec/exemplars'}]"
+ FileUtils.mkdir(rails_root + 'spec/exemplars')
+ end
+else
+ if File.directory?(rails_root + 'test')
+ unless File.directory?(rails_root + 'test/exemplars')
+ puts "Creating directory [#{rails_root + 'test/exemplars'}]"
+ FileUtils.mkdir(rails_root + 'test/exemplars')
+ end
+ else
+ puts "Creating directory [#{rails_root + 'spec'}]"
+ FileUtils.mkdir(rails_root + 'spec')
+ puts "Creating directory [#{rails_root + 'spec/exemplars'}]"
+ FileUtils.mkdir(rails_root + 'spec/exemplars')
+ end
+end
+
+puts readme_contents
View
230 vendor/plugins/object_daddy/lib/object_daddy.rb
@@ -0,0 +1,230 @@
+module ObjectDaddy
+
+ def self.included(klass)
+ klass.extend ClassMethods
+ if defined? ActiveRecord and klass < ActiveRecord::Base
+ klass.extend RailsClassMethods
+
+ class << klass
+ alias_method :validates_presence_of_without_object_daddy, :validates_presence_of
+ alias_method :validates_presence_of, :validates_presence_of_with_object_daddy
+ end
+ end
+ end
+
+ module ClassMethods
+ attr_accessor :exemplars_generated, :exemplar_path, :generators
+ attr_reader :presence_validated_attributes
+ protected :exemplars_generated=
+
+ # :call-seq:
+ # spawn()
+ # spawn() do |obj| ... end
+ # spawn(args)
+ # spawn(args) do |obj| ... end
+ #
+ # Creates a valid instance of this class, using any known generators. The
+ # generated instance is yielded to a block if provided.
+ def spawn(args = {})
+ gather_exemplars
+ if @concrete_subclass_name
+ return block_given? \
+ ? const_get(@concrete_subclass_name).spawn(args) {|instance| yield instance} \
+ : const_get(@concrete_subclass_name).spawn(args)
+ end
+ generate_values(args)
+ instance = new(args)
+ yield instance if block_given?
+ instance
+ end
+
+ # register a generator for an attribute of this class
+ # generator_for :foo do |prev| ... end
+ # generator_for :foo do ... end
+ # generator_for :foo, value
+ # generator_for :foo => value
+ # generator_for :foo, :class => GeneratorClass
+ # generator_for :foo, :method => :method_name
+ def generator_for(handle, args = {}, &block)
+ if handle.is_a?(Hash)
+ raise ArgumentError, "only specify one attr => value pair at a time" unless handle.keys.length == 1
+ gen_data = handle
+ handle = gen_data.keys.first
+ args = gen_data[handle]
+ end
+
+ raise ArgumentError, "an attribute name must be specified" unless handle = handle.to_sym
+
+ unless args.is_a?(Hash)
+ unless block
+ retval = args
+ block = lambda { retval } # lambda { args } results in returning the empty hash that args gets changed to
+ end
+ args = {} # args is assumed to be a hash for the rest of the method
+ end
+
+ if args[:start]
+ block ||= lambda { |prev| prev.succ }
+ end
+
+ if args[:method]
+ h = { :method => args[:method].to_sym }
+ h[:start] = args[:start] if args[:start]
+ record_generator_for(handle, h)
+ elsif args[:class]
+ raise ArgumentError, "generator class [#{args[:class].name}] does not have a :next method" unless args[:class].respond_to?(:next)
+ record_generator_for(handle, :class => args[:class])
+ elsif block
+ raise ArgumentError, "generator block must take an optional single argument" unless (-1..1).include?(block.arity) # NOTE: lambda {} has an arity of -1, while lambda {||} has an arity of 0
+ h = { :block => block }
+ h[:start] = args[:start] if args[:start]
+ record_generator_for(handle, h)
+ else
+ raise ArgumentError, "a block, :class generator, :method generator, or value must be specified to generator_for"
+ end
+ end
+
+ def generates_subclass(subclass_name)
+ @concrete_subclass_name = subclass_name.to_s
+ end
+
+ def gather_exemplars
+ return if exemplars_generated
+
+ self.generators ||= {}
+ if superclass.respond_to?(:gather_exemplars)
+ superclass.gather_exemplars
+ self.generators = (superclass.generators).merge(generators).dup
+ end
+
+ exemplar_path.each do |raw_path|
+ path = File.join(raw_path, "#{underscore(name)}_exemplar.rb")
+ load(path) if File.exists?(path)
+ end
+
+ self.exemplars_generated = true
+ end
+
+ def presence_validated_attributes
+ @presence_validated_attributes ||= {}
+ attrs = @presence_validated_attributes
+ if superclass.respond_to?(:presence_validated_attributes)
+ attrs = superclass.presence_validated_attributes.merge(attrs)
+ end
+ attrs
+ end
+
+ protected
+
+ # we define an underscore helper ourselves since the ActiveSupport isn't available if we're not using Rails
+ def underscore(string)
+ string.gsub(/([a-z])([A-Z])/, '\1_\2').downcase
+ end
+
+ def record_generator_for(handle, generator)
+ self.generators ||= {}
+ raise ArgumentError, "a generator for attribute [:#{handle}] has already been specified" if (generators[handle] || {})[:source] == self
+ generators[handle] = { :generator => generator, :source => self }
+ end
+
+ private
+
+ def generate_values(args)
+ (generators || {}).each_pair do |handle, gen_data|
+ next if args.include?(handle) or args.include?(handle.to_s)
+
+ generator = gen_data[:generator]
+ if generator[:block]
+ process_generated_value(args, handle, generator, generator[:block])
+ elsif generator[:method]
+ method = method(generator[:method])
+ if method.arity == 1
+ process_generated_value(args, handle, generator, method)
+ else
+ args[handle] = method.call
+ end
+ elsif generator[:class]
+ args[handle] = generator[:class].next
+ end
+ end
+
+ generate_missing(args)
+ end
+
+ def process_generated_value(args, handle, generator, block)
+ if generator[:start]
+ value = generator[:start]
+ generator.delete(:start)
+ else
+ value = block.call(generator[:prev])
+ end
+ generator[:prev] = args[handle] = value
+ end
+
+ def generate_missing(args)
+ if presence_validated_attributes and !presence_validated_attributes.empty?
+ req = {}
+ (presence_validated_attributes.keys - args.keys).each {|a| req[a.to_s] = true } # find attributes required by validates_presence_of not already set
+
+ belongs_to_associations = reflect_on_all_associations(:belongs_to).to_a
+ missing = belongs_to_associations.select { |a| req[a.name.to_s] or req[a.primary_key_name.to_s] }
+ if create_scope = scope(:create)
+ missing.reject! { |a| create_scope.include?(a.primary_key_name) }
+ end
+ missing.reject! { |a| [a.name, a.primary_key_name].any? { |n| args.stringify_keys.include?(n.to_s) } }
+ missing.each {|a| args[a.name] = a.class_name.constantize.generate }
+ end
+ end
+ end
+
+ module RailsClassMethods
+ def exemplar_path
+ dir = File.directory?(File.join(RAILS_ROOT, 'spec')) ? 'spec' : 'test'
+ File.join(RAILS_ROOT, dir, 'exemplars')
+ end
+
+ def validates_presence_of_with_object_daddy(*attr_names)
+ @presence_validated_attributes ||= {}
+ new_attr = attr_names.dup
+ new_attr.pop if new_attr.last.is_a?(Hash)
+ new_attr.each {|a| @presence_validated_attributes[a] = true }
+ validates_presence_of_without_object_daddy(*attr_names)
+ end
+
+ # :call-seq:
+ # generate()
+ # generate() do |obj| ... end
+ # generate(args)
+ # generate(args) do |obj| ... end
+ #
+ # Creates and tries to save an instance of this class, using any known
+ # generators. The generated instance is yielded to a block if provided.
+ #
+ # This will not raise errors on a failed save. Use generate! if you
+ # want errors raised.
+ def generate(args = {})
+ spawn(args) do |instance|
+ instance.save
+ yield instance if block_given?
+ end
+ end
+
+ # :call-seq:
+ # generate()
+ # generate() do |obj| ... end
+ # generate(args)
+ # generate(args) do |obj| ... end
+ #
+ # Creates and tries to save! an instance of this class, using any known
+ # generators. The generated instance is yielded to a block if provided.
+ #
+ # This will raise errors on a failed save. Use generate if you do not want
+ # errors raised.
+ def generate!(args = {})
+ spawn(args) do |instance|
+ instance.save!
+ yield instance if block_given?
+ end
+ end
+ end
+end
View
123 vendor/plugins/object_daddy/spec/install_spec.rb
@@ -0,0 +1,123 @@
+require File.dirname(__FILE__) + '/spec_helper'
+require 'fileutils'
+
+describe 'the plugin install.rb script' do
+ before :each do
+ FileUtils.stubs(:mkdir)
+ self.stubs(:puts).returns(true)
+ end
+
+ def do_install
+ eval File.read(File.join(File.dirname(__FILE__), *%w[.. install.rb ]))
+ end
+
+ describe 'when there is a spec directory under RAILS_ROOT' do
+ before :each do
+ File.stubs(:directory?).with('./../../../spec').returns(true)
+ end
+
+ describe 'and there is a spec/exemplars directory under RAILS_ROOT' do
+ before :each do
+ File.stubs(:directory?).with('./../../../spec/exemplars').returns(true)
+ end
+
+ it 'should not create any new directories' do
+ FileUtils.expects(:mkdir).never
+ do_install
+ end
+ end
+
+ describe 'but there is no spec/exemplars directory under RAILS_ROOT' do
+ before :each do
+ File.stubs(:directory?).with('./../../../spec/exemplars').returns(false)
+ end
+
+ it 'should create a spec/exemplars directory under RAILS_ROOT' do
+ FileUtils.expects(:mkdir).with('./../../../spec/exemplars')
+ do_install
+ end
+ end
+ end
+
+ describe 'when there is no spec directory under RAILS_ROOT' do
+ before :each do
+ File.stubs(:directory?).with('./../../../spec').returns(false)
+ end
+
+ describe 'and there is a test directory under RAILS_ROOT' do
+ before :each do
+ File.stubs(:directory?).with('./../../../test').returns(true)
+ end
+
+ describe 'and there is a test/exemplars directory under RAILS_ROOT' do
+ before :each do
+ File.stubs(:directory?).with('./../../../test/exemplars').returns(true)
+ end
+
+ it 'should not create any new directories' do
+ FileUtils.expects(:mkdir).never
+ do_install
+ end
+ end
+
+ describe 'but there is no test/exemplars directory under RAILS_ROOT' do
+ before :each do
+ File.stubs(:directory?).with('./../../../test/exemplars').returns(false)
+ end
+
+ it 'should create a test/exemplars directory under RAILS_ROOT' do
+ FileUtils.expects(:mkdir).with('./../../../test/exemplars')
+ do_install
+ end
+ end
+ end
+
+ describe 'and there is no test directory under RAILS_ROOT' do
+ before :each do
+ File.stubs(:directory?).with('./../../../test').returns(false)
+ end
+
+ it 'should create a spec directory under RAILS_ROOT' do
+ FileUtils.expects(:mkdir).with('./../../../spec')
+ do_install
+ end
+
+ it 'should create a spec/exemplars directory under RAILS_ROOT' do
+ FileUtils.expects(:mkdir).with('./../../../spec/exemplars')
+ do_install
+ end
+ end
+ end
+
+ it 'displays the content of the plugin README file' do
+ self.stubs(:readme_contents).returns('README CONTENTS')
+ self.expects(:puts).with('README CONTENTS')
+ do_install
+ end
+
+ describe 'readme_contents' do
+ it 'should work without arguments' do
+ do_install
+ lambda { readme_contents }.should_not raise_error(ArgumentError)
+ end
+
+ it 'should accept no arguments' do
+ do_install
+ lambda { readme_contents(:foo) }.should raise_error(ArgumentError)
+ end
+
+ it 'should read the plugin README file' do
+ do_install
+ File.stubs(:join).returns('/path/to/README')
+ IO.expects(:read).with('/path/to/README')
+ readme_contents
+ end
+
+ it 'should return the contents of the plugin README file' do
+ do_install
+ File.stubs(:join).returns('/path/to/README')
+ IO.stubs(:read).with('/path/to/README').returns('README CONTENTS')
+ readme_contents.should == 'README CONTENTS'
+ end
+ end
+end
View
861 vendor/plugins/object_daddy/spec/object_daddy_spec.rb
@@ -0,0 +1,861 @@
+require File.dirname(__FILE__) + '/spec_helper'
+require 'ostruct'
+require 'object_daddy'
+
+describe ObjectDaddy, "when included into a class" do
+ before(:each) do
+ @class = Class.new
+ @class.send(:include, ObjectDaddy)
+ end
+
+ it "should provide a means of spawning a class instance" do
+ @class.should respond_to(:spawn)
+ end
+
+ it "should not provide a means of generating and saving a class instance" do
+ @class.should_not respond_to(:generate)
+ end
+
+ it "should not provide a means of generating and saving a class instance while raising exceptions" do
+ @class.should_not respond_to(:generate!)
+ end
+
+ it "should provide a means of registering a generator to assist in creating class instances" do
+ @class.should respond_to(:generator_for)
+ end
+end
+
+describe ObjectDaddy, "when registering a generator method" do
+ before(:each) do
+ @class = Class.new(OpenStruct)
+ @class.send(:include, ObjectDaddy)
+ end
+
+ it "should fail unless an attribute name is provided" do
+ lambda { @class.generator_for }.should raise_error(ArgumentError)
+ end
+
+ it "should fail if an attribute is specified that already has a generator" do
+ @class.generator_for :foo do |prev| end
+ lambda { @class.generator_for :foo do |prev| end }.should raise_error(ArgumentError)
+ end
+
+ it "should be agnostic to attribute names specified as symbols or strings" do
+ @class.generator_for :foo do |prev| end
+ lambda { @class.generator_for 'foo' do |prev| end }.should raise_error(ArgumentError)
+ end
+
+ it "should keep generators registered for different target classes separate" do
+ @class2 = Class.new
+ @class2.send :include, ObjectDaddy
+ @class2.generator_for :foo do |prev| end
+ lambda { @class.generator_for 'foo' do |prev| end }.should_not raise_error
+ end
+
+ it "should succeed if a generator block is provided" do
+ lambda { @class.generator_for :foo do |prev| end }.should_not raise_error
+ end
+
+ it "should not fail if a generator block doesn't handle a previous value" do
+ lambda { @class.generator_for :foo, :first => 'baz' do end }.should_not raise_error(ArgumentError)
+ end
+
+ it "should not fail if a generator block specifically doesn't handle a previous value" do
+ lambda { @class.generator_for :foo, :first => 'baz' do || end }.should_not raise_error(ArgumentError)
+ end
+
+ it "should fail if a generator block expects more than one argument" do
+ lambda { @class.generator_for :foo, :first => 'baz' do |x, y| end }.should raise_error(ArgumentError)
+ end
+
+ it "should allow an initial value with a block argument" do
+ lambda { @class.generator_for :foo, :start => 'baz' do |prev| end }.should_not raise_error
+ end
+
+ it "should succeed if a generator class is provided" do
+ @generator = Class.new
+ @generator.stubs(:next)
+ lambda { @class.generator_for :foo, :class => @generator }.should_not raise_error
+ end
+
+ it "should fail if a generator class is specified which doesn't have a next method" do
+ @generator = Class.new
+ lambda { @class.generator_for :foo, :class => @generator }.should raise_error(ArgumentError)
+ end
+
+ it "should succeed if a generator method name is provided" do
+ @class.stubs(:method_name)
+ lambda { @class.generator_for :foo, :method => :method_name }.should_not raise_error
+ end
+
+ it "should not fail if a non-existent generator method name is provided" do
+ lambda { @class.generator_for :foo, :method => :fake_method }.should_not raise_error(ArgumentError)
+ end
+
+ it "should allow an initial value with a method argument" do
+ @class.stubs(:method_name)
+ lambda { @class.generator_for :foo, :start => 'baz', :method => :method_name }.should_not raise_error
+ end
+
+ it 'should succeed if a value is provided' do
+ lambda { @class.generator_for :foo, 'value' }.should_not raise_error(ArgumentError)
+ end
+
+ it 'should succeed when given an attr => value hash' do
+ lambda { @class.generator_for :foo => 'value' }.should_not raise_error(ArgumentError)
+ end
+
+ it 'should fail when given an attr => value hash with multiple attrs' do
+ lambda { @class.generator_for :foo => 'value', :bar => 'other value' }.should raise_error(ArgumentError)
+ end
+
+ it "should fail unless a generator block, generator class, generator method, or value is provided" do
+ lambda { @class.generator_for 'foo' }.should raise_error(ArgumentError)
+ end
+end
+
+describe ObjectDaddy, 'recording the registration of a generator method' do
+ before(:each) do
+ ObjectDaddy::ClassMethods.send(:public, :record_generator_for)
+ @class = Class.new(OpenStruct)
+ @class.send(:include, ObjectDaddy)
+ end
+
+ it 'should accept a handle and generator' do
+ lambda { @class.record_generator_for('handle', 'generator') }.should_not raise_error(ArgumentError)
+ end
+
+ it 'should require generator' do
+ lambda { @class.record_generator_for('handle') }.should raise_error(ArgumentError)
+ end
+
+ it 'should require a handle' do
+ lambda { @class.record_generator_for }.should raise_error(ArgumentError)
+ end
+
+ it 'should save the generator' do
+ @class.record_generator_for('handle', 'generator')
+ @class.generators['handle'][:generator].should == 'generator'
+ end
+
+ it 'should save the class that specified the generator' do
+ @class.record_generator_for('handle', 'generator')
+ @class.generators['handle'][:source].should == @class
+ end
+
+ it 'should fail if the handle has already been recorded' do
+ @class.record_generator_for('handle', 'generator')
+ lambda { @class.record_generator_for('handle', 'generator 2') }.should raise_error
+ end
+
+ it 'should not fail if the handle has not already been recorded' do
+ lambda { @class.record_generator_for('handle', 'generator') }.should_not raise_error
+ end
+end
+
+describe ObjectDaddy, 'when registering exemplars' do
+ before :each do
+ @class = Class.new(OpenStruct)
+ @class.send(:include, ObjectDaddy)
+ @file_path = File.join(File.dirname(__FILE__), 'tmp')
+ @file_name = File.join(@file_path, 'widget_exemplar.rb')
+ @class.stubs(:exemplar_path).returns(@file_path)
+ @class.stubs(:name).returns('Widget')
+ end
+
+ describe 'before exemplars have been registered' do
+ before :each do
+ @class.stubs(:exemplars_generated).returns(false)
+ end
+
+ it "should look for exemplars for the target class in the standard exemplar path" do
+ @class.expects(:exemplar_path).returns(@file_path)
+ @class.gather_exemplars
+ end
+
+ it "should look for an exemplar for the target class, based on the class's name" do
+ @class.expects(:name).returns('Widget')
+ @class.gather_exemplars
+ end
+
+ it "should register any generators found in the exemplar for the target class" do
+ # we are using the concrete Widget class here because otherwise it's difficult to have our exemplar file work in our class
+ begin
+ # a dummy class, useful for testing the actual loading of exemplar files
+ Widget = Class.new(OpenStruct) { include ObjectDaddy }
+ File.open(@file_name, 'w') {|f| f.puts "class Widget\ngenerator_for :foo\nend\n"}
+ Widget.stubs(:exemplar_path).returns(@file_path)
+ Widget.expects(:generator_for)
+ Widget.gather_exemplars
+ ensure
+ # clean up test data file
+ File.unlink(@file_name) if File.exists?(@file_name)
+ Object.send(:remove_const, :Widget)
+ end
+ end
+
+ it "should read from all paths when exemplar_path returns an array" do
+ # we are using the concrete Widget class here because otherwise it's difficult to have our exemplar file work in our class
+ begin
+ # a dummy class, useful for testing the actual loading of exemplar files
+ Widget = Class.new(OpenStruct) { include ObjectDaddy }
+ File.open(@file_name, 'w') {|f| f.puts "class Widget\ngenerator_for :foo\nend\n"}
+ other_filename = 'widget_exemplar.rb'
+ File.open(other_filename, 'w') {|f| f.puts "class Widget\ngenerator_for :foo\nend\n"}
+ Widget.stubs(:exemplar_path).returns(['.', @file_path])
+ Widget.expects(:generator_for).times(2)
+ Widget.gather_exemplars
+ ensure
+ # clean up test data file
+ File.unlink(@file_name) if File.exists?(@file_name)
+ File.unlink(other_filename) if File.exists?(other_filename)
+ Object.send(:remove_const, :Widget)
+ end
+ end
+
+
+ it 'should record that exemplars have been registered' do
+ @class.expects(:exemplars_generated=).with(true)
+ @class.gather_exemplars
+ end
+ end
+
+ describe 'after exemplars have been registered' do
+ before :each do
+ @class.stubs(:exemplars_generated).returns(true)
+ end
+
+ it "should not look for exemplars for the target class in the standard exemplar path" do
+ @class.expects(:exemplar_path).never
+ @class.gather_exemplars
+ end
+
+ it "should not look for an exemplar for the target class, based on the class's name" do
+ @class.expects(:name).never
+ @class.gather_exemplars
+ end
+
+ it 'should register no generators' do
+ # we are using the concrete Widget class here because otherwise it's difficult to have our exemplar file work in our class
+ begin
+ # a dummy class, useful for testing the actual loading of exemplar files
+ Widget = Class.new(OpenStruct) { include ObjectDaddy }
+ File.open(@file_name, 'w') {|f| f.puts "class Widget\ngenerator_for :foo\nend\n"}
+ Widget.stubs(:exemplar_path).returns(@file_path)
+ Widget.stubs(:exemplars_generated).returns(true)
+ Widget.expects(:generator_for).never
+ Widget.gather_exemplars
+ ensure
+ # clean up test data file
+ File.unlink(@file_name) if File.exists?(@file_name)
+ Object.send(:remove_const, :Widget)
+ end
+ end
+
+ it 'should not record that exemplars have been registered' do
+ @class.expects(:exemplars_generated=).never
+ @class.gather_exemplars
+ end
+ end
+
+ it "should register no generators if no exemplar for the target class is available" do
+ @class.expects(:generator_for).never
+ @class.gather_exemplars
+ end
+end
+
+describe ObjectDaddy, "when spawning a class instance" do
+ before(:each) do
+ @class = Class.new(OpenStruct)
+ @class.send(:include, ObjectDaddy)
+ @file_path = File.join(File.dirname(__FILE__), 'tmp')
+ @file_name = File.join(@file_path, 'widget_exemplar.rb')
+ @class.stubs(:exemplar_path).returns(@file_path)
+ @class.stubs(:name).returns('Widget')
+ end
+
+ it "should yield the instance to a block if given" do
+ yielded_object = nil
+ @class.spawn do |obj|
+ yielded_object = obj
+ end
+ @class.should === yielded_object
+ end
+
+ it "should register exemplars for the target class" do
+ @class.expects(:gather_exemplars)
+ @class.spawn
+ end
+
+ it "should allow attributes to be overridden" do
+ @class.spawn(:foo => 'xyzzy').foo.should == 'xyzzy'
+ end
+
+ it "should use any generators registered with blocks" do
+ @class.generator_for :foo do |prev| "foo"; end
+ @class.spawn.foo.should == 'foo'
+ end
+
+ it "should not use a block generator for an attribute that has been overridden" do
+ @class.generator_for :foo do |prev| "foo"; end
+ @class.spawn(:foo => 'xyzzy').foo.should == 'xyzzy'
+ end
+
+ it "should use any generators registered with generator method names" do
+ @class.stubs(:generator_method).returns('bar')
+ @class.generator_for :foo, :method => :generator_method
+ @class.spawn.foo.should == 'bar'
+ end
+
+ it 'should fail if a generator is registered with a non-existent method name' do
+ @class.generator_for :foo, :method => :nonexistent_metho
+ lambda { @class.spawn.foo }.should raise_error
+ end
+
+ it "should not use a method generator for an attribute that has been overridden" do
+ @class.stubs(:generator_method).returns('bar')
+ @class.generator_for :foo, :method => :generator_method
+ @class.spawn(:foo => 'xyzzy').foo.should == 'xyzzy'
+ end
+
+ it "should use any generators registered with generator classes" do
+ @generator_class = Class.new do
+ def self.next() 'baz' end
+ end
+ @class.generator_for :foo, :class => @generator_class
+ @class.spawn.foo.should == 'baz'
+ end
+
+ it "should not use a class generator for an attribute that has been overridden" do
+ @generator_class = Class.new do
+ def self.next() 'baz' end
+ end
+ @class.generator_for :foo, :class => @generator_class
+ @class.spawn(:foo => 'xyzzy').foo.should == 'xyzzy'
+ end
+
+ it "should return the initial value first if one was registered for a block generator" do
+ @class.generator_for :foo, :start => 'frobnitz' do |prev| "foo"; end
+ @class.spawn.foo.should == 'frobnitz'
+ end
+
+ it "should return the block applied to the initial value on the second call if an initial value was registered for a block generator" do
+ @class.generator_for :foo, :start => 'frobnitz' do |prev| prev + 'a'; end
+ @class.spawn
+ @class.spawn.foo.should == 'frobnitza'
+ end
+
+ it "should return the block applied to the previous value when repeatedly calling a block generator" do
+ @class.generator_for :foo do |prev| prev ? prev.succ : 'test'; end
+ @class.spawn
+ @class.spawn.foo.should == 'tesu'
+ end
+
+ it 'should not require a block if an initial value is given' do
+ lambda { @class.generator_for :foo, :start => 'crapola' }.should_not raise_error(ArgumentError)
+ end
+
+ it 'should default the generator to increment the value if an initial value is given' do
+ @class.generator_for :foo, :start => 'crapola'
+ @class.spawn
+ @class.spawn.foo.should == 'crapolb'
+ end
+
+ it "should return the initial value first if one was registered for a method generator" do
+ @class.instance_eval do
+ def self.generator_value_method(prev)
+ 'foo'
+ end
+ end
+
+ @class.generator_for :foo, :start => 'frobnitz', :method => :generator_value_method
+ @class.spawn.foo.should == 'frobnitz'
+ end
+
+ it "should return the method applied to the initial value on the second call if an initial value was registered for a method generator" do
+ @class.instance_eval do
+ def self.generator_value_method(prev)
+ prev.succ
+ end
+ end
+
+ @class.generator_for :foo, :start => 'frobnitz', :method => :generator_value_method
+ @class.spawn
+ @class.spawn.foo.should == 'frobniua'
+ end
+
+ it "should return the method applied to the previous value when repeatedly calling a method generator" do
+ @class.instance_eval do
+ def self.generator_value_method(prev)
+ if prev
+ prev.succ
+ else
+ 'test'
+ end
+ end
+ end
+
+ @class.generator_for :foo, :method => :generator_value_method
+ @class.spawn
+ @class.spawn.foo.should == 'tesu'
+ end
+
+ it 'should use the return value for a block generator that takes no argument' do
+ x = 5
+ @class.generator_for(:foo) { x }
+ @class.spawn.foo.should == x
+ end
+
+ it 'should use the return value for a block generator that explicitly takes no argument' do
+ x = 5
+ @class.generator_for(:foo) { || x }
+ @class.spawn.foo.should == x
+ end
+
+ it 'should use the supplied value for the generated value' do
+ x = 5
+ @class.generator_for :foo, x
+ @class.spawn.foo.should == x
+ end
+
+ it 'should use the supplied attr => value value for the generated value' do
+ x = 5
+ @class.generator_for :foo => x
+ @class.spawn.foo.should == x
+ end
+
+ it "should call the normal target class constructor" do
+ @class.expects(:new)
+ @class.spawn
+ end
+
+ it 'should not generate a value for an attribute that has been specified as nil' do
+ @class.generator_for :foo => 5
+ @class.spawn(:foo => nil).foo.should be_nil
+ end
+
+ it 'should not generate a value for an attribute that has been specified as false' do
+ @class.generator_for :foo => 5
+ @class.spawn(:foo => false).foo.should be(false)
+ end
+
+ describe 'for an abstract parent class' do
+ before :each do
+ Widget = Class.new(OpenStruct) { include ObjectDaddy }
+ SubWidget = Class.new(Widget) {include ObjectDaddy }
+ Widget.stubs(:exemplar_path).returns(@file_path)
+ SubWidget.stubs(:exemplar_path).returns(File.join(@file_path, 'sub_widget_exemplar.rb'))
+ end
+
+ after :each do
+ [:Widget, :SubWidget].each { |const| Object.send(:remove_const, const) }
+ end
+
+ it 'should generate an instance of a specified concrete subclass (specced using a symbol)' do
+ Widget.generates_subclass :SubWidget
+ Widget.spawn.should be_instance_of(SubWidget)
+ end
+
+ it 'should generate an instance of a specified concrete subclass (specced using a string)' do
+ Widget.generates_subclass 'SubWidget'
+ Widget.spawn.should be_instance_of(SubWidget)
+ end
+
+ it 'should generate an instance of a specified concrete subclass and yield to a block if given' do
+ yielded_object = nil
+ Widget.generates_subclass :SubWidget
+ Widget.spawn do |obj|
+ yielded_object = obj
+ end
+ yielded_object.should be_instance_of(SubWidget)
+ end
+
+ describe 'using exemplar files' do
+ before :each do
+ File.open(@file_name, 'w') do |f|
+ f.puts "class Widget\ngenerates_subclass 'SubWidget'\nend"
+ end
+ end
+
+ after :each do
+ File.unlink @file_name
+ end
+
+ it 'should generate an instance fo the specified concrete subclass' do
+ Widget.spawn.should be_instance_of(SubWidget)
+ end
+ end
+ end
+
+ describe 'for a subclass' do
+ before :each do
+ @subclass = Class.new(@class)
+ @subclass.send(:include, ObjectDaddy)
+ @subfile_path = File.join(File.dirname(__FILE__), 'tmp')
+ @subfile_name = File.join(@file_path, 'sub_widget_exemplar.rb')
+ @subclass.stubs(:exemplar_path).returns(@file_path)
+ @subclass.stubs(:name).returns('SubWidget')
+ end
+
+ describe 'using generators from files' do
+ before :each do
+ Widget = Class.new(OpenStruct) { include ObjectDaddy }
+ SubWidget = Class.new(Widget) { include ObjectDaddy }
+
+ Widget.stubs(:exemplar_path).returns(@file_path)
+ SubWidget.stubs(:exemplar_path).returns(@subfile_path)
+
+ File.open(@file_name, 'w') do |f|
+ f.puts "class Widget\ngenerator_for :blah do |prev| 'blah'; end\ngenerator_for :other do |prev| 'bang'; end\nend\n"
+ end
+ end
+
+ after :each do
+ [@file_name, @subfile_name].each { |file| File.unlink(file) if File.exists?(file) }
+ [:Widget, :SubWidget].each { |const| Object.send(:remove_const, const) }
+ end
+
+ it 'should use generators from the parent class' do
+ SubWidget.spawn.other.should == 'bang'
+ SubWidget.spawn.blah.should == 'blah'
+ end
+
+ describe 'with subclass generators' do
+ before :each do
+ File.open(@subfile_name, 'w') do |f|
+ f.puts "class SubWidget\ngenerator_for :blah do |prev| 'blip'; end\nend\n"
+ end
+ end
+
+ it 'should still use generators from the parent class' do
+ SubWidget.spawn.other.should == 'bang'
+ end
+
+ it 'should allow overriding parent generators' do
+ SubWidget.spawn.blah.should == 'blip'
+ end
+ end
+ end
+
+ describe 'using generators called directly' do
+ before :each do
+ @class.generator_for :other do |prev| 'bang'; end
+ @class.generator_for :blah do |prev| 'blah'; end
+ end
+
+ it 'should use generators from the parent class' do
+ @subclass.spawn.other.should == 'bang'
+ @subclass.spawn.blah.should == 'blah'
+ end
+
+ describe 'with subclass generators' do
+ before :each do
+ @subclass.generator_for :blah do |prev| 'blip'; end
+ end
+
+ it 'should still use generators from the parent class' do
+ @subclass.spawn.other.should == 'bang'
+ end
+
+ it 'should allow overriding parent generators' do
+ @subclass.spawn.blah.should == 'blip'
+ end
+ end
+ end
+ end
+end
+
+# conditionally do Rails tests, if we were included as a plugin
+if File.exists?("#{File.dirname(__FILE__)}/../../../../config/environment.rb")
+
+ setup_rails_database
+
+ class Foo < ActiveRecord::Base
+ has_many :frobnitzes, :class_name => 'Frobnitz'
+ end
+
+ class Bar < ActiveRecord::Base
+ end
+
+ class Thing < ActiveRecord::Base
+ has_many :frobnitzes, :class_name => 'Frobnitz'
+ end
+
+ class Frobnitz < ActiveRecord::Base
+ belongs_to :foo
+ belongs_to :bar
+ belongs_to :thing
+ belongs_to :bango, :class_name => 'Blah', :foreign_key => 'bangbang_id'
+ belongs_to :blotto, :class_name => 'YaModel', :foreign_key => 'blitblot_id'
+ validates_presence_of :foo
+ validates_presence_of :thing_id
+ validates_presence_of :bangbang_id
+ validates_presence_of :blotto
+ validates_presence_of :name
+ validates_presence_of :title, :on => :create, :message => "can't be blank"
+ validates_format_of :title, :with => /^\d+$/
+ end
+
+ class SubFrobnitz < Frobnitz
+ validates_presence_of :bar
+ end
+
+ class Blah < ActiveRecord::Base
+ end
+
+ class YaModel < ActiveRecord::Base
+ end
+
+ describe ObjectDaddy, "when integrated with Rails" do
+ it "should provide a means of generating and saving a class instance" do
+ Frobnitz.should respond_to(:generate)
+ end
+
+ it "should provide a means of generating and saving a class instance while raising exceptions" do
+ Frobnitz.should respond_to(:generate!)
+ end
+
+ describe "and a block is passed to generate" do
+ it "should yield the instance to the block" do
+ yielded_object = nil
+ YaModel.generate do |obj|
+ yielded_object = obj
+ end
+ YaModel.should === yielded_object
+ end
+
+ it "should save the instance before yielding" do
+ instance = Frobnitz.new
+ YaModel.generate do |obj|
+ obj.should_not be_new_record
+ end
+ end
+ end
+
+ describe "and a block is passed to generate!" do
+ it "should yield the instance to the block" do
+ yielded_object = nil
+ YaModel.generate! do |obj|
+ yielded_object = obj
+ end
+ YaModel.should === yielded_object
+ end
+
+ it "should save the instance before yielding" do
+ instance = Frobnitz.new
+ YaModel.generate! do |obj|
+ obj.should_not be_new_record
+ end
+ end
+ end
+
+ describe 'giving an exemplar path for an ActiveRecord model' do
+ it 'should check if a spec directory exists' do
+ File.expects(:directory?).with(File.join(RAILS_ROOT, 'spec'))
+ Frobnitz.exemplar_path
+ end
+
+ describe 'if a spec directory exists' do
+ before :each do
+ File.stubs(:directory?).returns(true)
+ end
+
+ it 'should use the spec directory' do
+ Frobnitz.exemplar_path.should == File.join(RAILS_ROOT, 'spec', 'exemplars')
+ end
+ end
+
+ describe 'if a spec directory does not exist' do
+ before :each do
+ File.stubs(:directory?).returns(false)
+ end
+
+ it 'should use the test directory' do
+ Frobnitz.exemplar_path.should == File.join(RAILS_ROOT, 'test', 'exemplars')
+ end
+ end
+ end
+
+ describe 'when an association is required by name' do
+ it 'should generate an instance for the association' do
+ foo = Foo.create(:name => 'some foo')
+ Foo.expects(:generate).returns(foo)
+ Frobnitz.spawn
+ end
+
+ it 'should assign an instance for the association' do
+ foo = Foo.create(:name => 'some foo')
+ Foo.stubs(:generate).returns(foo)
+ Frobnitz.spawn.foo.should == foo
+ end
+
+ it 'should generate an instance for the association using specified foreign key and class name values' do
+ ya_model = YaModel.create(:name => 'ya model')
+ YaModel.expects(:generate).returns(ya_model)
+ Frobnitz.spawn
+ end
+
+ it 'should assign an instance for the association using specified foreign key and class name values' do
+ ya_model = YaModel.create(:name => 'ya model')
+ YaModel.stubs(:generate).returns(ya_model)
+ Frobnitz.spawn.blotto.should == ya_model
+ end
+
+ it 'should use the parent object when generating an instance through a has_many association' do
+ foo = Foo.create(:name => 'some foo')
+ frob = foo.frobnitzes.generate
+ frob.foo.should == foo
+ end
+
+ it 'should not generate an instance if the attribute is overridden by nil' do
+ Foo.expects(:generate).never
+ Frobnitz.spawn(:foo => nil)
+ end
+
+ it 'should not assign an instance if the attribute is overridden by nil' do
+ Frobnitz.spawn(:foo => nil).foo.should be_nil
+ end
+
+ it 'should not generate an instance if the attribute (*_id) is overridden' do
+ foo = Foo.create(:name => 'some foo')
+ Foo.expects(:generate).never
+ Frobnitz.spawn(:foo_id => foo.id)
+ end
+
+ it 'should use the given attribute (*_id) instead of assigning a new association object' do
+ foo = Foo.create(:name => 'some foo')
+ Frobnitz.spawn(:foo_id => foo.id).foo.should == foo
+ end
+ end
+
+ describe 'when an association is required by ID' do
+ it 'should generate an instance for the association' do
+ thing = Thing.create(:name => 'some thing')
+ Thing.expects(:generate).returns(thing)
+ Frobnitz.spawn
+ end
+
+ it 'should assign an instance for the association' do
+ thing = Thing.create(:name => 'some thing')
+ Thing.stubs(:generate).returns(thing)
+ Frobnitz.spawn.thing.should == thing
+ end
+
+ it 'should generate an instance for the association using specified foreign key and class name values' do
+ blah = Blah.create(:bam => 'blah')
+ Blah.expects(:generate).returns(blah)
+ Frobnitz.spawn
+ end
+
+ it 'should assign an instance for the association using specified foreign key and class name values' do
+ blah = Blah.create(:bam => 'blah')
+ Blah.stubs(:generate).returns(blah)
+ Frobnitz.spawn.bango.should == blah
+ end
+
+ it 'should use the parent object when generating an instance through a has_many association' do
+ thing = Thing.create(:name => 'some thing')
+ frob = thing.frobnitzes.generate
+ frob.thing.should == thing
+ end
+
+ it 'should not generate an instance if the attribute is overridden by nil' do
+ Thing.expects(:generate).never
+ Frobnitz.spawn(:thing_id => nil)
+ end
+
+ it 'should not assign an instance if the attribute is overridden by nil' do
+ Frobnitz.spawn(:thing_id => nil).thing.should be_nil
+ end
+
+ it 'should not generate an instance if the association is overridden' do
+ thing = Thing.create(:name => 'some thing')
+ Thing.expects(:generate).never
+ Frobnitz.spawn(:thing => thing)
+ end
+
+ it 'should use the given association object instead of assigning a new one' do
+ thing = Thing.create(:name => 'some thing')
+ Frobnitz.spawn(:thing => thing).thing.should == thing
+ end
+ end
+
+ it 'should handle a belongs_to association required through inheritance' do
+ thing = Thing.create(:name => 'some thing')
+ Thing.expects(:generate).returns(thing)
+ SubFrobnitz.spawn
+ end
+
+ it 'should include belongs_to associations required by the subclass' do
+ bar = Bar.create
+ Bar.expects(:generate).returns(bar)
+ SubFrobnitz.spawn
+ end
+
+ it 'should not include belongs_to associations required by the subclass at the parent class level' do
+ Bar.expects(:generate).never
+ Frobnitz.spawn
+ end
+
+ it "should not generate instances of belongs_to associations which are not required by a presence_of validator" do
+ Bar.expects(:generate).never
+ Frobnitz.spawn
+ end
+
+ it "should not generate any values for attributes that do not have generators" do
+ Frobnitz.spawn.name.should be_nil
+ end
+
+ it "should use specified values for attributes that do not have generators" do
+ Frobnitz.spawn(:name => 'test').name.should == 'test'
+ end
+
+ it "should use specified values for attributes that would otherwise be generated" do
+ Foo.expects(:generate).never
+ foo = Foo.new
+ Frobnitz.spawn(:foo => foo).foo.should == foo
+ end
+
+ it 'should pass the supplied validator options to the real validator method' do
+ Blah.validates_presence_of :bam, :if => lambda { false }
+ Blah.new.should be_valid
+ end
+
+ it "should ignore optional arguments to presence_of validators" do
+ Frobnitz.presence_validated_attributes.should have_key(:title)
+ end
+
+ it "should return an unsaved record if spawning" do
+ Thing.spawn.should be_new_record
+ end
+
+ it "should return a saved record if generating" do
+ Thing.generate.should_not be_new_record
+ end
+
+ it 'should return a saved record if generating while raising exceptions' do
+ Thing.generate!.should_not be_new_record
+ end
+
+ it "should not fail if trying to generate and save an invalid object" do
+ lambda { Frobnitz.generate(:title => 'bob') }.should_not raise_error(ActiveRecord::RecordInvalid)
+ end
+
+ it "should return an invalid object if trying to generate and save an invalid object" do
+ Frobnitz.generate(:title => 'bob').should_not be_valid
+ end
+
+ it "should fail if trying to generate and save an invalid object while raising acceptions" do
+ lambda { Frobnitz.generate!(:title => 'bob') }.should raise_error(ActiveRecord::RecordInvalid)
+ end
+
+ it "should return a valid object if generate and save succeeds" do
+ Frobnitz.generate(:title => '5', :name => 'blah').should be_valid
+ end
+
+ it 'should allow attributes to be overriden with string keys' do
+ Frobnitz.generator_for :name => 'thing'
+ Frobnitz.generate('name' => 'boo').name.should == 'boo'
+ end
+ end
+end
View
3  vendor/plugins/object_daddy/spec/resources/config/database.yml
@@ -0,0 +1,3 @@
+sqlite3:
+ :adapter: sqlite3
+ :dbfile: vendor/plugins/object_daddy/spec/tmp/object_daddy.db
View
30 vendor/plugins/object_daddy/spec/resources/schema
@@ -0,0 +1,30 @@
+ActiveRecord::Schema.define(:version => 0) do
+ create_table :frobnitzs, :force => true do |t|
+ t.column :name, :string
+ t.column :title, :string
+ t.column :foo_id, :integer
+ t.column :thing_id, :integer
+ t.column :bangbang_id, :integer
+ t.column :blitblot_id, :integer
+ end
+
+ create_table :foos, :force => true do |t|
+ t.column :name, :string
+ end
+
+ create_table :bars, :force => true do |t|
+ t.column :name, :string
+ end
+
+ create_table :things, :force => true do |t|
+ t.column :name, :string
+ end
+
+ create_table :blahs, :force => true do |t|
+ t.column :bam, :string
+ end
+
+ create_table :ya_models, :force => true do |t|
+ t.column :name, :string
+ end
+end
View
5 vendor/plugins/object_daddy/spec/spec.opts
@@ -0,0 +1,5 @@
+--colour
+--format
+s
+--loadby
+mtime
View
20 vendor/plugins/object_daddy/spec/spec_helper.rb
@@ -0,0 +1,20 @@
+$:.unshift(File.dirname(__FILE__) + '/../lib/')
+
+Spec::Runner.configure do |config|
+ config.mock_with :mocha
+end
+
+def setup_rails_database
+ dir = File.dirname(__FILE__)
+
+ ENV["RAILS_ENV"] ||= "test"
+ require "#{dir}/../../../../config/environment"
+
+ db = YAML::load(IO.read("#{dir}/resources/config/database.yml"))
+ ActiveRecord::Base.configurations = {'test' => db[ENV['DB'] || 'sqlite3']}
+ ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test'])
+ ActiveRecord::Migration.verbose = false
+ load "#{dir}/resources/schema"
+
+ require "#{dir}/../init.rb"
+end
View
0  vendor/plugins/object_daddy/spec/tmp/.git_sucks
No changes.
Please sign in to comment.
Something went wrong with that request. Please try again.