mirror of
https://github.com/TracksApp/tracks.git
synced 2025-12-16 15:20:13 +01:00
merge upstream
This commit is contained in:
parent
c58186451f
commit
ce1c092173
72 changed files with 4469 additions and 0 deletions
196
spec/models/todo_spec.rb
Normal file
196
spec/models/todo_spec.rb
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
||||
|
||||
describe Todo do
|
||||
def valid_attributes(attributes={})
|
||||
{
|
||||
:description => "don't forget the milk",
|
||||
:context => mock_model(Context, :name => 'errands')
|
||||
}.merge(attributes)
|
||||
end
|
||||
|
||||
def create_todo(attributes={})
|
||||
todo = Todo.new(valid_attributes(attributes))
|
||||
todo.stub!(:user).and_return(mock_model(User, :date => Time.now))
|
||||
todo.save!
|
||||
todo
|
||||
end
|
||||
|
||||
before(:each) do
|
||||
@todo = Todo.new
|
||||
end
|
||||
|
||||
it_should_belong_to :context
|
||||
it_should_belong_to :project
|
||||
it_should_belong_to :user
|
||||
|
||||
it_should_validate_presence_of :description
|
||||
it_should_validate_presence_of :context
|
||||
it_should_validate_length_of :description, :maximum => 100
|
||||
it_should_validate_length_of :notes, :maximum => 60_000
|
||||
|
||||
it 'validates presence of show_from when deferred'
|
||||
|
||||
it 'ensures that show_from is a date in the future' do
|
||||
todo = Todo.new(valid_attributes)
|
||||
todo.stub!(:user).and_return(mock_model(User, :date => Time.now))
|
||||
todo.show_from = 3.days.ago
|
||||
todo.should have(1).error_on(:show_from)
|
||||
end
|
||||
|
||||
it 'allows show_from to be blank' do
|
||||
todo = Todo.new(valid_attributes(:show_from => ''))
|
||||
todo.should_not have(:any).error_on(:show_from)
|
||||
end
|
||||
|
||||
describe 'states' do
|
||||
it 'is active on initializing' do
|
||||
create_todo.should be_active
|
||||
end
|
||||
|
||||
it 'is deferred when show from is in the future' do
|
||||
create_todo(:show_from => 1.week.from_now).should be_deferred
|
||||
end
|
||||
|
||||
describe 'active' do
|
||||
%w(project_hidden completed deferred).each do |from_state|
|
||||
it "is activable from `#{from_state}'" do
|
||||
todo = create_todo
|
||||
todo.state = from_state
|
||||
todo.send("#{from_state}?").should be_true
|
||||
todo.activate!
|
||||
todo.should be_active
|
||||
end
|
||||
end
|
||||
|
||||
it 'clears show_from when entering active state' do
|
||||
todo = create_todo
|
||||
todo.show_from = 3.days.from_now
|
||||
todo.should be_deferred
|
||||
todo.activate!
|
||||
todo.should be_active
|
||||
todo.show_from.should be_nil
|
||||
end
|
||||
|
||||
it 'clears completed_at when entering active state' do
|
||||
todo = create_todo
|
||||
todo.complete!
|
||||
todo.should be_completed
|
||||
todo.activate!
|
||||
todo.should be_active
|
||||
todo.completed_at.should be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe 'completed' do
|
||||
%w(active project_hidden deferred).each do |from_state|
|
||||
it "is completable from `#{from_state}'" do
|
||||
todo = create_todo
|
||||
todo.state = from_state
|
||||
todo.send("#{from_state}?").should be_true
|
||||
todo.complete!
|
||||
todo.should be_completed
|
||||
end
|
||||
end
|
||||
|
||||
it 'sets complated_at' do
|
||||
todo = create_todo
|
||||
todo.complete!
|
||||
todo.completed_at.should_not be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe 'project_hidden' do
|
||||
%w(active deferred).each do |from_state|
|
||||
it "is hiddable from `#{from_state}'" do
|
||||
todo = create_todo
|
||||
todo.state = from_state
|
||||
todo.send("#{from_state}?").should be_true
|
||||
todo.hide!
|
||||
todo.should be_project_hidden
|
||||
end
|
||||
end
|
||||
|
||||
it 'unhides to deferred when if show_from' do
|
||||
todo = create_todo(:show_from => 4.days.from_now)
|
||||
todo.hide!
|
||||
todo.should be_project_hidden
|
||||
todo.unhide!
|
||||
todo.should be_deferred
|
||||
end
|
||||
|
||||
it 'unhides to active when not show_from' do
|
||||
todo = create_todo(:show_from => '')
|
||||
todo.hide!
|
||||
todo.should be_project_hidden
|
||||
todo.unhide!
|
||||
todo.should be_active
|
||||
end
|
||||
end
|
||||
|
||||
it "is deferrable from `active'" do
|
||||
todo = create_todo
|
||||
todo.activate!
|
||||
todo.should be_active
|
||||
todo.defer!
|
||||
todo.should be_deferred
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when toggling completion' do
|
||||
it 'toggles to active when completed' do
|
||||
todo = create_todo
|
||||
todo.complete!
|
||||
todo.should be_completed
|
||||
todo.toggle_completion!
|
||||
todo.should be_active
|
||||
end
|
||||
|
||||
it 'toggles to completed when not completed' do
|
||||
todo = create_todo
|
||||
todo.should_not be_completed
|
||||
todo.toggle_completion!
|
||||
todo.should be_completed
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when retrieving project' do
|
||||
it 'returns project if set' do
|
||||
project = mock_model(Project)
|
||||
todo = Todo.new(:project => project)
|
||||
todo.project.should == project
|
||||
end
|
||||
|
||||
it 'returns a NullProject if not set' do
|
||||
Todo.new.project.should be_an_instance_of(NullProject)
|
||||
end
|
||||
end
|
||||
|
||||
describe('when setting show_from') { it 'is speced' }
|
||||
|
||||
it 'is starred if tag is "starred"' do
|
||||
todo = create_todo
|
||||
todo.should_not be_starred
|
||||
todo.add_tag('starred')
|
||||
todo.reload
|
||||
todo.should be_starred
|
||||
end
|
||||
|
||||
describe 'when toggling star flag' do
|
||||
it 'toggles to not starred when starred' do
|
||||
todo = create_todo
|
||||
todo.add_tag('starred')
|
||||
todo.should be_starred
|
||||
todo.toggle_star!
|
||||
todo.reload
|
||||
todo.should_not be_starred
|
||||
end
|
||||
|
||||
it 'toggles to starred when not starred' do
|
||||
todo = create_todo
|
||||
todo.should_not be_starred
|
||||
todo.toggle_star!
|
||||
todo.reload
|
||||
todo.should be_starred
|
||||
end
|
||||
end
|
||||
end
|
||||
181
spec/models/user_spec.rb
Normal file
181
spec/models/user_spec.rb
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
||||
|
||||
describe User do
|
||||
def valid_attributes(attributes={})
|
||||
{
|
||||
:login => 'simon',
|
||||
:password => 'foobarspam',
|
||||
:password_confirmation => 'foobarspam'
|
||||
}.merge(attributes)
|
||||
end
|
||||
|
||||
before(:each) do
|
||||
@user = User.new
|
||||
end
|
||||
|
||||
describe 'associations' do
|
||||
it 'has many contexts' do
|
||||
User.should have_many(:contexts).
|
||||
with_order('position ASC').
|
||||
with_dependent(:delete_all)
|
||||
end
|
||||
|
||||
it 'has many projects' do
|
||||
User.should have_many(:projects).
|
||||
with_order('projects.position ASC').
|
||||
with_dependent(:delete_all)
|
||||
end
|
||||
|
||||
# TODO: uses fixtures to test those
|
||||
it 'has many active_projects' do
|
||||
User.should have_many(:active_projects).
|
||||
with_order('projects.position ASC').
|
||||
with_conditions('state = ?', 'active').
|
||||
with_class_name('Project')
|
||||
end
|
||||
|
||||
it 'has many active contexts' do
|
||||
User.should have_many(:active_contexts).
|
||||
with_order('position ASC').
|
||||
with_conditions('hide = ?', 'true').
|
||||
with_class_name('Context')
|
||||
end
|
||||
|
||||
it 'has many todos' do
|
||||
User.should have_many(:todos).
|
||||
with_order('todos.completed_at DESC, todos.created_at DESC').
|
||||
with_dependent(:delete_all)
|
||||
end
|
||||
|
||||
it 'has many deferred todos' do
|
||||
User.should have_many(:deferred_todos).
|
||||
with_order('show_from ASC, todos.created_at DESC').
|
||||
with_conditions('state = ?', 'deferred').
|
||||
with_class_name('Todo')
|
||||
end
|
||||
|
||||
it 'has many completed todos' do
|
||||
User.should have_many(:completed_todos).
|
||||
with_order('todos.completed_at DESC').
|
||||
with_conditions('todos.state = ? and todos.completed_at is not null', 'completed').
|
||||
with_include(:project, :context).
|
||||
with_class_name('Todo')
|
||||
end
|
||||
|
||||
it 'has many notes' do
|
||||
User.should have_many(:notes).
|
||||
with_order('created_at DESC').
|
||||
with_dependent(:delete_all)
|
||||
end
|
||||
|
||||
it 'has many taggings' do
|
||||
User.should have_many(:taggings)
|
||||
end
|
||||
|
||||
it 'has many tags through taggings' do
|
||||
User.should have_many(:tags).through(:taggings).with_select('DISTINCT tags.*')
|
||||
end
|
||||
|
||||
it 'has one preference' do
|
||||
User.should have_one(:preference)
|
||||
end
|
||||
end
|
||||
|
||||
it_should_validate_presence_of :login
|
||||
it_should_validate_presence_of :password
|
||||
it_should_validate_presence_of :password_confirmation
|
||||
|
||||
it_should_validate_length_of :password, :within => 5..40
|
||||
it_should_validate_length_of :login, :within => 3..80
|
||||
|
||||
it_should_validate_uniqueness_of :login
|
||||
it_should_validate_confirmation_of :password
|
||||
|
||||
it 'validates presence of password only when password is required'
|
||||
it 'validates presence of password_confirmation only when password is required'
|
||||
it 'validates confirmation of password only when password is required'
|
||||
it 'validates presence of open_id_url only when using openid'
|
||||
|
||||
it 'accepts only allow auth_type authorized by the admin' do
|
||||
Tracks::Config.should_receive(:auth_schemes).exactly(3).times.and_return(%w(database open_id))
|
||||
User.new(valid_attributes(:auth_type => 'database')).should_not have(:any).error_on(:auth_type)
|
||||
User.new(valid_attributes(:auth_type => 'open_id')).should_not have(:any).error_on(:auth_type)
|
||||
User.new(valid_attributes(:auth_type => 'ldap')).should have(1).error_on(:auth_type)
|
||||
end
|
||||
|
||||
it 'returns login for #to_param' do
|
||||
@user.login = 'john'
|
||||
@user.to_param.should == 'john'
|
||||
end
|
||||
|
||||
it 'has a custom finder to find admin' do
|
||||
User.should_receive(:find).with(:first, :conditions => ['is_admin = ?', true])
|
||||
User.find_admin
|
||||
end
|
||||
|
||||
it 'has a custom finder to find by openid url'
|
||||
it 'knows if there is any user through #no_users_yet? (TODO: better description)'
|
||||
|
||||
describe 'when choosing what do display as a name' do
|
||||
it 'displays login when no first name and last name' do
|
||||
User.new(valid_attributes).display_name.should == 'simon'
|
||||
end
|
||||
|
||||
it 'displays last name when no first name' do
|
||||
User.new(valid_attributes(:last_name => 'foo')).display_name.should == 'foo'
|
||||
end
|
||||
|
||||
it 'displays first name when no last name' do
|
||||
User.new(valid_attributes(:first_name => 'bar')).display_name.should == 'bar'
|
||||
end
|
||||
|
||||
it 'displays first name and last name when both specified' do
|
||||
User.new(valid_attributes(:first_name => 'foo', :last_name => 'bar')).display_name.should == 'foo bar'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'authentication' do
|
||||
before(:each) do
|
||||
@user = User.create!(valid_attributes)
|
||||
end
|
||||
|
||||
it 'authenticates user' do
|
||||
User.authenticate('simon', 'foobarspam').should == @user
|
||||
end
|
||||
|
||||
it 'resets password' do
|
||||
@user.update_attributes(
|
||||
:password => 'new password',
|
||||
:password_confirmation => 'new password'
|
||||
)
|
||||
User.authenticate('simon', 'new password').should == @user
|
||||
end
|
||||
|
||||
it 'does not rehash password after update of login' do
|
||||
@user.update_attribute(:login, 'foobar')
|
||||
User.authenticate('foobar', 'foobarspam').should == @user
|
||||
end
|
||||
|
||||
it 'sets remember token' do
|
||||
@user.remember_me
|
||||
@user.remember_token.should_not be_nil
|
||||
@user.remember_token_expires_at.should_not be_nil
|
||||
end
|
||||
|
||||
it 'unsets remember token' do
|
||||
@user.remember_me
|
||||
@user.remember_token.should_not be_nil
|
||||
@user.forget_me
|
||||
@user.remember_token.should be_nil
|
||||
end
|
||||
|
||||
it 'remembers me default two weeks' do
|
||||
before = 2.weeks.from_now.utc
|
||||
@user.remember_me
|
||||
after = 2.weeks.from_now.utc
|
||||
@user.remember_token.should_not be_nil
|
||||
@user.remember_token_expires_at.should_not be_nil
|
||||
@user.remember_token_expires_at.should be_between(before, after)
|
||||
end
|
||||
end
|
||||
end
|
||||
19
spec/scenarios/contexts_scenario.rb
Normal file
19
spec/scenarios/contexts_scenario.rb
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
class ContextsScenario < Scenario::Base
|
||||
uses :users
|
||||
|
||||
def load
|
||||
%w(Call Email Errand Someday).each_with_index do |context, index|
|
||||
create_context context, index+1
|
||||
end
|
||||
end
|
||||
|
||||
def create_context(name, position)
|
||||
create_model :context, name.downcase.to_sym,
|
||||
:name => name,
|
||||
:position => position,
|
||||
:hide => name == 'Someday' ? true : false,
|
||||
:created_at => Time.now,
|
||||
:updated_at => Time.now,
|
||||
:user_id => user_id(:sean)
|
||||
end
|
||||
end
|
||||
20
spec/scenarios/projects_scenario.rb
Normal file
20
spec/scenarios/projects_scenario.rb
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
class ProjectsScenario < Scenario::Base
|
||||
def load
|
||||
create_project :build_time_machine, 'Build a working time machine'
|
||||
create_project :make_more_money, 'Make more money than Billy Gates'
|
||||
create_project :evict_dinosaurs, 'Evict dinosaurs from the garden'
|
||||
create_project :attend_railsconf, 'Attend RailsConf'
|
||||
end
|
||||
|
||||
def create_project(identifier, name)
|
||||
attributes = {
|
||||
:name => name,
|
||||
:state => 'active',
|
||||
:created_at => 4.day.ago,
|
||||
:updated_at => 1.minute.ago
|
||||
}
|
||||
create_model :project,
|
||||
identifier || attributes[:name].split.first.downcase.to_sym,
|
||||
attributes
|
||||
end
|
||||
end
|
||||
30
spec/scenarios/todos_scenario.rb
Normal file
30
spec/scenarios/todos_scenario.rb
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
class TodosScenario < Scenario::Base
|
||||
uses :contexts, :projects, :users
|
||||
|
||||
def load
|
||||
create_todo :bill,
|
||||
:description => 'Call Bill Gates to find out how much he makes per day',
|
||||
:user => :sean,
|
||||
:context => :call,
|
||||
:project => :make_more_money
|
||||
create_todo :bank,
|
||||
:description => 'Call my bank',
|
||||
:user => :sean,
|
||||
:context => :call,
|
||||
:project => :make_more_money
|
||||
end
|
||||
|
||||
def create_todo(identifier, options={})
|
||||
context = options.delete(:context)
|
||||
project = options.delete(:project)
|
||||
user = options.delete(:user)
|
||||
attributes = {
|
||||
:state => 'active',
|
||||
:created_at => 1.week.ago,
|
||||
:context_id => context_id(context),
|
||||
:project_id => project_id(project),
|
||||
:user_id => user_id(user)
|
||||
}.merge(options)
|
||||
create_model :todo, identifier, attributes
|
||||
end
|
||||
end
|
||||
19
spec/scenarios/users_scenario.rb
Normal file
19
spec/scenarios/users_scenario.rb
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
class UsersScenario < Scenario::Base
|
||||
def load
|
||||
create_user :login => 'johnny', :first_name => 'Johnny', :last_name => 'Smith'
|
||||
create_user :login => 'jane', :first_name => 'Jane', :last_name => 'Pilbeam'
|
||||
create_user :login => 'sean', :first_name => 'Sean', :last_name => 'Pallmer'
|
||||
end
|
||||
|
||||
def create_user(attributes={})
|
||||
password = attributes[:login] + Time.now.to_s
|
||||
attributes = {
|
||||
:password => password,
|
||||
:password_confirmation => password,
|
||||
:is_admin => attributes[:is_admin] || false,
|
||||
}.merge(attributes)
|
||||
identifier = attributes[:login].downcase.to_sym
|
||||
user = create_model :user, identifier, attributes
|
||||
Preference.create(:show_number_completed => 5, :user => user)
|
||||
end
|
||||
end
|
||||
4
vendor/plugins/scenarios/.gitignore
vendored
Normal file
4
vendor/plugins/scenarios/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
environments
|
||||
*.log
|
||||
tmp
|
||||
vendor
|
||||
19
vendor/plugins/scenarios/LICENSE
vendored
Normal file
19
vendor/plugins/scenarios/LICENSE
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2007, Adam Williams and John W. Long.
|
||||
|
||||
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.
|
||||
262
vendor/plugins/scenarios/README
vendored
Normal file
262
vendor/plugins/scenarios/README
vendored
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
== Rails Scenarios Plugin
|
||||
|
||||
Who hasn't experienced the pain of dozens of YAML files filled with hundreds
|
||||
of inter-related data structures? When do you look at People of an
|
||||
Organization and not have to look at the organization_id, open the
|
||||
organizations.yml file, and search for 'id: X'?
|
||||
|
||||
In a nutshell, scenarios are a drop in replacement for YAML fixtures. Instead
|
||||
of encouraging you to create a mindless amount of raw data in the form of
|
||||
YAML, scenarios encourage you to create code that populates your tables with
|
||||
the appropriate records.
|
||||
|
||||
How is it different from other solutions? A few things:
|
||||
|
||||
* It continues to provide a fundamental, fast insertion method using attributes
|
||||
written directly to a table. This is the
|
||||
Scenarios::TableMethods#create_record method.
|
||||
|
||||
* It allows you to create records using validations if you prefer, or if it's
|
||||
important to have all your callbacks be invoked. See
|
||||
Scenarios::TableMethods#create_model. Both create_record and create_model
|
||||
allow you to name your instances for retrieval by the instance and id reader
|
||||
methods (more below).
|
||||
|
||||
* Nothing stops you from simply invoking YouModel.create!, etc. We'll still
|
||||
keep track of the tables the scenario modifies and clean things up afterward.
|
||||
|
||||
* It allows you to create re-usable scenarios as classes. These classes are
|
||||
like any other class - they may include modules, subclass, and be composed of
|
||||
other scenarios. See Scenarios::Base.uses. This also means that you can load
|
||||
any scenario into any Rails environment. That's what the 'rake
|
||||
db:scenario:load' task is good for (more below). Very handy for re-using all
|
||||
that test support code to create populated demos!
|
||||
|
||||
=== Quick Start
|
||||
|
||||
Since Scenarios is a Rails plugin at this time, you should get it installed,
|
||||
using the appropriate method (script/plugin, svn, piston) into your
|
||||
vendor/plugins directory. Once you have this, in your spec_helper.rb or
|
||||
test_helper.rb, add the following line after the spec requires:
|
||||
|
||||
require 'scenarios'
|
||||
|
||||
The Scenarios you write should be placed in the spec/scenarios directory of your
|
||||
Rails project if you're using RSpec, or the test/scenarios directory of your
|
||||
Rails project if you're using Test::Unit. Scenario file names always end in
|
||||
"_scenario.rb" and classes end in "Scenario".
|
||||
|
||||
A simple scenario looks like this:
|
||||
|
||||
# in spec/scenarios/users_scenario.rb or test/scenarios/users_scenario.rb
|
||||
class UsersScenario < Scenario::Base
|
||||
def load
|
||||
create_record :user, :john, :name => 'John', :password => 'doodaht'
|
||||
create_record :user, :cindy, :name => 'Cindy', :password => 'whoot!'
|
||||
end
|
||||
end
|
||||
|
||||
In the example above, I'm using the <tt>create_record</tt> instance method to
|
||||
create two users: John and Cindy. Notice the calls to <tt>create_record</tt>.
|
||||
There are three parameters. The first is the singular name of the table to
|
||||
insert the record into, the second is the symbolic name of the record (more on
|
||||
that later), and the third is a hash of the attributes of the record.
|
||||
|
||||
To use the UsersScenario in a description, you should declare it using
|
||||
the <tt>scenario</tt> method. Here it is within a spec file (RSpec):
|
||||
|
||||
# in spec/models/user_spec.rb
|
||||
describe User do
|
||||
scenario :users
|
||||
|
||||
it "should allow me to do something with John" do
|
||||
user = users(:john)
|
||||
user.password.should == "doodaht"
|
||||
end
|
||||
end
|
||||
|
||||
and here it is within a standard Test::Unit test:
|
||||
|
||||
# in test/unit/user_test.rb
|
||||
class UserTest < Test::Unit::TestCase
|
||||
|
||||
scenario :users
|
||||
|
||||
def test_do_something
|
||||
user = users(:john)
|
||||
assert_equal "doodaht", user.password
|
||||
end
|
||||
end
|
||||
|
||||
Notice that it is easy to load an instance of a model object using its
|
||||
symbolic name with a reader method, similar to that of Rails' fixtures. In the
|
||||
example above, I loaded John with the reader method <tt>users</tt> and the
|
||||
symbolic name <tt>:john</tt>. (Remember that in the Users scenario I declared
|
||||
that John should be accessible through the symbolic name <tt>:john</tt>.)
|
||||
|
||||
I could also have retrieved an array of user fixtures by passing in
|
||||
multiple symbolic names to the reader method:
|
||||
|
||||
# in spec/models/user_spec.rb
|
||||
describe User do
|
||||
scenario :users
|
||||
|
||||
it "should allow me to get all admins" do
|
||||
admins = users(:john, :ryan)
|
||||
User.admins.should eql(admins)
|
||||
end
|
||||
end
|
||||
|
||||
=== Composition
|
||||
|
||||
In real life your scenarios will probably grow quite complicated. The
|
||||
scenarios plugin allows you to deal with this complexity through composition.
|
||||
|
||||
Here's a simple example:
|
||||
|
||||
# in spec/scenarios/posts_scenario.rb or test/scenarios/posts_scenario.rb
|
||||
class PostsScenario < Scenario::Base
|
||||
def load
|
||||
create_record :post, :first, :title => "First Post"
|
||||
create_record :post, :second, :title => "Second Post"
|
||||
end
|
||||
end
|
||||
|
||||
# in spec/scenarios/comments_scenario.rb or test/scenarios/comments_scenario.rb
|
||||
class CommentsScenario < Scenario::Base
|
||||
uses :posts
|
||||
|
||||
def load
|
||||
create_record :comment, :first, :body => "Nice post!", :post_id => post_id(:first)
|
||||
create_record :comment, :second, :body => "I like it.", :post_id => post_id(:first)
|
||||
create_record :comment, :third, :body => "I thoroughly disagree.", :post_id => post_id(:second)
|
||||
end
|
||||
end
|
||||
|
||||
In the example above, the CommentsScenario declares that it depends on the
|
||||
Posts scenario with the <tt>uses</tt> class method. This means that if you
|
||||
load the CommentsScenario, the PostsScenario will be loaded first and the
|
||||
CommentsScenario will have access to all the data loaded by the PostsScenario
|
||||
in its own <tt>load</tt> method. Note that inside the load method I'm using
|
||||
another form of reader methed which simply gives you the id for a symbolic
|
||||
name (in this case: <tt>post_id</tt>). This is most useful for making
|
||||
associations, as done here with comments and posts.
|
||||
|
||||
=== Helper Methods
|
||||
|
||||
Another way of simplifying your scenarios and specs/tests is through helper
|
||||
methods. The Scenarios plugin provides a handy way to declare helper methods
|
||||
that are accessible from inside the scenario and also from inside related
|
||||
RSpec/Test::Unit examples:
|
||||
|
||||
# in spec/scenarios/users_scenario.rb or test/scenarios/users_scenario.rb
|
||||
class UsersScenario < Scenario::Base
|
||||
def load
|
||||
create_user :name => "John"
|
||||
end
|
||||
|
||||
helpers do
|
||||
def create_user(attributes={})
|
||||
create_record :user, attributes[:name].downcase.intern, attributes
|
||||
end
|
||||
def login_as(user)
|
||||
@request.session[:user_id] = user.id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Helper methods declared inside the helpers block are mixed into the scenario
|
||||
when it is instantiated and mixed into examples that declare that they are using
|
||||
the scenario. Also, in the case where one scenario <tt>uses</tt> another, the
|
||||
using scenario will have the helper methods of the used scenario.
|
||||
|
||||
# in spec/controllers/projects_controller_spec.rb
|
||||
describe "Projects screen" do
|
||||
scenario :users
|
||||
|
||||
it "should show active projects" do
|
||||
login_as(users(:john))
|
||||
get :projects
|
||||
@response.should have_tag('#active_projects')
|
||||
end
|
||||
end
|
||||
|
||||
# in test/functional/projects_controller_test.rb
|
||||
class PeopleControllerTest < Test::Unit::TestCase
|
||||
scenario :users
|
||||
|
||||
def test_index
|
||||
login_as(users(:john))
|
||||
get :projects
|
||||
assert_tag('#active_projects')
|
||||
end
|
||||
end
|
||||
|
||||
Notice that within my specs/tests I have access to the login_as helper method
|
||||
declared inside the <tt>helpers</tt> block of the UsersScenario. Scenario
|
||||
helpers are a great way to share helper methods between specs/tests that use a
|
||||
specific scenario.
|
||||
|
||||
=== Built-in Scenario
|
||||
|
||||
There is a scenario named 'blank' that comes with the plugin. This scenario is
|
||||
useful when you want to express, and guarantee, that the database is empty. It
|
||||
works by using your db/schema.rb, so if the table isn't created in there, it
|
||||
won't be cleaned up.
|
||||
|
||||
Scenario.load_paths is an array of the locations to look for scenario
|
||||
definitions. The built-in scenarios directory is consulted last, so if you'd
|
||||
like to re-define, for instance, the 'blank' scenario, simply create
|
||||
'blank_scenario.rb' in your spec/scenarios or test/scenarios directory.
|
||||
|
||||
=== Load Rake Task
|
||||
|
||||
The Scenarios plugin provides a single Rake task, <tt>db:scenario:load</tt>,
|
||||
which you may use in a fashion similar to Rails fixtures'
|
||||
<tt>db:fixtures:load</tt>.
|
||||
|
||||
rake db:scenario:load SCENARIO=comments
|
||||
|
||||
When invoked, this task will populate the development database with the named
|
||||
scenario.
|
||||
|
||||
If you do not specify SCENARIO, the task will expect to find a default scenario
|
||||
(a file 'default_scenario.rb' having DefaultScenario defined in it). It is our
|
||||
practice to have it such that this scenario <tt>uses</tt> a number of our other
|
||||
scenarios, thereby:
|
||||
|
||||
* encouraging us to use test data that looks good in the running development
|
||||
application
|
||||
|
||||
* allowing us to troubleshoot failing tests in the running development
|
||||
application
|
||||
|
||||
=== More Information
|
||||
|
||||
For more information, be sure to look through the documentation over at RubyForge:
|
||||
|
||||
* http://faithfulcode.rubyforge.org/docs/scenarios
|
||||
|
||||
You might also enjoy taking a look at the specs for the plugin and the example
|
||||
scenarios:
|
||||
|
||||
* http://faithfulcode.rubyforge.org/svn/plugins/trunk/scenarios/spec/scenarios_spec.rb
|
||||
* http://faithfulcode.rubyforge.org/svn/plugins/trunk/scenarios/spec/scenarios
|
||||
|
||||
Browse the complete source code:
|
||||
|
||||
* http://faithfulcode.rubyforge.org/svn/plugins/trunk/scenarios
|
||||
|
||||
=== Running Tests
|
||||
|
||||
You should be able to simply run rake. Notice in testing/environment.rb the
|
||||
revisions under which this project will work. If you intend to test against
|
||||
HEAD, you will need to delete the directory testing/tmp/trunk/HEAD. At some
|
||||
point, it would be nice to have the script track the revision of HEAD that we
|
||||
have, and update the directory automatically.
|
||||
|
||||
=== License
|
||||
|
||||
The Scenarios plugin is released under the MIT-License and is Copyright (c)
|
||||
2007, Adam Williams and John W. Long. Special thanks to Chris Redinger for his
|
||||
part in helping us get this plugin ready for the public.
|
||||
10
vendor/plugins/scenarios/Rakefile
vendored
Normal file
10
vendor/plugins/scenarios/Rakefile
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
require File.expand_path(File.dirname(__FILE__) + '/testing/plugit_descriptor')
|
||||
|
||||
require 'rake/rdoctask'
|
||||
Rake::RDocTask.new(:doc) do |r|
|
||||
r.title = "Rails Scenarios Plugin"
|
||||
r.main = "README"
|
||||
r.options << "--line-numbers"
|
||||
r.rdoc_files.include("README", "LICENSE", "lib/**/*.rb")
|
||||
r.rdoc_dir = "doc"
|
||||
end
|
||||
1
vendor/plugins/scenarios/TODO
vendored
Normal file
1
vendor/plugins/scenarios/TODO
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
Make sure before :all's that use scenario methods work. They don't right now.
|
||||
127
vendor/plugins/scenarios/helpers.diff
vendored
Normal file
127
vendor/plugins/scenarios/helpers.diff
vendored
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
Only in /Users/aiwilliams/Workspaces/faithfulcode/scenarios/: .git
|
||||
Only in scenarios/: .svn
|
||||
Only in scenarios/: .tm_last_run_ruby
|
||||
diff -r scenarios/Rakefile /Users/aiwilliams/Workspaces/faithfulcode/scenarios/Rakefile
|
||||
3c3
|
||||
< TESTING_ENVIRONMENTS["rspec_3317_rails_8956"].load
|
||||
---
|
||||
> TESTING_ENVIRONMENTS["rspec_3119_rails_8375"].load
|
||||
Only in /Users/aiwilliams/Workspaces/faithfulcode/scenarios/: helpers.diff
|
||||
Only in scenarios/lib: .svn
|
||||
Only in scenarios/lib/scenarios: .svn
|
||||
diff -r scenarios/lib/scenarios/base.rb /Users/aiwilliams/Workspaces/faithfulcode/scenarios/lib/scenarios/base.rb
|
||||
11,14c11,12
|
||||
< # be included into the scenario and all specs that include the scenario.
|
||||
< # You may also provide names of helpers from your scenarios/helpers
|
||||
< # directory, or any other module you'd like included in your Scenario.
|
||||
< def helpers(helper_names_or_modules = [], &block)
|
||||
---
|
||||
> # be included into the scenario and all specs that include the scenario
|
||||
> def helpers(&block)
|
||||
17,19d14
|
||||
< mod.module_eval do
|
||||
< [helper_names_or_modules].flatten.each {|h| include h.is_a?(Module) ? h : h.to_scenario_helper}
|
||||
< end
|
||||
Only in scenarios/lib/scenarios/builtin: .svn
|
||||
Only in scenarios/lib/scenarios/extensions: .svn
|
||||
diff -r scenarios/lib/scenarios/extensions/string.rb /Users/aiwilliams/Workspaces/faithfulcode/scenarios/lib/scenarios/extensions/string.rb
|
||||
22,39d21
|
||||
< # Convert a string into the associated scenario helper module:
|
||||
< #
|
||||
< # "basic".to_scenario_helper #=> BasicScenarioHelper
|
||||
< # "basic_scenario".to_scenario_helper #=> BasicScenarioHelper
|
||||
< #
|
||||
< # Raises Scenario::NameError if the the helper cannot be loacated as
|
||||
< # 'helpers/<name>_helper' in Scenario.load_paths.
|
||||
< def to_scenario_helper
|
||||
< class_name = "#{self.strip.camelize.sub(/ScenarioHelper$/, '')}ScenarioHelper"
|
||||
< Scenario.load_paths.each do |path|
|
||||
< filename = "#{path}/#{class_name.underscore}.rb"
|
||||
< if File.file?(filename)
|
||||
< require filename
|
||||
< break
|
||||
< end
|
||||
< end
|
||||
< class_name.constantize rescue raise Scenario::NameError, "Expected to find #{class_name} in #{Scenario.load_paths.inspect}"
|
||||
< end
|
||||
diff -r scenarios/lib/scenarios/extensions/symbol.rb /Users/aiwilliams/Workspaces/faithfulcode/scenarios/lib/scenarios/extensions/symbol.rb
|
||||
14,23d13
|
||||
< # Convert a symbol into the associated scenario helper module:
|
||||
< #
|
||||
< # :basic.to_scenario_helper #=> BasicScenarioHelper
|
||||
< # :basic_scenario.to_scenario_helper #=> BasicScenarioHelper
|
||||
< #
|
||||
< # Raises Scenario::NameError if the the helper cannot be loacated as
|
||||
< # 'helpers/<name>_helper' in Scenario.load_paths.
|
||||
< def to_scenario_helper
|
||||
< to_s.to_scenario_helper
|
||||
< end
|
||||
Only in scenarios/spec: .svn
|
||||
Only in scenarios/spec: environments.rb
|
||||
Only in scenarios/spec/scenarios: .svn
|
||||
Only in scenarios/spec/scenarios: helpers
|
||||
diff -r scenarios/spec/scenarios_spec.rb /Users/aiwilliams/Workspaces/faithfulcode/scenarios/spec/scenarios_spec.rb
|
||||
23,27d22
|
||||
< it 'should allow us to have helpers in scenarios/helpers directory which we can get through the helpers class method' do
|
||||
< klass = :empty.to_scenario
|
||||
< klass.helpers :myown
|
||||
< end
|
||||
<
|
||||
diff -r scenarios/spec/spec_helper.rb /Users/aiwilliams/Workspaces/faithfulcode/scenarios/spec/spec_helper.rb
|
||||
1,20c1,6
|
||||
< $LOAD_PATH << File.dirname(__FILE__) + '/../testing'
|
||||
<
|
||||
< require File.dirname(__FILE__) + "/environments"
|
||||
<
|
||||
< require 'active_support'
|
||||
< require 'active_record'
|
||||
< require 'action_controller'
|
||||
< require 'action_view'
|
||||
<
|
||||
< module Spec
|
||||
< module Rails
|
||||
< module Example
|
||||
< end
|
||||
< end
|
||||
< end
|
||||
<
|
||||
< require 'spec/rails'
|
||||
< require 'scenarios'
|
||||
<
|
||||
< require 'models'
|
||||
\ No newline at end of file
|
||||
---
|
||||
> require File.expand_path(File.dirname(__FILE__) + "/../testing/environment")
|
||||
> TESTING_ENVIRONMENTS[TESTING_ENVIRONMENT].load(DATABASE_ADAPTER)
|
||||
> require "models"
|
||||
> require "spec"
|
||||
> require "spec/rails"
|
||||
> require "scenarios"
|
||||
Only in scenarios/tasks: .svn
|
||||
Only in scenarios/test: .svn
|
||||
Only in scenarios/testing: .svn
|
||||
diff -r scenarios/testing/environment.rb /Users/aiwilliams/Workspaces/faithfulcode/scenarios/testing/environment.rb
|
||||
15c15
|
||||
< TESTING_ENVIRONMENT = "rspec_3317_rails_8956" unless defined?(TESTING_ENVIRONMENT)
|
||||
---
|
||||
> TESTING_ENVIRONMENT = "rspec_3119_rails_8375" unless defined?(TESTING_ENVIRONMENT)
|
||||
31c31
|
||||
< # system "cd #{lib.support_directory} && patch -p0 < #{File.join(TESTING_ROOT, "rspec_on_rails_3119.patch")}"
|
||||
---
|
||||
> system "cd #{lib.support_directory} && patch -p0 < #{File.join(TESTING_ROOT, "rspec_on_rails_3119.patch")}"
|
||||
36,38c36,38
|
||||
< TESTING_ENVIRONMENTS << TestingLibrary::Environment.new("rspec_3317_rails_8956", SUPPORT_TEMP, DB_CONFIG_FILE, DB_SCHEMA_FILE) do |env|
|
||||
< env.package "rails", "http://svn.rubyonrails.org/rails", "trunk", "8956", &rails_package
|
||||
< env.package "rspec", "http://rspec.rubyforge.org/svn", "trunk", "3317", &rspec_package
|
||||
---
|
||||
> TESTING_ENVIRONMENTS << TestingLibrary::Environment.new("rspec_3119_rails_8375", SUPPORT_TEMP, DB_CONFIG_FILE, DB_SCHEMA_FILE) do |env|
|
||||
> env.package "rails", "http://svn.rubyonrails.org/rails", "trunk", "8375", &rails_package
|
||||
> env.package "rspec", "http://rspec.rubyforge.org/svn", "trunk", "3119", &rspec_package
|
||||
40c40
|
||||
< TESTING_ENVIRONMENTS << TestingLibrary::Environment.new("rspec_3317_rails_1_2_6", SUPPORT_TEMP, DB_CONFIG_FILE, DB_SCHEMA_FILE) do |env|
|
||||
---
|
||||
> TESTING_ENVIRONMENTS << TestingLibrary::Environment.new("rspec_3119_rails_1_2_6", SUPPORT_TEMP, DB_CONFIG_FILE, DB_SCHEMA_FILE) do |env|
|
||||
42c42
|
||||
< env.package "rspec", "http://rspec.rubyforge.org/svn", "trunk", "3317", &rspec_package
|
||||
---
|
||||
> env.package "rspec", "http://rspec.rubyforge.org/svn", "trunk", "3119", &rspec_package
|
||||
34
vendor/plugins/scenarios/lib/scenarios.rb
vendored
Normal file
34
vendor/plugins/scenarios/lib/scenarios.rb
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
module Scenarios
|
||||
# Thrown by Scenario.load when it cannot find a specific senario.
|
||||
class NameError < ::NameError; end
|
||||
|
||||
class << self
|
||||
# The locations from which scenarios will be loaded.
|
||||
mattr_accessor :load_paths
|
||||
self.load_paths = ["#{RAILS_ROOT}/spec/scenarios", "#{RAILS_ROOT}/test/scenarios", "#{File.dirname(__FILE__)}/scenarios/builtin"]
|
||||
|
||||
# Load a scenario by name. <tt>scenario_name</tt> can be a string, symbol,
|
||||
# or the scenario class.
|
||||
def load(scenario_name)
|
||||
klass = scenario_name.to_scenario
|
||||
klass.load
|
||||
klass
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# The Scenario namespace makes for Scenario::Base
|
||||
Scenario = Scenarios
|
||||
|
||||
# For Rails 1.2 compatibility
|
||||
unless Class.instance_methods.include?(:superclass_delegating_reader)
|
||||
require File.dirname(__FILE__) + "/scenarios/extensions/delegating_attributes"
|
||||
end
|
||||
|
||||
require 'active_record/fixtures'
|
||||
require 'scenarios/configuration'
|
||||
require 'scenarios/table_blasting'
|
||||
require 'scenarios/table_methods'
|
||||
require 'scenarios/loading'
|
||||
require 'scenarios/base'
|
||||
require 'scenarios/extensions'
|
||||
73
vendor/plugins/scenarios/lib/scenarios/base.rb
vendored
Normal file
73
vendor/plugins/scenarios/lib/scenarios/base.rb
vendored
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
module Scenarios
|
||||
class Base
|
||||
class << self
|
||||
# Class method to load the scenario. Used internally by the Scenarios
|
||||
# plugin.
|
||||
def load
|
||||
new.load_scenarios(used_scenarios + [self])
|
||||
end
|
||||
|
||||
# Class method for your own scenario to define helper methods that will
|
||||
# be included into the scenario and all specs that include the scenario
|
||||
def helpers(&block)
|
||||
mod = (const_get(:Helpers) rescue const_set(:Helpers, Module.new))
|
||||
mod.module_eval(&block) if block_given?
|
||||
mod
|
||||
end
|
||||
|
||||
# Class method for your own scenario to define the scenarios that it
|
||||
# depends on. If your scenario depends on other scenarios those
|
||||
# scenarios will be loaded before the load method on your scenario is
|
||||
# executed.
|
||||
def uses(*scenarios)
|
||||
names = scenarios.map(&:to_scenario).reject { |n| used_scenarios.include?(n) }
|
||||
used_scenarios.concat(names)
|
||||
end
|
||||
|
||||
# Class method that returns the scenarios used by your scenario.
|
||||
def used_scenarios # :nodoc:
|
||||
@used_scenarios ||= []
|
||||
@used_scenarios = (@used_scenarios.collect(&:used_scenarios) + @used_scenarios).flatten.uniq
|
||||
end
|
||||
|
||||
# Returns the scenario class.
|
||||
def to_scenario
|
||||
self
|
||||
end
|
||||
end
|
||||
|
||||
include TableMethods
|
||||
include Loading
|
||||
|
||||
attr_reader :table_config
|
||||
|
||||
# Initialize a scenario with a Configuration. Used internally by the
|
||||
# Scenarios plugin.
|
||||
def initialize(config = Configuration.new)
|
||||
@table_config = config
|
||||
table_config.update_scenario_helpers self.class
|
||||
self.extend table_config.table_readers
|
||||
self.extend table_config.scenario_helpers
|
||||
end
|
||||
|
||||
# This method should be implemented in your scenarios. You may also have
|
||||
# scenarios that simply use other scenarios, so it is not required that
|
||||
# this be overridden.
|
||||
def load
|
||||
end
|
||||
|
||||
# Unload a scenario, sort of. This really only deletes the records, all of
|
||||
# them, of every table this scenario modified. The goal is to maintain a
|
||||
# clean database for successive runs. Used internally by the Scenarios
|
||||
# plugin.
|
||||
def unload
|
||||
return if unloaded?
|
||||
record_metas.each_value { |meta| blast_table(meta.table_name) }
|
||||
@unloaded = true
|
||||
end
|
||||
|
||||
def unloaded?
|
||||
@unloaded == true
|
||||
end
|
||||
end
|
||||
end
|
||||
18
vendor/plugins/scenarios/lib/scenarios/builtin/blank_scenario.rb
vendored
Normal file
18
vendor/plugins/scenarios/lib/scenarios/builtin/blank_scenario.rb
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
class BlankScenario < Scenarios::Base
|
||||
def load
|
||||
table_names.each do |table|
|
||||
blast_table(table)
|
||||
end
|
||||
end
|
||||
|
||||
def table_names
|
||||
self.class.table_names
|
||||
end
|
||||
|
||||
def self.table_names
|
||||
@table_names ||= begin
|
||||
schema = (open(RAILS_ROOT + '/db/schema.rb') { |f| f.read } rescue '')
|
||||
schema.grep(/create_table\s+(['"])(.+?)\1/m) { $2 }
|
||||
end
|
||||
end
|
||||
end
|
||||
55
vendor/plugins/scenarios/lib/scenarios/configuration.rb
vendored
Normal file
55
vendor/plugins/scenarios/lib/scenarios/configuration.rb
vendored
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
module Scenarios
|
||||
class Configuration # :nodoc:
|
||||
attr_reader :blasted_tables, :loaded_scenarios, :record_metas, :table_readers, :scenario_helpers, :symbolic_names_to_id
|
||||
|
||||
def initialize
|
||||
@blasted_tables = Set.new
|
||||
@record_metas = Hash.new
|
||||
@table_readers = Module.new
|
||||
@scenario_helpers = Module.new
|
||||
@symbolic_names_to_id = Hash.new {|h,k| h[k] = Hash.new}
|
||||
@loaded_scenarios = Array.new
|
||||
end
|
||||
|
||||
# Given a created record (currently ScenarioModel or ScenarioRecord),
|
||||
# update the table readers module appropriately such that this record and
|
||||
# it's id are findable via methods like 'people(symbolic_name)' and
|
||||
# 'person_id(symbolic_name)'.
|
||||
def update_table_readers(record)
|
||||
ids, record_meta = symbolic_names_to_id, record.record_meta # scoping assignments
|
||||
|
||||
ids[record_meta.table_name][record.symbolic_name] = record.id
|
||||
table_readers.send :define_method, record_meta.id_reader do |*symbolic_names|
|
||||
record_ids = symbolic_names.flatten.collect do |symbolic_name|
|
||||
if symbolic_name.kind_of?(ActiveRecord::Base)
|
||||
symbolic_name.id
|
||||
else
|
||||
record_id = ids[record_meta.table_name][symbolic_name.to_sym]
|
||||
raise ActiveRecord::RecordNotFound, "No object is associated with #{record_meta.table_name}(:#{symbolic_name})" unless record_id
|
||||
record_id
|
||||
end
|
||||
end
|
||||
record_ids.size > 1 ? record_ids : record_ids.first
|
||||
end
|
||||
|
||||
table_readers.send :define_method, record_meta.record_reader do |*symbolic_names|
|
||||
results = symbolic_names.flatten.collect do |symbolic_name|
|
||||
symbolic_name.kind_of?(ActiveRecord::Base) ?
|
||||
symbolic_name :
|
||||
record_meta.record_class.find(send(record_meta.id_reader, symbolic_name))
|
||||
end
|
||||
results.size > 1 ? results : results.first
|
||||
end
|
||||
end
|
||||
|
||||
def update_scenario_helpers(scenario_class)
|
||||
scenario_helpers.module_eval do
|
||||
include scenario_class.helpers
|
||||
end
|
||||
end
|
||||
|
||||
def scenarios_loaded?
|
||||
!loaded_scenarios.blank?
|
||||
end
|
||||
end
|
||||
end
|
||||
5
vendor/plugins/scenarios/lib/scenarios/extensions.rb
vendored
Normal file
5
vendor/plugins/scenarios/lib/scenarios/extensions.rb
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
require File.dirname(__FILE__) + "/extensions/object"
|
||||
require File.dirname(__FILE__) + "/extensions/string"
|
||||
require File.dirname(__FILE__) + "/extensions/symbol"
|
||||
require File.dirname(__FILE__) + "/extensions/active_record"
|
||||
require File.dirname(__FILE__) + "/extensions/test_case" rescue nil
|
||||
14
vendor/plugins/scenarios/lib/scenarios/extensions/active_record.rb
vendored
Normal file
14
vendor/plugins/scenarios/lib/scenarios/extensions/active_record.rb
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
module ActiveRecord
|
||||
class Base
|
||||
cattr_accessor :table_config
|
||||
include Scenarios::TableBlasting
|
||||
|
||||
# In order to guarantee that tables are tracked when _create_model_ is
|
||||
# used, and those models cause other models to be created...
|
||||
def create_with_table_blasting
|
||||
prepare_table(self.class.table_name)
|
||||
create_without_table_blasting
|
||||
end
|
||||
alias_method_chain :create, :table_blasting
|
||||
end
|
||||
end
|
||||
40
vendor/plugins/scenarios/lib/scenarios/extensions/delegating_attributes.rb
vendored
Normal file
40
vendor/plugins/scenarios/lib/scenarios/extensions/delegating_attributes.rb
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# These class attributes behave something like the class
|
||||
# inheritable accessors. But instead of copying the hash over at
|
||||
# the time the subclass is first defined, the accessors simply
|
||||
# delegate to their superclass unless they have been given a
|
||||
# specific value. This stops the strange situation where values
|
||||
# set after class definition don't get applied to subclasses.
|
||||
class Class
|
||||
def superclass_delegating_reader(*names)
|
||||
class_name_to_stop_searching_on = self.superclass.name.blank? ? "Object" : self.superclass.name
|
||||
names.each do |name|
|
||||
class_eval <<-EOS
|
||||
def self.#{name}
|
||||
if defined?(@#{name})
|
||||
@#{name}
|
||||
elsif superclass < #{class_name_to_stop_searching_on} && superclass.respond_to?(:#{name})
|
||||
superclass.#{name}
|
||||
end
|
||||
end
|
||||
def #{name}
|
||||
self.class.#{name}
|
||||
end
|
||||
EOS
|
||||
end
|
||||
end
|
||||
|
||||
def superclass_delegating_writer(*names)
|
||||
names.each do |name|
|
||||
class_eval <<-EOS
|
||||
def self.#{name}=(value)
|
||||
@#{name} = value
|
||||
end
|
||||
EOS
|
||||
end
|
||||
end
|
||||
|
||||
def superclass_delegating_accessor(*names)
|
||||
superclass_delegating_reader(*names)
|
||||
superclass_delegating_writer(*names)
|
||||
end
|
||||
end
|
||||
5
vendor/plugins/scenarios/lib/scenarios/extensions/object.rb
vendored
Normal file
5
vendor/plugins/scenarios/lib/scenarios/extensions/object.rb
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
class Object
|
||||
def metaclass
|
||||
(class << self; self; end)
|
||||
end unless method_defined?(:metaclass)
|
||||
end
|
||||
22
vendor/plugins/scenarios/lib/scenarios/extensions/string.rb
vendored
Normal file
22
vendor/plugins/scenarios/lib/scenarios/extensions/string.rb
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
class String
|
||||
|
||||
# Convert a string into the associated scenario class:
|
||||
#
|
||||
# "basic".to_scenario #=> BasicScenario
|
||||
# "basic_scenario".to_scenario #=> BasicScenario
|
||||
#
|
||||
# Raises Scenario::NameError if the the scenario cannot be loacated in
|
||||
# Scenario.load_paths.
|
||||
def to_scenario
|
||||
class_name = "#{self.strip.camelize.sub(/Scenario$/, '')}Scenario"
|
||||
Scenario.load_paths.each do |path|
|
||||
filename = "#{path}/#{class_name.underscore}.rb"
|
||||
if File.file?(filename)
|
||||
require filename
|
||||
break
|
||||
end
|
||||
end
|
||||
class_name.constantize rescue raise Scenario::NameError, "Expected to find #{class_name} in #{Scenario.load_paths.inspect}"
|
||||
end
|
||||
|
||||
end
|
||||
14
vendor/plugins/scenarios/lib/scenarios/extensions/symbol.rb
vendored
Normal file
14
vendor/plugins/scenarios/lib/scenarios/extensions/symbol.rb
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
class Symbol
|
||||
|
||||
# Convert a symbol into the associated scenario class:
|
||||
#
|
||||
# :basic.to_scenario #=> BasicScenario
|
||||
# :basic_scenario.to_scenario #=> BasicScenario
|
||||
#
|
||||
# Raises Scenario::NameError if the the scenario cannot be located in
|
||||
# Scenario.load_paths.
|
||||
def to_scenario
|
||||
to_s.to_scenario
|
||||
end
|
||||
|
||||
end
|
||||
77
vendor/plugins/scenarios/lib/scenarios/extensions/test_case.rb
vendored
Normal file
77
vendor/plugins/scenarios/lib/scenarios/extensions/test_case.rb
vendored
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
module Test #:nodoc:
|
||||
module Unit #:nodoc:
|
||||
class TestCase #:nodoc:
|
||||
superclass_delegating_accessor :scenario_classes
|
||||
superclass_delegating_accessor :table_config
|
||||
|
||||
# Changing either of these is not supported at this time.
|
||||
self.use_transactional_fixtures = true
|
||||
self.use_instantiated_fixtures = false
|
||||
|
||||
include Scenarios::TableMethods
|
||||
include Scenarios::Loading
|
||||
|
||||
class << self
|
||||
# This class method is mixed into RSpec and allows you to declare that
|
||||
# you are using a given scenario or set of scenarios within a spec:
|
||||
#
|
||||
# scenario :basic # loads BasicScenario and any dependencies
|
||||
# scenario :posts, :comments # loads PostsScenario and CommentsScenario
|
||||
#
|
||||
# It accepts an array of scenarios (strings, symbols, or classes) and
|
||||
# will load them roughly in the order that they are specified.
|
||||
def scenario(*names)
|
||||
self.scenario_classes = []
|
||||
names.each do |name|
|
||||
scenario_class = name.to_scenario
|
||||
scenario_classes.concat(scenario_class.used_scenarios + [scenario_class])
|
||||
end
|
||||
scenario_classes.uniq!
|
||||
end
|
||||
|
||||
# Overridden to provide before all and after all code which sets up and
|
||||
# tears down scenarios
|
||||
def suite_with_scenarios
|
||||
suite = suite_without_scenarios
|
||||
class << suite
|
||||
attr_accessor :test_class
|
||||
def run_with_scenarios(*args, &block)
|
||||
debugger
|
||||
run_without_scenarios(*args, &block)
|
||||
test_class.table_config.loaded_scenarios.each { |s| s.unload } if test_class.table_config
|
||||
end
|
||||
alias_method_chain :run, :scenarios
|
||||
end
|
||||
suite.test_class = self
|
||||
suite
|
||||
end
|
||||
alias_method_chain :suite, :scenarios
|
||||
end
|
||||
|
||||
# Hook into fixtures loading lifecycle to instead load scenarios. This
|
||||
# is expected to be called in a fashion respective of
|
||||
# use_transactional_fixtures. I feel like a leech.
|
||||
def load_fixtures
|
||||
if !scenarios_loaded? || !use_transactional_fixtures?
|
||||
self.class.table_config = Scenarios::Configuration.new if !use_transactional_fixtures? || table_config.nil?
|
||||
load_scenarios(scenario_classes)
|
||||
end
|
||||
self.extend scenario_helpers
|
||||
self.extend table_readers
|
||||
end
|
||||
|
||||
# Here we are counting on existing logic to allow teardown method
|
||||
# overriding as done in fixtures.rb. Only if transaction fixtures are
|
||||
# not being used do we unload scenarios after a test. Otherwise, we wait
|
||||
# until the end of the run of all tests on this test_case (collection of
|
||||
# tests, right!). See the TestSuite extension done in _suite_ for
|
||||
# behaviour when using transaction fixtures.
|
||||
def teardown_with_scenarios
|
||||
teardown_without_scenarios
|
||||
loaded_scenarios.each { |s| s.unload } unless use_transactional_fixtures?
|
||||
end
|
||||
alias_method_chain :teardown, :scenarios
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
51
vendor/plugins/scenarios/lib/scenarios/loading.rb
vendored
Normal file
51
vendor/plugins/scenarios/lib/scenarios/loading.rb
vendored
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
module Scenarios
|
||||
# Provides scenario loading and convenience methods around the Configuration
|
||||
# that must be made available through a method _table_config_.
|
||||
module Loading # :nodoc:
|
||||
def load_scenarios(scenario_classes)
|
||||
install_active_record_tracking_hook
|
||||
scenario_classes.each do |scenario_class|
|
||||
scenario = scenario_class.new(table_config)
|
||||
scenario.load
|
||||
table_config.loaded_scenarios << scenario
|
||||
end if scenario_classes
|
||||
end
|
||||
|
||||
def loaded_scenarios
|
||||
table_config.loaded_scenarios
|
||||
end
|
||||
|
||||
def scenarios_loaded?
|
||||
table_config && table_config.scenarios_loaded?
|
||||
end
|
||||
|
||||
# The sum of all the loaded scenario's helper methods. These can be mixed
|
||||
# into anything you like to gain access to them.
|
||||
def scenario_helpers
|
||||
table_config.scenario_helpers
|
||||
end
|
||||
|
||||
# The sum of all the available table reading methods. These will only
|
||||
# include readers for which data has been placed into the table. These can
|
||||
# be mixed into anything you like to gain access to them.
|
||||
def table_readers
|
||||
table_config.table_readers
|
||||
end
|
||||
|
||||
# # This understand nesting descriptions one deep
|
||||
# def table_config
|
||||
# on_my_class = self.class.instance_variable_get("@table_config")
|
||||
# return on_my_class if on_my_class
|
||||
#
|
||||
# if self.class.superclass
|
||||
# on_super_class = self.class.superclass.instance_variable_get("@table_config")
|
||||
# return on_super_class if on_super_class
|
||||
# end
|
||||
# end
|
||||
|
||||
private
|
||||
def install_active_record_tracking_hook
|
||||
ActiveRecord::Base.table_config = table_config
|
||||
end
|
||||
end
|
||||
end
|
||||
20
vendor/plugins/scenarios/lib/scenarios/table_blasting.rb
vendored
Normal file
20
vendor/plugins/scenarios/lib/scenarios/table_blasting.rb
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
module Scenarios
|
||||
module TableBlasting
|
||||
def self.included(base)
|
||||
base.module_eval do
|
||||
delegate :blasted_tables, :to => :table_config
|
||||
end
|
||||
end
|
||||
|
||||
def blast_table(name) # :nodoc:
|
||||
ActiveRecord::Base.silence do
|
||||
ActiveRecord::Base.connection.delete "DELETE FROM #{name}", "Scenario Delete"
|
||||
end
|
||||
blasted_tables << name
|
||||
end
|
||||
|
||||
def prepare_table(name)
|
||||
blast_table(name) unless blasted_tables.include?(name)
|
||||
end
|
||||
end
|
||||
end
|
||||
205
vendor/plugins/scenarios/lib/scenarios/table_methods.rb
vendored
Normal file
205
vendor/plugins/scenarios/lib/scenarios/table_methods.rb
vendored
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
module Scenarios
|
||||
# This helper module contains the #create_record method. It is made
|
||||
# available to all Scenario instances, test and example classes, and test
|
||||
# and example instances.
|
||||
module TableMethods
|
||||
include TableBlasting
|
||||
|
||||
delegate :record_metas, :to => :table_config
|
||||
|
||||
# Insert a record into the database, add the appropriate helper methods
|
||||
# into the scenario and spec, and return the ID of the inserted record:
|
||||
#
|
||||
# create_record :event, :name => "Ruby Hoedown"
|
||||
# create_record Event, :hoedown, :name => "Ruby Hoedown"
|
||||
#
|
||||
# The first form will create a new record in the given class identifier
|
||||
# and no symbolic name (essentially).
|
||||
#
|
||||
# The second form is exactly like the first, except for that it provides a
|
||||
# symbolic name as the second parameter. The symbolic name will allow you
|
||||
# to access the record through a couple of helper methods:
|
||||
#
|
||||
# events(:hoedown) # The hoedown event
|
||||
# event_id(:hoedown) # The ID of the hoedown event
|
||||
#
|
||||
# These helper methods are only accessible for a particular table after
|
||||
# you have inserted a record into that table using <tt>create_record</tt>.
|
||||
def create_record(class_identifier, *args)
|
||||
insert(ScenarioRecord, class_identifier, *args) do |record|
|
||||
meta = record.record_meta
|
||||
fixture = record.to_fixture
|
||||
begin
|
||||
meta.connection.insert_fixture(fixture, record.record_meta.table_name)
|
||||
rescue # Rails 1.2 compatible!
|
||||
meta.connection.execute "INSERT INTO #{meta.table_name} (#{fixture.key_list}) VALUES (#{fixture.value_list})"
|
||||
end
|
||||
record.id
|
||||
end
|
||||
end
|
||||
|
||||
# Instantiate and save! a model, add the appropriate helper methods into
|
||||
# the scenario and spec, and return the new model instance:
|
||||
#
|
||||
# create_model :event, :name => "Ruby Hoedown"
|
||||
# create_model Event, :hoedown, :name => "Ruby Hoedown"
|
||||
#
|
||||
# The first form will create a new model with no symbolic name
|
||||
# (essentially).
|
||||
#
|
||||
# The second form is exactly like the first, except for that it provides a
|
||||
# symbolic name as the second parameter. The symbolic name will allow you
|
||||
# to access the record through a couple of helper methods:
|
||||
#
|
||||
# events(:hoedown) # The hoedown event
|
||||
# event_id(:hoedown) # The ID of the hoedown event
|
||||
#
|
||||
# These helper methods are only accessible for a particular table after
|
||||
# you have inserted a record into that table using <tt>create_model</tt>.
|
||||
def create_model(class_identifier, *args)
|
||||
insert(ScenarioModel, class_identifier, *args) do |record|
|
||||
model = record.to_model
|
||||
model.save!
|
||||
model
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def insert(record_or_model, class_identifier, *args, &insertion)
|
||||
symbolic_name, attributes = extract_creation_arguments(args)
|
||||
record_meta = (record_metas[class_identifier] ||= RecordMeta.new(class_identifier))
|
||||
record = record_or_model.new(record_meta, attributes, symbolic_name)
|
||||
return_value = nil
|
||||
ActiveRecord::Base.silence do
|
||||
prepare_table(record_meta.table_name)
|
||||
return_value = insertion.call record
|
||||
table_config.update_table_readers(record)
|
||||
self.extend table_config.table_readers
|
||||
end
|
||||
return_value
|
||||
end
|
||||
|
||||
def extract_creation_arguments(arguments)
|
||||
if arguments.size == 2 && arguments.last.kind_of?(Hash)
|
||||
arguments
|
||||
elsif arguments.size == 1 && arguments.last.kind_of?(Hash)
|
||||
[nil, arguments[0]]
|
||||
else
|
||||
[nil, Hash.new]
|
||||
end
|
||||
end
|
||||
|
||||
class RecordMeta # :nodoc:
|
||||
attr_reader :class_name, :record_class, :table_name
|
||||
|
||||
def initialize(class_identifier)
|
||||
@class_identifier = class_identifier
|
||||
@class_name = resolve_class_name(class_identifier)
|
||||
@record_class = class_name.constantize
|
||||
@table_name = record_class.table_name
|
||||
end
|
||||
|
||||
def timestamp_columns
|
||||
@timestamp_columns ||= begin
|
||||
timestamps = %w(created_at created_on updated_at updated_on)
|
||||
columns.select do |column|
|
||||
timestamps.include?(column.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def columns
|
||||
@columns ||= connection.columns(table_name)
|
||||
end
|
||||
|
||||
def connection
|
||||
record_class.connection
|
||||
end
|
||||
|
||||
def id_reader
|
||||
@id_reader ||= begin
|
||||
reader = ActiveRecord::Base.pluralize_table_names ? table_name.singularize : table_name
|
||||
"#{reader}_id".to_sym
|
||||
end
|
||||
end
|
||||
|
||||
def record_reader
|
||||
table_name.to_sym
|
||||
end
|
||||
|
||||
def resolve_class_name(class_identifier)
|
||||
case class_identifier
|
||||
when Symbol
|
||||
class_identifier.to_s.singularize.camelize
|
||||
when Class
|
||||
class_identifier.name
|
||||
when String
|
||||
class_identifier
|
||||
end
|
||||
end
|
||||
|
||||
def to_s
|
||||
"#<RecordMeta: #{table_name}>"
|
||||
end
|
||||
end
|
||||
|
||||
class ScenarioModel # :nodoc:
|
||||
attr_reader :attributes, :model, :record_meta, :symbolic_name
|
||||
delegate :id, :to => :model
|
||||
|
||||
def initialize(record_meta, attributes, symbolic_name = nil)
|
||||
@record_meta = record_meta
|
||||
@attributes = attributes.stringify_keys
|
||||
@symbolic_name = symbolic_name || object_id
|
||||
end
|
||||
|
||||
def to_hash
|
||||
to_model.attributes
|
||||
end
|
||||
|
||||
def to_model
|
||||
@model ||= record_meta.record_class.new(attributes)
|
||||
end
|
||||
end
|
||||
|
||||
class ScenarioRecord # :nodoc:
|
||||
attr_reader :record_meta, :symbolic_name
|
||||
|
||||
def initialize(record_meta, attributes, symbolic_name = nil)
|
||||
@record_meta = record_meta
|
||||
@attributes = attributes.stringify_keys
|
||||
@symbolic_name = symbolic_name || object_id
|
||||
|
||||
install_default_attributes!
|
||||
end
|
||||
|
||||
def id
|
||||
@attributes['id']
|
||||
end
|
||||
|
||||
def to_hash
|
||||
@attributes
|
||||
end
|
||||
|
||||
def to_fixture
|
||||
Fixture.new(to_hash, record_meta.class_name)
|
||||
end
|
||||
|
||||
def install_default_attributes!
|
||||
@attributes['id'] ||= symbolic_name.to_s.hash.abs
|
||||
install_timestamps!
|
||||
end
|
||||
|
||||
def install_timestamps!
|
||||
record_meta.timestamp_columns.each do |column|
|
||||
@attributes[column.name] = now(column) unless @attributes.key?(column.name)
|
||||
end
|
||||
end
|
||||
|
||||
def now(column)
|
||||
now = ActiveRecord::Base.default_timezone == :utc ? column.klass.now.utc : column.klass.now
|
||||
now.to_s(:db)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
9
vendor/plugins/scenarios/spec/scenarios/complex_composite_scenario.rb
vendored
Normal file
9
vendor/plugins/scenarios/spec/scenarios/complex_composite_scenario.rb
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
class ComplexCompositeScenario < Scenario::Base
|
||||
uses :composite, :places
|
||||
|
||||
helpers do
|
||||
def method_from_complex_composite_scenario
|
||||
:method_from_complex_composite_scenario
|
||||
end
|
||||
end
|
||||
end
|
||||
9
vendor/plugins/scenarios/spec/scenarios/composite_scenario.rb
vendored
Normal file
9
vendor/plugins/scenarios/spec/scenarios/composite_scenario.rb
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
class CompositeScenario < Scenario::Base
|
||||
uses :people, :things
|
||||
|
||||
helpers do
|
||||
def method_from_composite_scenario
|
||||
:method_from_composite_scenario
|
||||
end
|
||||
end
|
||||
end
|
||||
4
vendor/plugins/scenarios/spec/scenarios/empty_scenario.rb
vendored
Normal file
4
vendor/plugins/scenarios/spec/scenarios/empty_scenario.rb
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
class EmptyScenario < Scenario::Base
|
||||
def load
|
||||
end
|
||||
end
|
||||
26
vendor/plugins/scenarios/spec/scenarios/people_scenario.rb
vendored
Normal file
26
vendor/plugins/scenarios/spec/scenarios/people_scenario.rb
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
class PeopleScenario < Scenario::Base
|
||||
|
||||
def load
|
||||
create_person "John Long"
|
||||
create_person "Adam Williams"
|
||||
end
|
||||
|
||||
helpers do
|
||||
def create_person(attributes = {})
|
||||
if attributes.kind_of?(String)
|
||||
first, last = attributes.split(/\s+/)
|
||||
attributes = { :first_name => first, :last_name => last }
|
||||
end
|
||||
attributes = person_params(attributes)
|
||||
create_record(:person, attributes[:first_name].strip.gsub(' ', '_').underscore.to_sym, attributes)
|
||||
end
|
||||
|
||||
def person_params(attributes = {})
|
||||
attributes = {
|
||||
:first_name => "John",
|
||||
:last_name => "Q."
|
||||
}.update(attributes)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
22
vendor/plugins/scenarios/spec/scenarios/places_scenario.rb
vendored
Normal file
22
vendor/plugins/scenarios/spec/scenarios/places_scenario.rb
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
class PlacesScenario < Scenario::Base
|
||||
|
||||
def load
|
||||
create_place "Taj Mahal", "India"
|
||||
create_place "Whitehouse", "Washington DC"
|
||||
end
|
||||
|
||||
helpers do
|
||||
def create_place(name, location)
|
||||
attributes = place_params(:name => name, :location => location)
|
||||
create_record(:place, name.strip.gsub(' ', '_').underscore.to_sym, attributes)
|
||||
end
|
||||
|
||||
def place_params(attributes = {})
|
||||
attributes = {
|
||||
:name => "Noplace",
|
||||
:location => "Nowhere"
|
||||
}.update(attributes)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
22
vendor/plugins/scenarios/spec/scenarios/things_scenario.rb
vendored
Normal file
22
vendor/plugins/scenarios/spec/scenarios/things_scenario.rb
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
class ThingsScenario < Scenario::Base
|
||||
|
||||
def load
|
||||
create_thing "one"
|
||||
create_thing "two"
|
||||
end
|
||||
|
||||
helpers do
|
||||
def create_thing(attributes = {})
|
||||
attributes = { :name => attributes } if attributes.kind_of?(String)
|
||||
attributes = thing_params(attributes)
|
||||
create_record(:thing, attributes[:name].strip.gsub(' ', '_').underscore.to_sym, attributes)
|
||||
end
|
||||
|
||||
def thing_params(attributes = {})
|
||||
attributes = {
|
||||
:name => "Unnamed Thing",
|
||||
:description => "I'm not sure what this is."
|
||||
}.update(attributes)
|
||||
end
|
||||
end
|
||||
end
|
||||
185
vendor/plugins/scenarios/spec/scenarios_spec.rb
vendored
Normal file
185
vendor/plugins/scenarios/spec/scenarios_spec.rb
vendored
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
require File.expand_path(File.dirname(__FILE__) + "/spec_helper")
|
||||
|
||||
class ExplodesOnSecondInstantiationScenario < Scenario::Base
|
||||
cattr_accessor :instance
|
||||
def initialize(*args)
|
||||
raise "Should only be created once" if self.class.instance
|
||||
self.class.instance = super(*args)
|
||||
end
|
||||
end
|
||||
|
||||
describe "Scenario loading" do
|
||||
scenario :explodes_on_second_instantiation
|
||||
|
||||
it "should work" do
|
||||
end
|
||||
|
||||
it 'should work again' do
|
||||
end
|
||||
end
|
||||
|
||||
describe "Scenario loading" do
|
||||
it "should load from configured directories" do
|
||||
Scenario.load(:empty)
|
||||
EmptyScenario
|
||||
end
|
||||
|
||||
it "should raise Scenario::NameError when the scenario does not exist" do
|
||||
lambda { Scenario.load(:whatcha_talkin_bout) }.should raise_error(Scenario::NameError)
|
||||
end
|
||||
|
||||
it "should allow us to add helper methods through the helpers class method" do
|
||||
klass = :empty.to_scenario
|
||||
klass.helpers do
|
||||
def hello
|
||||
"Hello World"
|
||||
end
|
||||
end
|
||||
klass.new.methods.should include('hello')
|
||||
end
|
||||
|
||||
it "should provide a built-in scenario named :blank which clears all tables found in schema.rb" do
|
||||
Scenario.load(:blank)
|
||||
BlankScenario
|
||||
end
|
||||
end
|
||||
|
||||
describe Scenarios::TableMethods do
|
||||
scenario :things
|
||||
|
||||
it "should understand namespaced models" do
|
||||
create_record "ModelModule::Model", :raking, :name => "Raking", :description => "Moving leaves around"
|
||||
models(:raking).should_not be_nil
|
||||
end
|
||||
|
||||
it "should include record creation methods" do
|
||||
create_record(:thing, :three, :name => "Three")
|
||||
things(:three).name.should == "Three"
|
||||
end
|
||||
|
||||
it "should include other example helper methods" do
|
||||
create_thing("The Thing")
|
||||
things(:the_thing).name.should == "The Thing"
|
||||
end
|
||||
|
||||
describe "for retrieving objects" do
|
||||
it "should have a pluralized name" do
|
||||
should respond_to("things")
|
||||
should_not respond_to("thing")
|
||||
end
|
||||
|
||||
it "should answer a single object given a single name" do
|
||||
things(:one).should be_kind_of(Thing)
|
||||
things("one").should be_kind_of(Thing)
|
||||
things(:two).name.should == "two"
|
||||
end
|
||||
|
||||
it "should answer an array of objects given multiple names" do
|
||||
things(:one, :two).should be_kind_of(Array)
|
||||
things(:one, :two).should eql([things(:one), things(:two)])
|
||||
end
|
||||
|
||||
it "should just return the argument if an AR instance is given" do
|
||||
thing = things(:one)
|
||||
things(thing).should eql(thing)
|
||||
end
|
||||
end
|
||||
|
||||
describe "for retrieving ids" do
|
||||
it "should have a singular name" do
|
||||
should respond_to("thing_id")
|
||||
should_not respond_to("thing_ids")
|
||||
should_not respond_to("things_id")
|
||||
end
|
||||
|
||||
it "should answer a single id given a single name" do
|
||||
thing_id(:one).should be_kind_of(Fixnum)
|
||||
thing_id("one").should be_kind_of(Fixnum)
|
||||
end
|
||||
|
||||
it "should answer an array of ids given multiple names" do
|
||||
thing_id(:one, :two).should be_kind_of(Array)
|
||||
thing_id(:one, :two).should eql([thing_id(:one), thing_id(:two)])
|
||||
thing_id("one", "two").should eql([thing_id(:one), thing_id(:two)])
|
||||
end
|
||||
|
||||
it "should answer the id of the argument if an AR instance id given" do
|
||||
thing = things(:one)
|
||||
thing_id(thing).should == thing.id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "it uses people and things scenarios", :shared => true do
|
||||
it "should have reader helper methods for each used scenario" do
|
||||
should respond_to(:things)
|
||||
should respond_to(:people)
|
||||
end
|
||||
|
||||
it "should allow us to use helper methods from each scenario inside an example" do
|
||||
should respond_to(:create_thing)
|
||||
should respond_to(:create_person)
|
||||
end
|
||||
end
|
||||
|
||||
describe "A composite scenario" do
|
||||
scenario :composite
|
||||
|
||||
it_should_behave_like "it uses people and things scenarios"
|
||||
|
||||
it "should allow us to use helper methods scenario" do
|
||||
should respond_to(:method_from_composite_scenario)
|
||||
end
|
||||
end
|
||||
|
||||
describe "Multiple scenarios" do
|
||||
scenario :things, :people
|
||||
|
||||
it_should_behave_like "it uses people and things scenarios"
|
||||
end
|
||||
|
||||
describe "A complex composite scenario" do
|
||||
scenario :complex_composite
|
||||
|
||||
it_should_behave_like "it uses people and things scenarios"
|
||||
|
||||
it "should have correct reader helper methods" do
|
||||
should respond_to(:places)
|
||||
end
|
||||
|
||||
it "should allow us to use correct helper methods" do
|
||||
should respond_to(:create_place)
|
||||
end
|
||||
end
|
||||
|
||||
describe "Overlapping scenarios" do
|
||||
scenario :composite, :things, :people
|
||||
|
||||
it "should not cause scenarios to be loaded twice" do
|
||||
Person.find_all_by_first_name("John").size.should == 1
|
||||
end
|
||||
end
|
||||
|
||||
describe "create_record table method" do
|
||||
scenario :empty
|
||||
|
||||
it "should automatically set timestamps" do
|
||||
create_record :note, :first, :content => "first note"
|
||||
note = notes(:first)
|
||||
note.created_at.should be_instance_of(Time)
|
||||
end
|
||||
end
|
||||
|
||||
describe "create_model table method" do
|
||||
scenario :empty
|
||||
|
||||
it "should support symbolic names" do
|
||||
thing = create_model Thing, :mything, :name => "My Thing", :description => "For testing"
|
||||
things(:mything).should == thing
|
||||
end
|
||||
|
||||
it "should blast any table touched as a side effect of creating a model (callbacks, observers, etc.)" do
|
||||
create_model SideEffectyThing
|
||||
blasted_tables.should include(Thing.table_name)
|
||||
end
|
||||
end
|
||||
7
vendor/plugins/scenarios/spec/spec.opts
vendored
Normal file
7
vendor/plugins/scenarios/spec/spec.opts
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
--colour
|
||||
--format
|
||||
progress
|
||||
--loadby
|
||||
mtime
|
||||
--reverse
|
||||
--backtrace
|
||||
24
vendor/plugins/scenarios/spec/spec_helper.rb
vendored
Normal file
24
vendor/plugins/scenarios/spec/spec_helper.rb
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
require File.expand_path(File.dirname(__FILE__) + '/../testing/plugit_descriptor')
|
||||
|
||||
TESTING_ROOT = File.expand_path("#{File.dirname(__FILE__)}/../testing")
|
||||
TESTING_TMP = "#{TESTING_ROOT}/tmp"
|
||||
|
||||
require 'fileutils'
|
||||
FileUtils.mkdir_p(TESTING_TMP)
|
||||
FileUtils.touch("#{TESTING_TMP}/test.log")
|
||||
|
||||
require 'logger'
|
||||
RAILS_DEFAULT_LOGGER = Logger.new("#{TESTING_TMP}/test.log")
|
||||
RAILS_DEFAULT_LOGGER.level = Logger::DEBUG
|
||||
|
||||
ActiveRecord::Base.silence do
|
||||
ActiveRecord::Base.configurations = {'sqlite3' => {
|
||||
'adapter' => 'sqlite3',
|
||||
'database' => "#{TESTING_TMP}/sqlite3.db"
|
||||
}}
|
||||
ActiveRecord::Base.establish_connection 'sqlite3'
|
||||
load "#{TESTING_ROOT}/schema.rb"
|
||||
end
|
||||
|
||||
require "models"
|
||||
require "scenarios"
|
||||
19
vendor/plugins/scenarios/tasks/scenario.rake
vendored
Normal file
19
vendor/plugins/scenarios/tasks/scenario.rake
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
namespace :db do
|
||||
namespace :scenario do
|
||||
desc "Load a scenario into the current environment's database using SCENARIO=scenario_name"
|
||||
task :load => 'db:reset' do
|
||||
scenario_name = ENV['SCENARIO'] || 'default'
|
||||
begin
|
||||
klass = Scenarios.load(scenario_name)
|
||||
puts "Loaded #{klass.name.underscore.gsub('_', ' ')}."
|
||||
rescue Scenarios::NameError => e
|
||||
if scenario_name == 'default'
|
||||
puts "Error! Set the SCENARIO environment variable or define a DefaultScenario class."
|
||||
else
|
||||
puts "Error! Invalid scenario name [#{scenario_name}]."
|
||||
end
|
||||
exit(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
2
vendor/plugins/scenarios/testing/application.rb
vendored
Normal file
2
vendor/plugins/scenarios/testing/application.rb
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
class ApplicationController < ActionController::Base
|
||||
end
|
||||
14
vendor/plugins/scenarios/testing/models.rb
vendored
Normal file
14
vendor/plugins/scenarios/testing/models.rb
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
class Person < ActiveRecord::Base; end
|
||||
class Place < ActiveRecord::Base; end
|
||||
class Thing < ActiveRecord::Base; end
|
||||
class Note < ActiveRecord::Base; end
|
||||
|
||||
class SideEffectyThing < ActiveRecord::Base
|
||||
after_create do
|
||||
Thing.create!
|
||||
end
|
||||
end
|
||||
|
||||
module ModelModule
|
||||
class Model < ActiveRecord::Base; end
|
||||
end
|
||||
44
vendor/plugins/scenarios/testing/plugit_descriptor.rb
vendored
Normal file
44
vendor/plugins/scenarios/testing/plugit_descriptor.rb
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
require 'rubygems'
|
||||
gem 'plugit'
|
||||
require 'plugit'
|
||||
|
||||
$LOAD_PATH << File.expand_path("#{File.dirname(__FILE__)}/../lib")
|
||||
$LOAD_PATH << File.expand_path(File.dirname(__FILE__))
|
||||
RAILS_ROOT = File.expand_path("#{File.dirname(__FILE__)}/..")
|
||||
|
||||
Plugit.describe do |scenarios|
|
||||
scenarios.environments_root_path = File.dirname(__FILE__) + '/environments'
|
||||
vendor_directory = File.expand_path(File.dirname(__FILE__) + '/../vendor/plugins')
|
||||
|
||||
scenarios.environment :default, 'Released versions of Rails and RSpec' do |env|
|
||||
env.library :rails, :export => "git clone git://github.com/rails/rails.git" do |rails|
|
||||
rails.after_update { `git co v2.1.0_RC1` }
|
||||
rails.load_paths = %w{/activesupport/lib /activerecord/lib /actionpack/lib}
|
||||
rails.requires = %w{active_support active_record action_controller action_view}
|
||||
end
|
||||
env.library :rspec, :export => "git clone git://github.com/dchelimsky/rspec.git" do |rspec|
|
||||
rspec.after_update { `git co 1.1.4 && mkdir -p #{vendor_directory} && ln -sF #{File.expand_path('.')} #{vendor_directory + '/rspec'}` }
|
||||
rspec.requires = %w{spec}
|
||||
end
|
||||
env.library :rspec_rails, :export => "git clone git://github.com/dchelimsky/rspec-rails.git" do |rspec_rails|
|
||||
rspec_rails.after_update { `git co 1.1.4` }
|
||||
rspec_rails.requires = %w{spec/rails}
|
||||
end
|
||||
end
|
||||
|
||||
scenarios.environment :edge, 'Edge versions of Rails and RSpec' do |env|
|
||||
env.library :rails, :export => "git clone git://github.com/rails/rails.git --depth 1" do |rails|
|
||||
rails.before_install { `git pull` }
|
||||
rails.load_paths = %w{/activesupport/lib /activerecord/lib /actionpack/lib}
|
||||
rails.requires = %w{active_support active_record action_controller action_view}
|
||||
end
|
||||
env.library :rspec, :export => "git clone git://github.com/dchelimsky/rspec.git --depth 1" do |rspec|
|
||||
rspec.after_update { `git pull && mkdir -p #{vendor_directory} && ln -sF #{File.expand_path('.')} #{vendor_directory + '/rspec'}` }
|
||||
rspec.requires = %w{spec}
|
||||
end
|
||||
env.library :rspec_rails, :export => "git clone git://github.com/dchelimsky/rspec-rails.git --depth 1" do |rspec_rails|
|
||||
rspec_rails.after_update { `git pull` }
|
||||
rspec_rails.requires = %w{spec/rails}
|
||||
end
|
||||
end
|
||||
end
|
||||
31
vendor/plugins/scenarios/testing/schema.rb
vendored
Normal file
31
vendor/plugins/scenarios/testing/schema.rb
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
ActiveRecord::Schema.define do
|
||||
create_table :people, :force => true do |t|
|
||||
t.column :first_name, :string
|
||||
t.column :last_name, :string
|
||||
end
|
||||
|
||||
create_table :places, :force => true do |t|
|
||||
t.column :name, :string
|
||||
t.column :location, :string
|
||||
end
|
||||
|
||||
create_table :things, :force => true do |t|
|
||||
t.column :name, :string
|
||||
t.column :description, :string
|
||||
end
|
||||
|
||||
create_table :side_effecty_things, :force => true do |t|
|
||||
end
|
||||
|
||||
create_table :models, :force => true do |t|
|
||||
t.column :name, :string
|
||||
t.column :description, :string
|
||||
end
|
||||
|
||||
create_table :notes, :force => true do |t|
|
||||
t.column :content, :string
|
||||
t.column :created_at, :datetime
|
||||
t.column :updated_at, :datetime
|
||||
end
|
||||
end
|
||||
|
||||
2
vendor/plugins/skinny_spec/.gitignore
vendored
Normal file
2
vendor/plugins/skinny_spec/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
.DS_Store
|
||||
doc
|
||||
267
vendor/plugins/skinny_spec/README.rdoc
vendored
Normal file
267
vendor/plugins/skinny_spec/README.rdoc
vendored
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
= Skinny Spec
|
||||
|
||||
Skinny Spec is a collection of spec helper methods designed to help trim the fat and DRY up
|
||||
some of the bloat that sometimes results from properly specing your classes and templates.
|
||||
|
||||
== Requirements and Recommendations
|
||||
|
||||
Obviously you'll need to be using RSpec[http://github.com/dchelimsky/rspec/tree/master] and
|
||||
Rspec-Rails[http://github.com/dchelimsky/rspec-rails/tree/master] as your testing framework.
|
||||
|
||||
Skinny Spec was originally designed [and best enjoyed] if you're using
|
||||
Haml[http://github.com/nex3/haml/tree/master] and
|
||||
make_resourceful[http://github.com/rsl/make_resourceful/tree/master] but will default to
|
||||
ERb and a facsimile of Rails' default scaffolding [for the views and controllers, respectively]
|
||||
if Haml and/or make_resourceful are not available. I recommend using them though. :)
|
||||
|
||||
In addition, Skinny Spec uses Ruby2Ruby to make nicer expectation messages and you'll want to
|
||||
have that installed as well. It's not a dependency or anything but it <i>is</i> highly
|
||||
recommended.
|
||||
|
||||
== Setup
|
||||
|
||||
Once you've installed the plugin in your app's vendor/plugins folder, you're ready to rock!
|
||||
Skinny Spec includes itself into the proper RSpec classes so there's no configuration on your
|
||||
part. Sweet!
|
||||
|
||||
== Usage
|
||||
|
||||
The simplest way to use Skinny Specs is to generate a resource scaffold:
|
||||
|
||||
script/generate skinny_scaffold User
|
||||
|
||||
This command takes the usual complement of attribute definitions like
|
||||
<tt>script/generate scaffold</tt>. Then have a look at the generated files (particularly the
|
||||
specs) to see what's new and different with Skinny Spec.
|
||||
|
||||
=== Controller Specs
|
||||
|
||||
Let's look at the controller specs.
|
||||
|
||||
describe UsersController do
|
||||
def valid_attributes(args = {})
|
||||
{
|
||||
# Add valid attributes for the your params[:user] here!
|
||||
}.merge(args)
|
||||
end
|
||||
|
||||
describe "GET :index" do
|
||||
before(:each) do
|
||||
@users = stub_index(User)
|
||||
end
|
||||
|
||||
it_should_find_and_assign :users
|
||||
it_should_render :template, "index"
|
||||
end
|
||||
|
||||
# ...
|
||||
|
||||
describe "POST :create" do
|
||||
describe "when successful" do
|
||||
before(:each) do
|
||||
@user = stub_create(User)
|
||||
end
|
||||
|
||||
it_should_initialize_and_save :user
|
||||
it_should_redirect_to { user_url(@user) }
|
||||
end
|
||||
|
||||
# ...
|
||||
|
||||
First thing you should see is a method definition for
|
||||
<tt>valid_attributes</tt>. This will be used later by the <tt>create</tt> and <tt>update</tt>
|
||||
specs to more accurately represent how the controller works in actual practice by supplying
|
||||
somewhat real data for the <tt>params</tt> coming from the HTML forms.
|
||||
|
||||
Next we find an example group for <tt>GET :index</tt>. That <tt>stub_index</tt> method there
|
||||
does a lot of work behind the curtain. I'll leave it up to you to check the documentation for it
|
||||
(and its brothers and sister methods like <tt>stub_new</tt>) but I will point out that the
|
||||
methods named <tt>stub_<i>controller_method</i></tt> should only be used for stubbing and
|
||||
mocking the main object of the method. To create mocks for other ancillary objects, please
|
||||
use <tt>stub_find_all</tt>, <tt>stub_find_one</tt>, and <tt>stub_initialize</tt>. The reason
|
||||
for this is because the former methods actually save us a step by defining an implicit
|
||||
controller method request. If you add a new method to your resource routing, you'll want to
|
||||
use the helper method <tt>define_request</tt> in those example groups to define an explicit
|
||||
request. You can also define a method called <tt>shared_request</tt> to "share a
|
||||
<tt>define_request</tt>" across shared describe blocks, like so:
|
||||
|
||||
describe "POST :create" do
|
||||
def shared_request
|
||||
post :create
|
||||
end
|
||||
|
||||
describe "when successful" do
|
||||
# ...
|
||||
end
|
||||
|
||||
describe "when unsuccessful" do
|
||||
# ...
|
||||
end
|
||||
end
|
||||
|
||||
Note: When you're adding longer, more complicated controller specs you can still leverage
|
||||
implicit and shared requests by calling <tt>do_request</tt> in your spec as in the following
|
||||
example:
|
||||
|
||||
# Let's assume this controller is _not_ CategoriesController
|
||||
# and that loading the categories isn't part of the default actions
|
||||
describe "GET :index" do
|
||||
before(:each) do
|
||||
@categories = stub_find_all(Category)
|
||||
end
|
||||
|
||||
it "should preload categories" do
|
||||
Category.should_receive(:find).with(:all)
|
||||
do_request
|
||||
end
|
||||
|
||||
it "should assign @categories" do
|
||||
do_request
|
||||
assigns[:categories].should == @categories
|
||||
end
|
||||
end
|
||||
|
||||
Finally we get to the meat of the spec and of Skinny Specs itself: the actual expectations.
|
||||
The first thing you'll notice is the use of example group (read: "describe" block) level methods
|
||||
instead of the usual example (read: "it") blocks. Using this helper at the example group level
|
||||
saves us three lines over using an example block. (If this isn't significant to you, this is
|
||||
probably the wrong plugin for you as well. Sorry.) Note that none of these methods use the
|
||||
instance variables defined in the "before" block because they are all nil at the example block
|
||||
level. Let's look at a sample method to see how it works:
|
||||
|
||||
it_should_find_and_assign :users
|
||||
|
||||
This actually wraps two different expectations: one that <tt>User.should_receive(:find).with(:all)</tt>
|
||||
and another that the instance variable <tt>@users</tt> is assigned with the return value from that finder call.
|
||||
If you need to add more detailed arguments to the find, you can easily break this into two different
|
||||
expectations like:
|
||||
|
||||
it_should_find :users, :limit => 2
|
||||
it_should_assign :users
|
||||
|
||||
See the documentation for the <tt>it_should_find</tt> for more information. You might have guessed that
|
||||
<tt>it_should_initialize_assign</tt> and <tt>it_should_render_template</tt> work in a similar
|
||||
fashion and you'd be right. Again, see the documentation for these individual methods for more
|
||||
information. Lots of information in those docs.
|
||||
|
||||
A useful helper method that doesn't appear in any of the scaffolding is <tt>with_default_restful_actions</tt>
|
||||
which takes a block and evaluates it for each of the RESTful controller actions. Very useful for
|
||||
spec'ing that these methods redirect to the login page when the user isn't logged in, for example. This
|
||||
method is designed to be used inside an example like so:
|
||||
|
||||
describe "when not logged in" do
|
||||
it "should redirect all requests to the login page" do
|
||||
with_default_restful_actions do
|
||||
response.should redirect_to(login_url)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Before we're through with the controller specs, let me point out one more important detail. In
|
||||
order to use <tt>it_should_redirect_to</tt> we have to send the routing inside a block argument
|
||||
there so it can be evaluated in the example context instead of the example group, where it
|
||||
completely blows up. This methodology is used anywhere routing is referred to in a "skinny",
|
||||
example group level spec.
|
||||
|
||||
=== View Specs
|
||||
|
||||
Now let's move to the view specs!
|
||||
|
||||
describe "/users/form.html.haml" do
|
||||
before(:each) do
|
||||
@user = mock_and_assign(User, :stub => {
|
||||
:name => "foo",
|
||||
:birthday => 1.week.ago,
|
||||
:adult => false
|
||||
})
|
||||
end
|
||||
|
||||
it_should_have_form_for :user
|
||||
|
||||
it_should_allow_editing :user, :name
|
||||
it_should_allow_editing :user, :birthday
|
||||
it_should_allow_editing :user, :adult
|
||||
|
||||
it_should_link_to_show :user
|
||||
it_should_link_to { users_path }
|
||||
end
|
||||
|
||||
Like the special <tt>stub_index</tt> methods in the controller
|
||||
specs, the view specs have a shorthand mock and stub helpers: <tt>mock_and_assign</tt> and
|
||||
<tt>mock_and_assign_collection</tt>. These are well documented so please check them out.
|
||||
|
||||
There are also some really nice helper methods that I'd like point out. First is
|
||||
<tt>it_should_have_form_for</tt>. This is a really good convenience wrapper that basically wraps
|
||||
the much longer:
|
||||
|
||||
it "should use form_for to generate the proper form action and options" do
|
||||
template.should_receive(:form_for).with(@user)
|
||||
do_render
|
||||
end
|
||||
|
||||
Next up is the <tt>it_should_allow_editing</tt> helper. I love this method the most because it
|
||||
really helps DRY up that view spec while at the same time being amazingly unbrittle. Instead of
|
||||
creating an expectation for a specific form element, this method creates a generalized expectation
|
||||
that there's a form element with the <tt>name</tt> attribute set in such away that it will
|
||||
generate the proper <tt>params</tt> to use in the controller to edit or create the instance.
|
||||
Check out the docs and the source for more information on this. Also check out
|
||||
<tt>it_should_have_form_element_for</tt> which is roughly equivalent for those times when you use
|
||||
<tt>form_tag</tt> instead.
|
||||
|
||||
Finally let's look at those <tt>it_should_link_to_<i>controller_method</i></tt> helpers.
|
||||
These methods (and there's one each for the controller methods
|
||||
<tt>new</tt>, <tt>edit</tt>, <tt>show</tt>, and <tt>delete</tt>) point to instance variables
|
||||
which you should be created in the "before" blocks with <tt>mock_and_assign</tt>. The other is
|
||||
<tt>it_should_allow_editing</tt> which is likewise covered extensively in the documentation and
|
||||
I will just point out here that, like <tt>it_should_link_to_edit</tt> and such, it takes a
|
||||
symbol for the name of the instance variable it refers to and <i>additionally</i> takes
|
||||
a symbol for the name of the attribute to be edited.
|
||||
|
||||
Also note that, when constructing a long form example, instead of defining an instance variable
|
||||
for the name of the template and calling <tt>render @that_template</tt> you can simply call
|
||||
<tt>do_render</tt> which takes the name of the template from the outermost example group where
|
||||
it is customarily stated.
|
||||
|
||||
=== Model Specs
|
||||
|
||||
Skinny Spec adds a matcher for the various ActiveRecord associations. On the example group level
|
||||
you call them like:
|
||||
|
||||
it_should_belong_to :manager
|
||||
it_should_have_many :clients
|
||||
|
||||
Within an example you can call them on either the class or the instance setup in the
|
||||
"before" block. These are equivalent:
|
||||
|
||||
@user.should belong_to(:group)
|
||||
User.should belong_to(:group)
|
||||
|
||||
I've also added some very basic validation helpers like <tt>it_should_validate_presence_of</tt>,
|
||||
<tt>it_should_validate_uniqueness_of</tt>, <tt>it_should_not_mass_assign</tt>. Please consult
|
||||
the documentation for more information.
|
||||
|
||||
== Miscellaneous Notes
|
||||
|
||||
In the scaffolding, I have used my own idiomatic Rails usage:
|
||||
|
||||
* All controller actions which use HTML forms [<tt>new</tt>, <tt>edit</tt>, etc] use a shared
|
||||
form and leverage <tt>form_for</tt> to its fullest by letting it create the appropriate
|
||||
action and options.
|
||||
* Some instances where you might expect link_to are button_to. This is to provide a common
|
||||
interface element which can be styled the same instead of a mishmash of links and buttons and
|
||||
inputs everywhere. To take full advantage of this, I usually override many of Rails' default
|
||||
helpers with custom ones that all use actual HTML <tt>BUTTON</tt> elements which are much
|
||||
easier to style than "button" typed <tt>INPUT</tt>. I've provided a text file in the
|
||||
"additional" folder of this plugin which you can use in your ApplicationHelper. (I also
|
||||
provide an optional override helper for the <tt>label</tt> method which uses
|
||||
<tt>#titleize</tt> instead of <tt>humanize</tt> for stylistic reasons).
|
||||
* Probably more that I can't think of.
|
||||
|
||||
== Credits and Thanks
|
||||
|
||||
Sections of this code were taken from or inspired by Rick Olsen's
|
||||
rspec_on_rails_on_crack[http://github.com/technoweenie/rspec_on_rails_on_crack/tree/master].
|
||||
Also thanks and props to Hampton Catlin and Nathan Weizenbaum for the lovely and imminently useable
|
||||
Haml and make_resourceful. Also also praises and glory to David Chelimsky and the Rspec crew.
|
||||
|
||||
Also thanks to Don Petersen for his suggestions and fixes.
|
||||
11
vendor/plugins/skinny_spec/Rakefile
vendored
Normal file
11
vendor/plugins/skinny_spec/Rakefile
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
require 'rake'
|
||||
require 'rake/rdoctask'
|
||||
|
||||
desc 'Generate documentation for the Skinny Spec plugin'
|
||||
Rake::RDocTask.new(:rdoc) do |rdoc|
|
||||
rdoc.rdoc_dir = 'doc'
|
||||
rdoc.title = 'Skinny Spec'
|
||||
rdoc.options << '--line-numbers' << '--inline-source'
|
||||
rdoc.rdoc_files.include('README.rdoc')
|
||||
rdoc.rdoc_files.include('lib/**/*.rb')
|
||||
end
|
||||
58
vendor/plugins/skinny_spec/additional/helper_overrides.txt
vendored
Normal file
58
vendor/plugins/skinny_spec/additional/helper_overrides.txt
vendored
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
# Please insert these into your ApplicationHelper
|
||||
|
||||
# Replacement for Rails' default submit_tag helper
|
||||
# using HTML button element rather than HTML input element
|
||||
def submit_tag(text, options = {})
|
||||
content_tag :button, text, options.merge(:type => :submit)
|
||||
end
|
||||
|
||||
# Replacement for Rails' default button_to helper
|
||||
# using HTML button element rather than HTML input element
|
||||
def button_to(name, options = {}, html_options = {})
|
||||
html_options = html_options.stringify_keys
|
||||
convert_boolean_attributes!(html_options, %w( disabled ))
|
||||
|
||||
method_tag = ''
|
||||
if (method = html_options.delete('method')) && %w{put delete}.include?(method.to_s)
|
||||
method_tag = tag('input', :type => 'hidden', :name => '_method', :value => method.to_s)
|
||||
end
|
||||
|
||||
form_method = method.to_s == 'get' ? 'get' : 'post'
|
||||
|
||||
request_token_tag = ''
|
||||
if form_method == 'post' && protect_against_forgery?
|
||||
request_token_tag = tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token)
|
||||
end
|
||||
|
||||
if confirm = html_options.delete("confirm")
|
||||
html_options["onclick"] = "return #{confirm_javascript_function(confirm)};"
|
||||
end
|
||||
|
||||
url = options.is_a?(String) ? options : self.url_for(options)
|
||||
name ||= url
|
||||
|
||||
html_options.merge!("type" => "submit", "value" => name)
|
||||
|
||||
"<form method=\"#{form_method}\" action=\"#{escape_once url}\" class=\"button_to\"><div>" +
|
||||
method_tag + content_tag("button", name, html_options) + request_token_tag + "</div></form>"
|
||||
end
|
||||
|
||||
# Replacement for Rails' default button_to_function helper
|
||||
# using HTML button element rather than HTML input element
|
||||
def button_to_function(name, *args, &block)
|
||||
html_options = args.extract_options!
|
||||
function = args[0] || ''
|
||||
|
||||
html_options.symbolize_keys!
|
||||
function = update_page(&block) if block_given?
|
||||
content_tag(:button, name, html_options.merge({
|
||||
:onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
|
||||
}))
|
||||
end
|
||||
|
||||
# Replacement for Rails' default label helper
|
||||
# using String#titleize rather than String#humanize
|
||||
def label(object_name, method, text = nil, options = {})
|
||||
text ||= method.to_s[].titleize
|
||||
super
|
||||
end
|
||||
93
vendor/plugins/skinny_spec/generators/skinny_scaffold/skinny_scaffold_generator.rb
vendored
Normal file
93
vendor/plugins/skinny_spec/generators/skinny_scaffold/skinny_scaffold_generator.rb
vendored
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
class SkinnyScaffoldGenerator < Rails::Generator::NamedBase
|
||||
attr_reader :controller_class_path, :controller_file_path, :controller_class_nesting,
|
||||
:controller_class_nesting_depth, :controller_class_name, :controller_underscore_name,
|
||||
:controller_plural_name, :template_language
|
||||
alias_method :controller_file_name, :controller_underscore_name
|
||||
alias_method :controller_singular_name, :controller_file_name
|
||||
alias_method :controller_table_name, :controller_plural_name
|
||||
|
||||
def initialize(runtime_args, runtime_options = {})
|
||||
super
|
||||
|
||||
base_name, @controller_class_path, @controller_file_path, @controller_class_nesting, @controller_class_nesting_depth = extract_modules(@name.pluralize)
|
||||
@controller_class_name_without_nesting, @controller_underscore_name, @controller_plural_name = inflect_names(base_name)
|
||||
|
||||
if @controller_class_nesting.empty?
|
||||
@controller_class_name = @controller_class_name_without_nesting
|
||||
else
|
||||
@controller_class_name = "#{@controller_class_nesting}::#{@controller_class_name_without_nesting}"
|
||||
end
|
||||
end
|
||||
|
||||
def manifest
|
||||
record do |m|
|
||||
# Check for class naming collisions
|
||||
m.class_collisions controller_class_path, "#{controller_class_name}Controller", "#{controller_class_name}Helper"
|
||||
m.class_collisions class_path, "#{class_name}"
|
||||
|
||||
# # Controller, helper, and views directories
|
||||
m.directory File.join('app', 'views', controller_class_path, controller_file_name)
|
||||
m.directory File.join('spec', 'views', controller_class_path, controller_file_name)
|
||||
m.directory File.join('app', 'helpers', controller_class_path)
|
||||
m.directory File.join('spec', 'helpers', controller_class_path)
|
||||
m.directory File.join('app', 'controllers', controller_class_path)
|
||||
m.directory File.join('spec', 'controllers', controller_class_path)
|
||||
m.directory File.join('app', 'models', class_path)
|
||||
m.directory File.join('spec', 'models', class_path)
|
||||
|
||||
# Views
|
||||
@template_language = defined?(Haml) ? "haml" : "erb"
|
||||
%w{index show form}.each do |action|
|
||||
m.template "#{action}.html.#{template_language}",
|
||||
File.join('app/views', controller_class_path, controller_file_name, "#{action}.html.#{template_language}")
|
||||
m.template "#{action}.html_spec.rb",
|
||||
File.join('spec/views', controller_class_path, controller_file_name, "#{action}.html.#{template_language}_spec.rb")
|
||||
end
|
||||
m.template "index_partial.html.#{template_language}",
|
||||
File.join('app/views', controller_class_path, controller_file_name, "_#{file_name}.html.#{template_language}")
|
||||
m.template 'index_partial.html_spec.rb',
|
||||
File.join('spec/views', controller_class_path, controller_file_name, "_#{file_name}.html.#{template_language}_spec.rb")
|
||||
|
||||
# Helper
|
||||
m.template 'helper.rb',
|
||||
File.join('app/helpers', controller_class_path, "#{controller_file_name}_helper.rb")
|
||||
m.template 'helper_spec.rb',
|
||||
File.join('spec/helpers', controller_class_path, "#{controller_file_name}_helper_spec.rb")
|
||||
|
||||
# Controller
|
||||
m.template 'controller.rb',
|
||||
File.join('app/controllers', controller_class_path, "#{controller_file_name}_controller.rb")
|
||||
m.template 'controller_spec.rb',
|
||||
File.join('spec/controllers', controller_class_path, "#{controller_file_name}_controller_spec.rb")
|
||||
|
||||
# Model
|
||||
m.template 'model.rb',
|
||||
File.join('app/models', class_path, "#{file_name}.rb")
|
||||
m.template 'model_spec.rb',
|
||||
File.join('spec/models', class_path, "#{file_name}_spec.rb")
|
||||
|
||||
# Routing
|
||||
m.route_resources controller_file_name
|
||||
|
||||
unless options[:skip_migration]
|
||||
m.migration_template(
|
||||
'migration.rb', 'db/migrate',
|
||||
:assigns => {
|
||||
:migration_name => "Create#{class_name.pluralize.gsub(/::/, '')}",
|
||||
:attributes => attributes
|
||||
},
|
||||
:migration_file_name => "create_#{file_path.gsub(/\//, '_').pluralize}"
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def banner
|
||||
"Usage: #{$0} skinny_scaffold ModelName [field:type, field:type]"
|
||||
end
|
||||
|
||||
def model_name
|
||||
class_name.demodulize
|
||||
end
|
||||
end
|
||||
105
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/controller.rb
vendored
Normal file
105
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/controller.rb
vendored
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
class <%= controller_class_name %>Controller < ApplicationController
|
||||
<%- if defined?(Resourceful::Maker) -%>
|
||||
make_resourceful do
|
||||
actions :all
|
||||
|
||||
# Let's get the most use from form_for and share a single form here!
|
||||
response_for :new, :edit do
|
||||
render :template => "<%= plural_name %>/form"
|
||||
end
|
||||
|
||||
response_for :create_fails, :update_fails do
|
||||
flash[:error] = "There was a problem!"
|
||||
render :template => "<%= plural_name %>/form"
|
||||
end
|
||||
end
|
||||
<%- else -%>
|
||||
# GET /<%= table_name %>
|
||||
# GET /<%= table_name %>.xml
|
||||
def index
|
||||
@<%= table_name %> = <%= class_name %>.find(:all)
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.erb
|
||||
format.xml { render :xml => @<%= table_name %> }
|
||||
end
|
||||
end
|
||||
|
||||
# GET /<%= table_name %>/1
|
||||
# GET /<%= table_name %>/1.xml
|
||||
def show
|
||||
@<%= file_name %> = <%= class_name %>.find(params[:id])
|
||||
|
||||
respond_to do |format|
|
||||
format.html # show.html.erb
|
||||
format.xml { render :xml => @<%= file_name %> }
|
||||
end
|
||||
end
|
||||
|
||||
# GET /<%= table_name %>/new
|
||||
# GET /<%= table_name %>/new.xml
|
||||
def new
|
||||
@<%= file_name %> = <%= class_name %>.new
|
||||
|
||||
respond_to do |format|
|
||||
format.html { render :template => "<%= plural_name %>/form" }
|
||||
format.xml { render :xml => @<%= file_name %> }
|
||||
end
|
||||
end
|
||||
|
||||
# GET /<%= table_name %>/1/edit
|
||||
def edit
|
||||
@<%= file_name %> = <%= class_name %>.find(params[:id])
|
||||
render :template => "<%= plural_name %>/form"
|
||||
end
|
||||
|
||||
# POST /<%= table_name %>
|
||||
# POST /<%= table_name %>.xml
|
||||
def create
|
||||
@<%= file_name %> = <%= class_name %>.new(params[:<%= file_name %>])
|
||||
|
||||
respond_to do |format|
|
||||
if @<%= file_name %>.save
|
||||
flash[:notice] = '<%= class_name %> was successfully created.'
|
||||
format.html { redirect_to(@<%= file_name %>) }
|
||||
format.xml { render :xml => @<%= file_name %>, :status => :created, :location => @<%= file_name %> }
|
||||
else
|
||||
flash.now[:error] = '<%= class_name %> could not be created.'
|
||||
format.html { render :template => "<%= plural_name %>/form" }
|
||||
format.xml { render :xml => @<%= file_name %>.errors, :status => :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# PUT /<%= table_name %>/1
|
||||
# PUT /<%= table_name %>/1.xml
|
||||
def update
|
||||
@<%= file_name %> = <%= class_name %>.find(params[:id])
|
||||
|
||||
respond_to do |format|
|
||||
if @<%= file_name %>.update_attributes(params[:<%= file_name %>])
|
||||
flash[:notice] = '<%= class_name %> was successfully updated.'
|
||||
format.html { redirect_to(@<%= file_name %>) }
|
||||
format.xml { head :ok }
|
||||
else
|
||||
flash.now[:error] = '<%= class_name %> could not be created.'
|
||||
format.html { render :template => "<%= plural_name %>/form" }
|
||||
format.xml { render :xml => @<%= file_name %>.errors, :status => :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# DELETE /<%= table_name %>/1
|
||||
# DELETE /<%= table_name %>/1.xml
|
||||
def destroy
|
||||
@<%= file_name %> = <%= class_name %>.find(params[:id])
|
||||
@<%= file_name %>.destroy
|
||||
|
||||
respond_to do |format|
|
||||
flash[:notice] = '<%= class_name %> was successfully deleted.'
|
||||
format.html { redirect_to(<%= table_name %>_url) }
|
||||
format.xml { head :ok }
|
||||
end
|
||||
end
|
||||
<%- end -%>
|
||||
end
|
||||
116
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/controller_spec.rb
vendored
Normal file
116
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/controller_spec.rb
vendored
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
require File.dirname(__FILE__) + '/../spec_helper'
|
||||
|
||||
describe <%= controller_class_name %>Controller do
|
||||
def valid_attributes(args = {})
|
||||
{
|
||||
<% if attributes.empty? -%>
|
||||
# Add valid attributes for the your params[:<%= singular_name %>] here!
|
||||
<% else -%>
|
||||
<%- attributes.each_with_index do |attribute, index| -%>
|
||||
<%- case attribute.type -%>
|
||||
<%- when :string, :text -%>
|
||||
"<%= attribute.name %>" => "foo"<%= index < attributes.size - 1 ? "," : "" %>
|
||||
<%- when :integer, :float, :decimal -%>
|
||||
"<%= attribute.name %>" => 815<%= index < attributes.size - 1 ? "," : "" %>
|
||||
<%- when :boolean -%>
|
||||
"<%= attribute.name %>" => false<%= index < attributes.size - 1 ? "," : "" %>
|
||||
<%- when :date, :datetime, :time, :timestamp -%>
|
||||
"<%= attribute.name %>" => 1.week.ago<%= index < attributes.size - 1 ? "," : "" %>
|
||||
<%- else -%>
|
||||
"<%= attribute.name %>" => nil<%= index < attributes.size - 1 ? "," : "" %> # Could not determine valid attribute
|
||||
<%- end -%>
|
||||
<%- end -%>
|
||||
<% end -%>
|
||||
}.merge(args)
|
||||
end
|
||||
|
||||
describe "GET :index" do
|
||||
before(:each) do
|
||||
@<%= plural_name %> = stub_index(<%= class_name %>)
|
||||
end
|
||||
|
||||
it_should_find_and_assign :<%= plural_name %>
|
||||
it_should_render_template "index"
|
||||
end
|
||||
|
||||
describe "GET :new" do
|
||||
before(:each) do
|
||||
@<%= singular_name %> = stub_new(<%= class_name %>)
|
||||
end
|
||||
|
||||
it_should_initialize_and_assign :<%= singular_name %>
|
||||
it_should_render_template "form"
|
||||
end
|
||||
|
||||
describe "POST :create" do
|
||||
describe "when successful" do
|
||||
before(:each) do
|
||||
@<%= singular_name %> = stub_create(<%= class_name %>)
|
||||
end
|
||||
|
||||
it_should_initialize_and_save :<%= singular_name %>
|
||||
it_should_set_flash :notice
|
||||
it_should_redirect_to { <%= singular_name %>_url(@<%= singular_name %>) }
|
||||
end
|
||||
|
||||
describe "when unsuccessful" do
|
||||
before(:each) do
|
||||
@<%= singular_name %> = stub_create(<%= class_name %>, :return => :false)
|
||||
end
|
||||
|
||||
it_should_initialize_and_assign :<%= singular_name %>
|
||||
it_should_set_flash :error
|
||||
it_should_render_template "form"
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET :show" do
|
||||
before(:each) do
|
||||
@<%= singular_name %> = stub_show(<%= class_name %>)
|
||||
end
|
||||
|
||||
it_should_find_and_assign :<%= singular_name %>
|
||||
it_should_render_template "show"
|
||||
end
|
||||
|
||||
describe "GET :edit" do
|
||||
before(:each) do
|
||||
@<%= singular_name %> = stub_edit(<%= class_name %>)
|
||||
end
|
||||
|
||||
it_should_find_and_assign :<%= singular_name %>
|
||||
it_should_render_template "form"
|
||||
end
|
||||
|
||||
describe "PUT :update" do
|
||||
describe "when successful" do
|
||||
before(:each) do
|
||||
@<%= singular_name %> = stub_update(<%= class_name %>)
|
||||
end
|
||||
|
||||
it_should_find_and_update :<%= singular_name %>
|
||||
it_should_set_flash :notice
|
||||
it_should_redirect_to { <%= singular_name %>_url(@<%= singular_name %>) }
|
||||
end
|
||||
|
||||
describe "when unsuccessful" do
|
||||
before(:each) do
|
||||
@<%= singular_name %> = stub_update(<%= class_name %>, :return => :false)
|
||||
end
|
||||
|
||||
it_should_find_and_assign :<%= singular_name %>
|
||||
it_should_set_flash :error
|
||||
it_should_render_template "form"
|
||||
end
|
||||
end
|
||||
|
||||
describe "DELETE :destroy" do
|
||||
before(:each) do
|
||||
@<%= singular_name %> = stub_destroy(<%= class_name %>)
|
||||
end
|
||||
|
||||
it_should_find_and_destroy :<%= singular_name %>
|
||||
it_should_set_flash :notice
|
||||
it_should_redirect_to { <%= plural_name %>_url }
|
||||
end
|
||||
end
|
||||
25
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/form.html.erb
vendored
Normal file
25
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/form.html.erb
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<h1><%= singular_name %>.new_record? ? "New" : "Edit" %> <%= model_name %></h1>
|
||||
<%% form_for(@<%= singular_name %>) do |f| %>
|
||||
<div id="form_errors">
|
||||
<%%= f.error_messages %>
|
||||
</div>
|
||||
<%- if attributes.blank? -%>
|
||||
<p>Add your form elements here, please!</p>
|
||||
<%- else -%>
|
||||
<%- attributes.each do |attribute| -%>
|
||||
<p>
|
||||
<%%= f.label :<%= attribute.name %> %><br />
|
||||
<%%= f.<%= attribute.field_type %> :<%= attribute.name %> %>
|
||||
</p>
|
||||
<%- end -%>
|
||||
<%- end -%>
|
||||
<div id="commands">
|
||||
<%%= submit_tag "Save" %>
|
||||
<div id="navigation_commands">
|
||||
<%% unless @<%= singular_name %>.new_record? -%>
|
||||
<%%= button_to "Show", <%= singular_name %>_path(@<%= singular_name %>), :method => "get", :title => "Show <%= singular_name %>. Unsaved changes will be lost." %>
|
||||
<%% end -%>
|
||||
<%%= button_to "Back to List", <%= plural_name %>_path, :class => "cancel", :method => "get", :title => "Return to <%= singular_name %> list without saving changes" %>
|
||||
</div>
|
||||
</div>
|
||||
<%% end -%>
|
||||
18
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/form.html.haml
vendored
Normal file
18
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/form.html.haml
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
%h1== #{@<%= singular_name %>.new_record? ? "New" : "Edit"} #{<%= model_name %>}
|
||||
- form_for @<%= singular_name %> do |f|
|
||||
#form_errors= f.error_messages
|
||||
<% if attributes.blank? -%>
|
||||
%p Add your form elements here, please!
|
||||
<% else -%>
|
||||
<%- attributes.each do |attribute| -%>
|
||||
%p
|
||||
= f.label :<%= attribute.name %>
|
||||
= f.<%= attribute.field_type %> :<%= attribute.name %>
|
||||
<%- end -%>
|
||||
<% end -%>
|
||||
#commands
|
||||
= submit_tag "Save"
|
||||
#navigation_commands
|
||||
- unless @<%= singular_name %>.new_record?
|
||||
= button_to "Show", <%= singular_name %>_path(@<%= singular_name %>), :method => "get", :title => "Show <%= singular_name %>. Unsaved changes will be lost."
|
||||
= button_to "Back to List", <%= plural_name %>_path, :class => "cancel", :method => "get", :title => "Return to <%= singular_name %> list without saving changes"
|
||||
41
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/form.html_spec.rb
vendored
Normal file
41
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/form.html_spec.rb
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
require File.dirname(__FILE__) + '<%= '/..' * controller_class_nesting_depth %>/../../spec_helper'
|
||||
|
||||
describe "<%= File.join(controller_class_path, controller_singular_name) %>/form.html.<%= template_language %>" do
|
||||
before(:each) do
|
||||
@<%= singular_name %> = mock_and_assign(<%= model_name %>, :stub => {
|
||||
<% if attributes.blank? -%>
|
||||
# Add your stub attributes and return values here like:
|
||||
# :name => "Foo", :created_at => 1.week.ago, :updated_at => nil
|
||||
<% else -%>
|
||||
<%- attributes.each_with_index do |attribute, index| -%>
|
||||
<%- case attribute.type -%>
|
||||
<%- when :string, :text -%>
|
||||
:<%= attribute.name %> => "foo"<%= index < attributes.size - 1 ? "," : "" %>
|
||||
<%- when :integer, :float, :decimal -%>
|
||||
:<%= attribute.name %> => 815<%= index < attributes.size - 1 ? "," : "" %>
|
||||
<%- when :boolean -%>
|
||||
:<%= attribute.name %> => false<%= index < attributes.size - 1 ? "," : "" %>
|
||||
<%- when :date, :datetime, :time, :timestamp -%>
|
||||
:<%= attribute.name %> => 1.week.ago<%= index < attributes.size - 1 ? "," : "" %>
|
||||
<%- else -%>
|
||||
:<%= attribute.name %> => nil<%= index < attributes.size - 1 ? "," : "" %> # Could not determine valid attribute
|
||||
<%- end -%>
|
||||
<%- end -%>
|
||||
<% end -%>
|
||||
})
|
||||
end
|
||||
|
||||
it_should_have_form_for :<%= singular_name %>
|
||||
<% if attributes.blank? -%>
|
||||
# Add specs for editing attributes here, please! Like this:
|
||||
#
|
||||
# it_should_allow_editing :<%= singular_name %>, :foo
|
||||
<% else -%>
|
||||
<%- attributes.each do |attribute| -%>
|
||||
it_should_allow_editing :<%= singular_name %>, :<%= attribute.name %>
|
||||
<%- end -%>
|
||||
<% end -%>
|
||||
|
||||
it_should_link_to_show :<%= singular_name %>
|
||||
it_should_link_to { <%= plural_name %>_path }
|
||||
end
|
||||
2
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/helper.rb
vendored
Normal file
2
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/helper.rb
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
module <%= controller_class_name %>Helper
|
||||
end
|
||||
5
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/helper_spec.rb
vendored
Normal file
5
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/helper_spec.rb
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
require File.dirname(__FILE__) + '<%= '/..' * controller_class_nesting_depth %>/../spec_helper'
|
||||
|
||||
describe <%= controller_class_name %>Helper do
|
||||
# Add your specs here or remove this file completely, please!
|
||||
end
|
||||
31
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/index.html.erb
vendored
Normal file
31
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/index.html.erb
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<h1><%= model_name %> List</h1>
|
||||
<table>
|
||||
<%%- if @<%= plural_name %>.empty? -%>
|
||||
<tbody>
|
||||
<tr class="empty">
|
||||
<td>There are no <%= plural_name.humanize.downcase %></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<%%- else -%>
|
||||
<thead>
|
||||
<tr>
|
||||
<%- if attributes.blank? -%>
|
||||
<th><!-- Generic display column --></th>
|
||||
<%- else -%>
|
||||
<%- attributes.each do |attribute| -%>
|
||||
<th><%= attribute.name.titleize %></th>
|
||||
<%- end -%>
|
||||
<%- end -%>
|
||||
<th class="show"><!-- "Show" link column --></th>
|
||||
<th class="edit"><!-- "Edit" link column --></th>
|
||||
<th class="delete"><!-- "Delete" link column --></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<%%= render :partial => @<%= plural_name %> %>
|
||||
</tbody>
|
||||
<%%- end -%>
|
||||
</table>
|
||||
<div id="commands">
|
||||
<%%= button_to "New <%= singular_name.titleize %>", new_<%= singular_name %>_path, :method => "get" %>
|
||||
</div>
|
||||
23
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/index.html.haml
vendored
Normal file
23
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/index.html.haml
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
%h1 <%= model_name %> List
|
||||
%table
|
||||
- if @<%= plural_name %>.empty?
|
||||
%tbody
|
||||
%tr.empty
|
||||
%td== There are no <%= plural_name.humanize.downcase %>
|
||||
- else
|
||||
%thead
|
||||
%tr
|
||||
<% if attributes.blank? -%>
|
||||
%th= # Generic display column
|
||||
<% else -%>
|
||||
<%- attributes.each do |attribute| -%>
|
||||
%th <%= attribute.name.titleize %>
|
||||
<%- end -%>
|
||||
<% end -%>
|
||||
%th.show= # 'Show' link column
|
||||
%th.edit= # 'Edit' link column
|
||||
%th.delete= # 'Delete' link column
|
||||
%tbody
|
||||
= render :partial => @<%= plural_name %>
|
||||
#commands
|
||||
= button_to "New <%= singular_name.titleize %>", new_<%= singular_name %>_path, :method => "get"
|
||||
15
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/index.html_spec.rb
vendored
Normal file
15
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/index.html_spec.rb
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
require File.dirname(__FILE__) + '<%= '/..' * controller_class_nesting_depth %>/../../spec_helper'
|
||||
|
||||
describe "<%= File.join(controller_class_path, controller_singular_name) %>/index.html.<%= template_language %>" do
|
||||
before(:each) do
|
||||
@<%= plural_name %> = mock_and_assign_collection(<%= model_name %>)
|
||||
template.stub_render :partial => @<%= plural_name %>
|
||||
end
|
||||
|
||||
it "should render :partial => @<%= plural_name %>" do
|
||||
template.expect_render :partial => @<%= plural_name %>
|
||||
do_render
|
||||
end
|
||||
|
||||
it_should_link_to_new :<%= singular_name %>
|
||||
end
|
||||
12
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/index_partial.html.erb
vendored
Normal file
12
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/index_partial.html.erb
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<tr class="<%%= cycle("odd", "even") %>">
|
||||
<% if attributes.blank? -%>
|
||||
<td><%= model_name %> #<%%= <%= singular_name %>.id %></td>
|
||||
<% else -%>
|
||||
<%- attributes.each do |attribute| -%>
|
||||
<td><%%=h <%= singular_name %>.<%= attribute.name %> %></td>
|
||||
<%- end %>
|
||||
<% end -%>
|
||||
<td class="show"><%%= button_to "Show", <%= singular_name %>, :method => "get" %></td>
|
||||
<td class="edit"><%%= button_to "Edit", edit_<%= singular_name %>_path(<%= singular_name %>), :method => "get" %></td>
|
||||
<td class="delete"><%%= button_to "Delete", <%= singular_name %>, :method => "delete" %></td>
|
||||
</tr>
|
||||
11
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/index_partial.html.haml
vendored
Normal file
11
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/index_partial.html.haml
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
%tr{:class => cycle("odd", "even")}
|
||||
<% if attributes.blank? -%>
|
||||
%td== <%= model_name %> #{<%= singular_name %>.id}
|
||||
<% else -%>
|
||||
<%- attributes.each do |attribute| -%>
|
||||
%td=h <%= singular_name %>.<%= attribute.name %>
|
||||
<%- end -%>
|
||||
<% end -%>
|
||||
%td.show= button_to "Show", <%= singular_name %>_path(<%= singular_name %>), :method => "get"
|
||||
%td.edit= button_to "Edit", edit_<%= singular_name %>_path(<%= singular_name %>), :method => "get"
|
||||
%td.delete= button_to "Delete", <%= singular_name %>, :method => "delete"
|
||||
32
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/index_partial.html_spec.rb
vendored
Normal file
32
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/index_partial.html_spec.rb
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
require File.dirname(__FILE__) + '<%= '/..' * controller_class_nesting_depth %>/../../spec_helper'
|
||||
|
||||
describe "<%= File.join(controller_class_path, controller_singular_name) %>/_<%= singular_name %>.html.<%= template_language %>" do
|
||||
before(:each) do
|
||||
@<%= singular_name %> = mock_and_assign(<%= model_name %>, :stub => {
|
||||
<% if attributes.blank? -%>
|
||||
# Add your stub attributes and return values here like:
|
||||
# :name => "Foo", :created_at => 1.week.ago, :updated_at => nil
|
||||
<% else -%>
|
||||
<%- attributes.each_with_index do |attribute, index| -%>
|
||||
<%- case attribute.type -%>
|
||||
<%- when :string, :text -%>
|
||||
:<%= attribute.name %> => "foo"<%= index < attributes.size - 1 ? "," : "" %>
|
||||
<%- when :integer, :float, :decimal -%>
|
||||
:<%= attribute.name %> => 815<%= index < attributes.size - 1 ? "," : "" %>
|
||||
<%- when :boolean -%>
|
||||
:<%= attribute.name %> => false<%= index < attributes.size - 1 ? "," : "" %>
|
||||
<%- when :date, :datetime, :time, :timestamp -%>
|
||||
:<%= attribute.name %> => 1.week.ago<%= index < attributes.size - 1 ? "," : "" %>
|
||||
<%- else -%>
|
||||
:<%= attribute.name %> => nil<%= index < attributes.size - 1 ? "," : "" %>
|
||||
<%- end -%>
|
||||
<%- end -%>
|
||||
<% end -%>
|
||||
})
|
||||
template.stub!(:<%= singular_name %>).and_return(@<%= singular_name %>)
|
||||
end
|
||||
|
||||
it_should_link_to_show :<%= singular_name %>
|
||||
it_should_link_to_edit :<%= singular_name %>
|
||||
it_should_link_to_delete :<%= singular_name %>
|
||||
end
|
||||
14
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/migration.rb
vendored
Normal file
14
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/migration.rb
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
class <%= migration_name %> < ActiveRecord::Migration
|
||||
def self.up
|
||||
create_table :<%= table_name %>, :force => true do |t|
|
||||
<% attributes.each do |attribute| -%>
|
||||
t.column :<%= attribute.name %>, :<%= attribute.type %>
|
||||
<% end -%>
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table :<%= table_name %>
|
||||
end
|
||||
end
|
||||
2
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/model.rb
vendored
Normal file
2
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/model.rb
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
class <%= class_name %> < ActiveRecord::Base
|
||||
end
|
||||
25
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/model_spec.rb
vendored
Normal file
25
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/model_spec.rb
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
require File.dirname(__FILE__) + '<%= '/..' * class_nesting_depth %>/../spec_helper'
|
||||
|
||||
describe <%= class_name %> do
|
||||
def valid_attributes(args = {})
|
||||
{
|
||||
# Add valid attributes for building your model instances here!
|
||||
}.merge(args)
|
||||
end
|
||||
|
||||
before(:each) do
|
||||
@<%= singular_name %> = <%= class_name %>.new
|
||||
end
|
||||
|
||||
after(:each) do
|
||||
@<%= singular_name %>.destroy unless @<%= singular_name %>.new_record?
|
||||
end
|
||||
|
||||
# Add your model specs here, please!
|
||||
# And don't forget about the association matchers built-in to skinny_spec like:
|
||||
#
|
||||
# it_should_have_many :foos
|
||||
# it_should_validate_presence_of :bar
|
||||
#
|
||||
# Check out the docs for more information.
|
||||
end
|
||||
15
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/show.html.erb
vendored
Normal file
15
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/show.html.erb
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<h1>Show <%= model_name %></h1>
|
||||
<% if attributes.blank? -%>
|
||||
<p>Add your customized markup here, please!</p>
|
||||
<% else -%>
|
||||
<%- attributes.each do |attribute| -%>
|
||||
<p>
|
||||
<label><%= attribute.name.titleize %>:</label>
|
||||
<%%=h @<%= singular_name %>.<%= attribute.name %> %>
|
||||
</p>
|
||||
<%- end -%>
|
||||
<% end -%>
|
||||
<div id="commands">
|
||||
<%%= button_to "Edit", edit_<%= singular_name %>_path(@<%= singular_name %>), :method => "get" %>
|
||||
<%%= button_to "Back to List", <%= plural_name %>_path, :method => "get" %>
|
||||
</div>
|
||||
13
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/show.html.haml
vendored
Normal file
13
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/show.html.haml
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
%h1== Show #{<%= model_name %>}
|
||||
<% if attributes.blank? -%>
|
||||
%p Add your customized markup here, please!
|
||||
<% else -%>
|
||||
<%- attributes.each do |attribute| -%>
|
||||
%p
|
||||
%label <%= attribute.name.titleize %>:
|
||||
=h @<%= singular_name %>.<%= attribute.name %>
|
||||
<%- end -%>
|
||||
<% end -%>
|
||||
#commands
|
||||
= button_to "Edit", edit_<%= singular_name %>_path(@<%= singular_name %>), :method => "get"
|
||||
= button_to "Back to List", <%= plural_name %>_path, :method => "get"
|
||||
32
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/show.html_spec.rb
vendored
Normal file
32
vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/show.html_spec.rb
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
require File.dirname(__FILE__) + '<%= '/..' * controller_class_nesting_depth %>/../../spec_helper'
|
||||
|
||||
describe "<%= File.join(controller_class_path, controller_singular_name) %>/show.html.<%= template_language %>" do
|
||||
before(:each) do
|
||||
<% if attributes.blank? -%>
|
||||
@<%= singular_name %> = mock_and_assign(<%= model_name %>)
|
||||
<% else -%>
|
||||
@<%= singular_name %> = mock_and_assign(<%= model_name %>, :stub => {
|
||||
<%- attributes.each_with_index do |attribute, index| -%>
|
||||
<%- case attribute.type -%>
|
||||
<%- when :string, :text -%>
|
||||
:<%= attribute.name %> => "foo"<%= index < attributes.size - 1 ? "," : "" %>
|
||||
<%- when :integer, :float, :decimal -%>
|
||||
:<%= attribute.name %> => 815<%= index < attributes.size - 1 ? "," : "" %>
|
||||
<%- when :boolean -%>
|
||||
:<%= attribute.name %> => false<%= index < attributes.size - 1 ? "," : "" %>
|
||||
<%- when :date, :datetime, :time, :timestamp -%>
|
||||
:<%= attribute.name %> => 1.week.ago<%= index < attributes.size - 1 ? "," : "" %>
|
||||
<%- else -%>
|
||||
:<%= attribute.name %> => nil<%= index < attributes.size - 1 ? "," : "" %>
|
||||
<%- end -%>
|
||||
<%- end -%>
|
||||
})
|
||||
<% end -%>
|
||||
end
|
||||
|
||||
# Add your specs here, please! But remember not to make them brittle
|
||||
# by specing specing specific HTML elements and classes.
|
||||
|
||||
it_should_link_to_edit :<%= singular_name %>
|
||||
it_should_link_to { <%= plural_name %>_path }
|
||||
end
|
||||
3
vendor/plugins/skinny_spec/init.rb
vendored
Normal file
3
vendor/plugins/skinny_spec/init.rb
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
if RAILS_ENV == "test"
|
||||
require "skinny_spec"
|
||||
end
|
||||
46
vendor/plugins/skinny_spec/lib/lucky_sneaks/common_spec_helpers.rb
vendored
Normal file
46
vendor/plugins/skinny_spec/lib/lucky_sneaks/common_spec_helpers.rb
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
module LuckySneaks
|
||||
# These methods are mostly just called internally by various other spec helper
|
||||
# methods but you're welcome to use them as needed in your own specs.
|
||||
module CommonSpecHelpers
|
||||
# Returns class for the specified name. Example:
|
||||
#
|
||||
# class_for("foo") # => Foo
|
||||
def class_for(name)
|
||||
name.to_s.constantize
|
||||
rescue NameError
|
||||
name.to_s.classify.constantize
|
||||
# Let any other error rise!
|
||||
end
|
||||
|
||||
# Returns instance variable for the specified name. Example:
|
||||
#
|
||||
# instance_for("foo") # => @foo
|
||||
def instance_for(name)
|
||||
instance_variable_get("@#{name.to_s.underscore}")
|
||||
end
|
||||
|
||||
# Wraps a matcher that checks if the receiver contains an <tt>A</tt> element (link)
|
||||
# whose <tt>href</tt> attribute is set to the specified path.
|
||||
def have_link_to(path)
|
||||
have_tag("a[href='#{path}']")
|
||||
end
|
||||
|
||||
# Returns dummy value for specified attribute based on the datatype expected for that
|
||||
# attribute.
|
||||
def dummy_value_for(instance, attribute)
|
||||
if datatype = instance.column_for_attribute(attribute)
|
||||
actual = instance.send(attribute)
|
||||
case datatype.type
|
||||
when :string, :text
|
||||
actual == "foo" ? "bar" : "food"
|
||||
when :integer, :float, :decimal
|
||||
actual == 108 ? 815 : 108
|
||||
when :boolean
|
||||
actual ? false : true
|
||||
when :date, :datetime, :time, :timestamp
|
||||
actual == 1.week.ago ? 2.years.ago : 1.week.ago
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
67
vendor/plugins/skinny_spec/lib/lucky_sneaks/controller_request_helpers.rb
vendored
Normal file
67
vendor/plugins/skinny_spec/lib/lucky_sneaks/controller_request_helpers.rb
vendored
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
module LuckySneaks
|
||||
module ControllerRequestHelpers # :nodoc:
|
||||
def self.included(base)
|
||||
base.extend ExampleGroupMethods
|
||||
end
|
||||
|
||||
private
|
||||
def define_implicit_request(method)
|
||||
@controller_method = method
|
||||
@implicit_request = case method
|
||||
when :index, :new, :show, :edit
|
||||
proc { get method, params }
|
||||
when :create
|
||||
proc { post :create, params }
|
||||
when :update
|
||||
proc { put :update, params }
|
||||
when :destroy
|
||||
proc { put :destroy, params }
|
||||
end
|
||||
end
|
||||
|
||||
def eval_request
|
||||
instance_eval &self.class.instance_variable_get("@the_request")
|
||||
rescue ArgumentError # missing block
|
||||
try_shared_request_definition
|
||||
end
|
||||
alias do_request eval_request
|
||||
|
||||
def try_shared_request_definition
|
||||
shared_request
|
||||
rescue NameError
|
||||
if @implicit_request
|
||||
try_implicit_request
|
||||
else
|
||||
error_message = "Could not determine request definition for 'describe' context. "
|
||||
error_message << "Please use define_request or define a shared_request."
|
||||
raise ArgumentError, error_message
|
||||
end
|
||||
end
|
||||
|
||||
def try_implicit_request
|
||||
@implicit_request.call
|
||||
end
|
||||
|
||||
def get_response(&block)
|
||||
eval_request
|
||||
block.call(response) if block_given?
|
||||
response
|
||||
end
|
||||
|
||||
module ExampleGroupMethods
|
||||
# Defines a request at the example group ("describe") level to be evaluated in the examples. Example:
|
||||
#
|
||||
# define_request { get :index, params }
|
||||
#
|
||||
# <b>Note:</b> The following methods all define implicit requests: <tt>stub_index</tt>, <tt>stub_new</tt>,
|
||||
# <tt>stub_create</tt>, <tt>stub_show</tt>, <tt>stub_edit</tt>, <tt>stub_update</tt>, and
|
||||
# <tt>stub_destroy</tt>. Using them in your <tt>before</tt> blocks will allow you to forego
|
||||
# defining explicit requests using <tt>define_request</tt>. See
|
||||
# LuckySneaks::ControllerStubHelpers for information on these methods.
|
||||
def define_request(&block)
|
||||
raise ArgumentError, "Must provide a block to define a request!" unless block_given?
|
||||
@the_request = block
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
435
vendor/plugins/skinny_spec/lib/lucky_sneaks/controller_spec_helpers.rb
vendored
Normal file
435
vendor/plugins/skinny_spec/lib/lucky_sneaks/controller_spec_helpers.rb
vendored
Normal file
|
|
@ -0,0 +1,435 @@
|
|||
$:.unshift File.join(File.dirname(__FILE__), "..")
|
||||
require "skinny_spec"
|
||||
|
||||
module LuckySneaks
|
||||
module ControllerSpecHelpers # :nodoc:
|
||||
include LuckySneaks::CommonSpecHelpers
|
||||
include LuckySneaks::ControllerRequestHelpers
|
||||
include LuckySneaks::ControllerStubHelpers
|
||||
|
||||
def self.included(base)
|
||||
base.extend ExampleGroupMethods
|
||||
base.extend ControllerRequestHelpers::ExampleGroupMethods
|
||||
end
|
||||
|
||||
# Evaluates the specified block for each of the RESTful controller methods.
|
||||
# This is useful to spec that all controller methods redirect when no user is
|
||||
# logged in.
|
||||
def with_default_restful_actions(params = {}, &block)
|
||||
{
|
||||
:get => :index,
|
||||
:get => :new,
|
||||
:post => :create
|
||||
}.each do |method_id, message|
|
||||
self.send method_id, message, params
|
||||
block.call
|
||||
end
|
||||
{
|
||||
:get => :edit,
|
||||
:put => :update,
|
||||
:delete => :destroy
|
||||
}.each do |method_id, message|
|
||||
if params[:before]
|
||||
params.delete(:before).call
|
||||
end
|
||||
# Presuming any id will do
|
||||
self.send method_id, message, params.merge(:id => 1)
|
||||
block.call
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def create_ar_class_expectation(name, method, argument = nil, options = {})
|
||||
args = []
|
||||
if [:create, :update].include?(@controller_method)
|
||||
args << (argument.nil? ? valid_attributes : argument)
|
||||
else
|
||||
args << argument unless argument.nil?
|
||||
end
|
||||
args << options unless options.empty?
|
||||
if args.empty?
|
||||
return_value = class_for(name).send(method)
|
||||
class_for(name).should_receive(method).and_return(return_value)
|
||||
else
|
||||
return_value = class_for(name).send(method, *args)
|
||||
class_for(name).should_receive(method).with(*args).and_return(return_value)
|
||||
end
|
||||
end
|
||||
|
||||
def create_positive_ar_instance_expectation(name, method, *args)
|
||||
instance = instance_for(name)
|
||||
if args.empty?
|
||||
return_value = instance.send(method)
|
||||
instance.should_receive(method).and_return(true)
|
||||
else
|
||||
return_value = instance.send(method, *args)
|
||||
instance.should_receive(method).with(*args).and_return(true)
|
||||
end
|
||||
end
|
||||
|
||||
# These methods are designed to be used at the example group [read: "describe"] level
|
||||
# to simplify and DRY up common expectations.
|
||||
module ExampleGroupMethods
|
||||
# Creates an expectation that the controller method calls <tt>ActiveRecord::Base.find</tt>.
|
||||
# Examples:
|
||||
#
|
||||
# it_should_find :foos # => Foo.should_receive(:find).with(:all)
|
||||
# it_should_find :foos, :all # An explicit version of the above
|
||||
# it_should_find :foos, :conditions => {:foo => "bar"} # => Foo.should_receive(:find).with(:all, :conditions => {"foo" => "bar"}
|
||||
# it_should_find :foo # => Foo.should_recieve(:find).with(@foo.id.to_s)
|
||||
# it_should_find :foo, :params => "id" # => Foo.should_receive(:find).with(params[:id].to_s)
|
||||
# it_should_find :foo, 2 # => Foo.should_receive(:find).with("2")
|
||||
#
|
||||
# <b>Note:</b> All params (key and value) will be strings if they come from a form element and are handled
|
||||
# internally with this expectation.
|
||||
def it_should_find(name, *args)
|
||||
name_string = name.to_s
|
||||
name_message = if name_string == name_string.singularize
|
||||
"a #{name}"
|
||||
else
|
||||
name
|
||||
end
|
||||
it "should find #{name_message}" do
|
||||
options = args.extract_options!
|
||||
# Blech!
|
||||
argument = if param = params[options.delete(:params)]
|
||||
param.to_s
|
||||
else
|
||||
if args.first
|
||||
args.first
|
||||
elsif (instance = instance_variable_get("@#{name}")).is_a?(ActiveRecord::Base)
|
||||
instance.id.to_s
|
||||
else
|
||||
:all
|
||||
end
|
||||
end
|
||||
create_ar_class_expectation name, :find, argument, options
|
||||
eval_request
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the controller method calls <tt>ActiveRecord::Base.new</tt>.
|
||||
# Takes optional <tt>params</tt> for the initialization arguments. Example
|
||||
#
|
||||
# it_should_initialize :foo # => Foo.should_receive(:new)
|
||||
# it_should_initialize :foo, :params => :bar # => Foo.should_receive(:new).with(params[:bar])
|
||||
# it_should_initialize :foo, :bar => "baz" # => Foo.should_receive(:new).with(:bar => "baz")
|
||||
def it_should_initialize(name, options = {})
|
||||
it "should initialize a #{name}" do
|
||||
create_ar_class_expectation name, :new, params[options.delete(:params)], options
|
||||
eval_request
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the controller method calls <tt>ActiveRecord::Base#save</tt> on the
|
||||
# named instance. Example:
|
||||
#
|
||||
# it_should_save :foo # => @foo.should_receive(:save).and_return(true)
|
||||
#
|
||||
# <b>Note:</b> This helper should not be used to spec a failed <tt>save</tt> call. Use <tt>it_should_assign</tt>
|
||||
# instead, to verify that the instance is captured in an instance variable for the inevitable re-rendering
|
||||
# of the form template.
|
||||
def it_should_save(name)
|
||||
it "should save the #{name}" do
|
||||
create_positive_ar_instance_expectation name, :save
|
||||
eval_request
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the controller method calls <tt>ActiveRecord::Base#update_attributes</tt>
|
||||
# on the named instance. Takes optional argument for <tt>params</tt> to specify in the
|
||||
# expectation. Examples:
|
||||
#
|
||||
# it_should_update :foo # => @foo.should_receive(:update_attributes).and_return(true)
|
||||
# it_should_update :foo, :params => :bar # => @foo.should_receive(:update_attributes).with(params[:bar]).and_return(true)
|
||||
#
|
||||
# <b>Note:</b> This helper should not be used to spec a failed <tt>update_attributes</tt> call. Use
|
||||
# <tt>it_should_assign</tt> instead, to verify that the instance is captured in an instance variable
|
||||
# for the inevitable re-rendering of the form template.
|
||||
def it_should_update(name, options = {})
|
||||
it "should update the #{name}" do
|
||||
create_positive_ar_instance_expectation name, :update_attributes, params[options[:params]]
|
||||
eval_request
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the controller method calls <tt>ActiveRecord::Base#destroy</tt> on the named
|
||||
# instance. Example:
|
||||
#
|
||||
# it_should_destroy :foo # => @foo.should_receive(:destroy).and_return(true)
|
||||
#
|
||||
# <b>Note:</b> This helper should not be used to spec a failed <tt>destroy</tt> call. Use
|
||||
# <tt>it_should_assign</tt> instead, if you need to verify that the instance is captured in an instance
|
||||
# variable if it is re-rendered somehow. This is probably a really edge use case.
|
||||
def it_should_destroy(name, options = {})
|
||||
it "should delete the #{name}" do
|
||||
create_positive_ar_instance_expectation name, :destroy
|
||||
eval_request
|
||||
end
|
||||
end
|
||||
|
||||
# Creates expectation[s] that the controller method should assign the specified
|
||||
# instance variables along with any specified values. Examples:
|
||||
#
|
||||
# it_should_assign :foo # => assigns[:foo].should == @foo
|
||||
# it_should_assign :foo => "bar" # => assigns[:foo].should == "bar"
|
||||
# it_should_assign :foo => :nil # => assigns[:foo].should be_nil
|
||||
# it_should_assign :foo => :not_nil # => assigns[:foo].should_not be_nil
|
||||
# it_should_assign :foo => :undefined # => controller.send(:instance_variables).should_not include("@foo")
|
||||
#
|
||||
# Very special thanks to Rick Olsen for the basis of this code. The only reason I even
|
||||
# redefine it at all is purely an aesthetic choice for specs like "it should foo"
|
||||
# over ones like "it foos".
|
||||
def it_should_assign(*names)
|
||||
names.each do |name|
|
||||
if name.is_a?(Symbol)
|
||||
it_should_assign name => name
|
||||
elsif name.is_a?(Hash)
|
||||
name.each do |key, value|
|
||||
it_should_assign_instance_variable key, value
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Wraps the separate expectations <tt>it_should_find</tt> and <tt>it_should_assign</tt>
|
||||
# for simple cases. If you need more control over the parameters of the find, this
|
||||
# isn't the right helper method and you should write out the two expectations separately.
|
||||
def it_should_find_and_assign(*names)
|
||||
names.each do |name|
|
||||
it_should_find name
|
||||
it_should_assign name
|
||||
end
|
||||
end
|
||||
|
||||
# Wraps the separate expectations <tt>it_should_initialize</tt> and <tt>it_should_assign</tt>
|
||||
# for simple cases. If you need more control over the parameters of the initialization, this
|
||||
# isn't the right helper method and you should write out the two expectations separately.
|
||||
#
|
||||
# <b>Note:</b> This method is used for controller methods like <tt>new</tt>, where the instance
|
||||
# is initialized without being saved (this includes failed <tt>create</tt> requests).
|
||||
# If you want to spec that the controller method successfully saves the instance,
|
||||
# please use <tt>it_should_initialize_and_save</tt>.
|
||||
def it_should_initialize_and_assign(*names)
|
||||
names.each do |name|
|
||||
it_should_initialize name
|
||||
it_should_assign name
|
||||
end
|
||||
end
|
||||
|
||||
# Wraps the separate expectations <tt>it_should_initialize</tt> and <tt>it_should_save</tt>
|
||||
# for simple cases. If you need more control over the parameters of the initialization, this
|
||||
# isn't the right helper method and you should write out the two expectations separately.
|
||||
#
|
||||
# <b>Note:</b> This method is used for controller methods like <tt>create</tt>, where the instance
|
||||
# is initialized and successfully saved. If you want to spec that the instance is created
|
||||
# but not saved, just use <tt>it_should_initialize_and_assign</tt>.
|
||||
def it_should_initialize_and_save(*names)
|
||||
names.each do |name|
|
||||
it_should_initialize name
|
||||
it_should_save name
|
||||
end
|
||||
end
|
||||
|
||||
# Wraps the separate expectations <tt>it_should_find</tt> and <tt>it_should_update</tt>
|
||||
# for simple cases. If you need more control over the parameters of the find, this
|
||||
# isn't the right helper method and you should write out the two expectations separately.
|
||||
#
|
||||
# <b>Note:</b> This method is used for controller methods like <tt>update</tt>, where the
|
||||
# instance is loaded from the database and successfully saved. If you want to spec that the
|
||||
# instance is found but not saved, just use <tt>it_should_find_and_assign</tt>.
|
||||
def it_should_find_and_update(*names)
|
||||
names.each do |name|
|
||||
it_should_find name
|
||||
it_should_update name
|
||||
end
|
||||
end
|
||||
|
||||
# Wraps the separate expectations <tt>it_should_find</tt> and <tt>it_should_destroy</tt>
|
||||
# for simple cases. If you need more control over the parameters of the find, this
|
||||
# isn't the right helper method and you should write out the two expectations separately.
|
||||
def it_should_find_and_destroy(*names)
|
||||
names.each do |name|
|
||||
it_should_find name
|
||||
it_should_destroy name
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the specified collection (<tt>flash</tt> or <tt>session</tt>)
|
||||
# contains the specified key and value. To specify that the collection should be set
|
||||
# to <tt>nil</tt>, specify the value as :nil instead.
|
||||
def it_should_set(collection, key, value = nil, &block)
|
||||
it "should set #{collection}[:#{key}]" do
|
||||
# Allow flash.now[:foo] to remain in the flash
|
||||
flash.stub!(:sweep) if collection == :flash
|
||||
eval_request
|
||||
if value
|
||||
if value == :nil
|
||||
self.send(collection)[key].should be_nil
|
||||
else
|
||||
self.send(collection)[key].should == value
|
||||
end
|
||||
elsif block_given?
|
||||
self.send(collection)[key].should == block.call
|
||||
else
|
||||
self.send(collection)[key].should_not be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Wraps <tt>it_should_set :flash</tt>. To specify that the collection should be set
|
||||
# to <tt>nil</tt>, specify the value as :nil instead.
|
||||
def it_should_set_flash(name, value = nil, &block)
|
||||
it_should_set :flash, name, value, &block
|
||||
end
|
||||
|
||||
# Wraps <tt>it_should_set :session</tt>. To specify that the collection should be set
|
||||
# to <tt>nil</tt>, specify the value as :nil instead.
|
||||
def it_should_set_session(name, value = nil, &block)
|
||||
it_should_set :session, name, value, &block
|
||||
end
|
||||
|
||||
# Wraps the various <tt>it_should_render_<i>foo</i></tt> methods:
|
||||
# <tt>it_should_render_template</tt>, <tt>it_should_render_partial</tt>,
|
||||
# <tt>it_should_render_xml</tt>, <tt>it_should_render_json</tt>,
|
||||
# <tt>it_should_render_formatted</tt>, and <tt>it_should_render_nothing</tt>.
|
||||
def it_should_render(render_method, *args)
|
||||
send "it_should_render_#{render_method}", *args
|
||||
end
|
||||
|
||||
# Creates an expectation that the controller method renders the specified template.
|
||||
# Accepts the following options which create additional expectations.
|
||||
#
|
||||
# <tt>:content_type</tt>:: Creates an expectation that the Content-Type header for the response
|
||||
# matches the one specified
|
||||
# <tt>:status</tt>:: Creates an expectation that the HTTP status for the response
|
||||
# matches the one specified
|
||||
def it_should_render_template(name, options = {})
|
||||
create_status_expectation options[:status] if options[:status]
|
||||
it "should render '#{name}' template" do
|
||||
eval_request
|
||||
response.should render_template(name)
|
||||
end
|
||||
create_content_type_expectation(options[:content_type]) if options[:content_type]
|
||||
end
|
||||
|
||||
# Creates an expectation that the controller method renders the specified partial.
|
||||
# Accepts the following options which create additional expectations.
|
||||
#
|
||||
# <tt>:content_type</tt>:: Creates an expectation that the Content-Type header for the response
|
||||
# matches the one specified
|
||||
# <tt>:status</tt>:: Creates an expectation that the HTTP status for the response
|
||||
# matches the one specified
|
||||
def it_should_render_partial(name, options = {})
|
||||
create_status_expectation options[:status] if options[:status]
|
||||
it "should render '#{name}' partial" do
|
||||
controller.expect_render(:partial => name)
|
||||
eval_request
|
||||
end
|
||||
create_content_type_expectation(options[:content_type]) if options[:content_type]
|
||||
end
|
||||
|
||||
# Creates an expectation that the controller method renders the specified record via <tt>to_xml</tt>.
|
||||
# Accepts the following options which create additional expectations.
|
||||
#
|
||||
# <tt>:content_type</tt>:: Creates an expectation that the Content-Type header for the response
|
||||
# matches the one specified
|
||||
# <tt>:status</tt>:: Creates an expectation that the HTTP status for the response
|
||||
# matches the one specified
|
||||
def it_should_render_xml(record = nil, options = {}, &block)
|
||||
it_should_render_formatted :xml, record, options, &block
|
||||
end
|
||||
|
||||
# Creates an expectation that the controller method renders the specified record via <tt>to_json</tt>.
|
||||
# Accepts the following options which create additional expectations.
|
||||
#
|
||||
# <tt>:content_type</tt>:: Creates an expectation that the Content-Type header for the response
|
||||
# matches the one specified
|
||||
# <tt>:status</tt>:: Creates an expectation that the HTTP status for the response
|
||||
# matches the one specified
|
||||
def it_should_render_json(record = nil, options = {}, &block)
|
||||
it_should_render_formatted :json, record, options, &block
|
||||
end
|
||||
|
||||
# Called internally by <tt>it_should_render_xml</tt> and <tt>it_should_render_json</tt>
|
||||
# but should not really be called much externally unless you have defined your own
|
||||
# formats with a matching <tt>to_foo</tt> method on the record.
|
||||
#
|
||||
# Which is probably never.
|
||||
def it_should_render_formatted(format, record = nil, options = {}, &block)
|
||||
create_status_expectation options[:status] if options[:status]
|
||||
it "should render #{format.inspect}" do
|
||||
if record.is_a?(Hash)
|
||||
options = record
|
||||
record = nil
|
||||
end
|
||||
if record.nil? && !block_given?
|
||||
raise ArgumentError, "it_should_render must be called with either a record or a block and neither was given."
|
||||
else
|
||||
if record
|
||||
pieces = record.to_s.split(".")
|
||||
record = instance_variable_get("@#{pieces.shift}")
|
||||
record = record.send(pieces.shift) until pieces.empty?
|
||||
end
|
||||
block ||= proc { record.send("to_#{format}") }
|
||||
get_response do |response|
|
||||
response.should have_text(block.call)
|
||||
end
|
||||
end
|
||||
end
|
||||
create_content_type_expectation(options[:content_type]) if options[:content_type]
|
||||
end
|
||||
|
||||
# Creates an expectation that the controller method returns a blank page. You'd already
|
||||
# know when and why to use this so I'm not typing it out.
|
||||
def it_should_render_nothing(options = {})
|
||||
create_status_expectation options[:status] if options[:status]
|
||||
it "should render :nothing" do
|
||||
get_response do |response|
|
||||
response.body.strip.should be_blank
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the controller method redirects to the specified destination. Example:
|
||||
#
|
||||
# it_should_redirect_to { foos_url }
|
||||
#
|
||||
# <b>Note:</b> This method takes a block to evaluate the route in the example
|
||||
# context rather than the example group context.
|
||||
def it_should_redirect_to(hint = nil, &route)
|
||||
if hint.nil? && route.respond_to?(:to_ruby)
|
||||
hint = route.to_ruby.gsub(/(^proc \{)|(\}$)/, '').strip
|
||||
end
|
||||
it "should redirect to #{(hint || route)}" do
|
||||
eval_request
|
||||
response.should redirect_to(instance_eval(&route))
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def it_should_assign_instance_variable(name, value)
|
||||
expectation_proc = case value
|
||||
when :nil
|
||||
proc { assigns[name].should be_nil }
|
||||
when :not_nil
|
||||
proc { assigns[name].should_not be_nil }
|
||||
when :undefined
|
||||
proc { controller.send(:instance_variables).should_not include("@{name}") }
|
||||
when Symbol
|
||||
if (instance_variable = instance_variable_get("@#{name}")).nil?
|
||||
proc { assigns[name].should_not be_nil }
|
||||
else
|
||||
proc { assigns[name].should == instance_variable }
|
||||
end
|
||||
else
|
||||
proc { assigns[name].should == value }
|
||||
end
|
||||
it "should #{value == :nil ? 'not ' : ''}assign @#{name}" do
|
||||
eval_request
|
||||
instance_eval &expectation_proc
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
199
vendor/plugins/skinny_spec/lib/lucky_sneaks/controller_stub_helpers.rb
vendored
Normal file
199
vendor/plugins/skinny_spec/lib/lucky_sneaks/controller_stub_helpers.rb
vendored
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
module LuckySneaks # :nodoc:
|
||||
# These methods are designed to be used in your example <tt>before</tt> blocks to accomplish
|
||||
# a whole lot of functionality with just a tiny bit of effort. The methods which correspond
|
||||
# to the controller methods perform the most duties as they create the mock_model instances,
|
||||
# stub out all the necessary methods, and also create implicit requests to DRY up your spec
|
||||
# file even more. You are encouraged to use these methods to setup the basic calls for your
|
||||
# resources and only resort to the other methods when mocking and stubbing secondary objects
|
||||
# and calls.
|
||||
#
|
||||
# Both <tt>stub_create</tt> and <tt>stub_update</tt> benefit from having a <tt>valid_attributes</tt>
|
||||
# method defined at the top level of your example groups, ie the top-most "describe" block
|
||||
# of the spec file. If you did not generate your specs with <tt>skinny_scaffold</tt> or
|
||||
# <tt>skinny_resourceful</tt> generators, you can simply write a method like the following
|
||||
# for yourself:
|
||||
#
|
||||
# def valid_attributes
|
||||
# {
|
||||
# "foo" => "bar",
|
||||
# "baz" => "quux"
|
||||
# }
|
||||
# end
|
||||
#
|
||||
# Note this method employs strings as both the key and values to best replicate the way
|
||||
# they are used in actual controllers where the params will come from a form.
|
||||
module ControllerStubHelpers
|
||||
# Stubs out <tt>find :all</tt> and returns a collection of <tt>mock_model</tt>
|
||||
# instances of that class. Accepts the following options:
|
||||
#
|
||||
# <b>:format</b>:: Format of the request. Used to only add <tt>to_xml</tt> and
|
||||
# <tt>to_json</tt> when actually needed.
|
||||
# <b>:size</b>:: Number of instances to return in the result. Default is 3.
|
||||
# <b>:stub</b>:: Additional methods to stub on the instances
|
||||
#
|
||||
# Any additional options will be passed as arguments to the class find.
|
||||
# You will want to make sure to pass those arguments to the <tt>it_should_find</tt> spec as well.
|
||||
def stub_find_all(klass, options = {})
|
||||
returning(Array.new(options[:size] || 3){mock_model(klass)}) do |collection|
|
||||
stub_out klass, options.delete(:stub)
|
||||
if format = options.delete(:format)
|
||||
stub_formatted collection, format
|
||||
params[:format] = format
|
||||
end
|
||||
if options.empty?
|
||||
klass.stub!(:find).with(:all).and_return(collection)
|
||||
else
|
||||
klass.stub!(:find).with(:all, options).and_return(collection)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Alias for <tt>stub_find_all</tt> but additionally defines an implicit request <tt>get :index</tt>.
|
||||
def stub_index(klass, options = {})
|
||||
define_implicit_request :index
|
||||
stub_find_all klass, options
|
||||
end
|
||||
|
||||
# Stubs out <tt>new</tt> method and returns a <tt>mock_model</tt> instance marked as a new record.
|
||||
# Accepts the following options:
|
||||
#
|
||||
# <b>:format</b>:: Format of the request. Used to only add <tt>to_xml</tt> and
|
||||
# <tt>to_json</tt> when actually needed.
|
||||
# <b>:stub</b>:: Additional methods to stub on the instances
|
||||
#
|
||||
# It also accepts some options used to stub out <tt>save</tt> with a specified <tt>true</tt>
|
||||
# or <tt>false</tt> but you should be using <tt>stub_create</tt> in that case.
|
||||
def stub_initialize(klass, options = {})
|
||||
returning mock_model(klass) do |member|
|
||||
stub_out member, options.delete(:stub)
|
||||
if format = options[:format]
|
||||
stub_formatted member, format
|
||||
params[:format] = format
|
||||
end
|
||||
klass.stub!(:new).and_return(member)
|
||||
if options[:stub_save]
|
||||
stub_ar_method member, :save, options[:return]
|
||||
klass.stub!(:new).with(params[options[:params]]).and_return(member)
|
||||
else
|
||||
member.stub!(:new_record?).and_return(true)
|
||||
member.stub!(:id).and_return(nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Alias for <tt>stub_initialize</tt> which additionally defines an implicit request <tt>get :new</tt>.
|
||||
def stub_new(klass, options = {})
|
||||
define_implicit_request :new
|
||||
stub_initialize klass, options
|
||||
end
|
||||
|
||||
# Alias for <tt>stub_initialize</tt> which additionally defines an implicit request <tt>post :create</tt>.
|
||||
#
|
||||
# <b>Note:</b> If <tt>stub_create<tt> is provided an optional <tt>:params</tt> hash
|
||||
# or the method <tt>valid_attributes</tt> is defined within its scope,
|
||||
# those params will be added to the example's <tt>params</tt> object. If <i>neither</i>
|
||||
# are provided an <tt>ArgumentError</tt> will be raised.
|
||||
def stub_create(klass, options = {})
|
||||
define_implicit_request :create
|
||||
if options[:params].nil?
|
||||
if self.respond_to?(:valid_attributes)
|
||||
params[klass.name.underscore.to_sym] = valid_attributes
|
||||
options[:params] = valid_attributes
|
||||
else
|
||||
error_message = "Params for creating #{klass} could not be determined. "
|
||||
error_message << "Please define valid_attributes method in the base 'describe' block "
|
||||
error_message << "or manually set params in the before block."
|
||||
raise ArgumentError, error_message
|
||||
end
|
||||
end
|
||||
stub_initialize klass, options.merge(:stub_save => true)
|
||||
end
|
||||
|
||||
# Stubs out <tt>find</tt> and returns a single <tt>mock_model</tt>
|
||||
# instances of that class. Accepts the following options:
|
||||
#
|
||||
# <b>:format</b>:: Format of the request. Used to only add <tt>to_xml</tt> and
|
||||
# <tt>to_json</tt> when actually needed.
|
||||
# <b>:stub</b>:: Additional methods to stub on the instances
|
||||
#
|
||||
# Any additional options will be passed as arguments to <tt>find</tt>.You will want
|
||||
# to make sure to pass those arguments to the <tt>it_should_find</tt> spec as well.
|
||||
#
|
||||
# <b>Note:</b> The option <tt>:stub_ar</tt> is used internally by <tt>stub_update</tt>
|
||||
# and <tt>stub_destroy</tt>. If you need to stub <tt>update_attributes</tt> or
|
||||
# <tt>destroy</tt> you should be using the aforementioned methods instead.
|
||||
def stub_find_one(klass, options = {})
|
||||
returning mock_model(klass) do |member|
|
||||
stub_out member, options.delete(:stub)
|
||||
if options[:format]
|
||||
stub_formatted member, options[:format]
|
||||
params[:format] = options[:format]
|
||||
end
|
||||
if options[:current_object]
|
||||
params[:id] = member.id
|
||||
if options[:stub_ar]
|
||||
stub_ar_method member, options[:stub_ar], options[:return]
|
||||
end
|
||||
end
|
||||
klass.stub!(:find).with(member.id.to_s).and_return(member)
|
||||
end
|
||||
end
|
||||
|
||||
# Alias for <tt>stub_find_one</tt> which additionally defines an implicit request <tt>get :show</tt>.
|
||||
def stub_show(klass, options = {})
|
||||
define_implicit_request :show
|
||||
stub_find_one klass, options.merge(:current_object => true)
|
||||
end
|
||||
|
||||
# Alias for <tt>stub_find_one</tt> which additionally defines an implicit request <tt>get :edit</tt>.
|
||||
def stub_edit(klass, options = {})
|
||||
define_implicit_request :edit
|
||||
stub_find_one klass, options.merge(:current_object => true)
|
||||
end
|
||||
|
||||
# Alias for <tt>stub_find_one</tt> which additionally defines an implicit request <tt>put :update</tt>
|
||||
# and stubs out the <tt>update_attribute</tt> method on the instance as well.
|
||||
#
|
||||
# <b>Note:</b> If <tt>stub_update<tt> is provided an optional <tt>:params</tt> hash
|
||||
# or the method <tt>valid_attributes</tt> is defined within its scope,
|
||||
# those params will be added to the example's <tt>params</tt> object. If <i>neither</i>
|
||||
# are provided an <tt>ArgumentError</tt> will be raised.
|
||||
def stub_update(klass, options = {})
|
||||
define_implicit_request :update
|
||||
stub_find_one klass, options.merge(:current_object => true, :stub_ar => :update_attributes)
|
||||
end
|
||||
|
||||
# Alias for <tt>stub_find_one</tt> which additionally defines an implicit request <tt>delete :destroy</tt>
|
||||
# and stubs out the <tt>destroy</tt> method on the instance as well.
|
||||
def stub_destroy(klass, options = {})
|
||||
define_implicit_request :destroy
|
||||
stub_find_one klass, options.merge(:current_object => true, :stub_ar => :destroy)
|
||||
end
|
||||
|
||||
# Stubs <tt>to_xml</tt> or <tt>to_json</tt> respectively based on <tt>format</tt> argument.
|
||||
def stub_formatted(object, format)
|
||||
return unless format
|
||||
object.stub!("to_#{format}").and_return("#{object.class} formatted as #{format}")
|
||||
end
|
||||
|
||||
private
|
||||
# Stubs out multiple methods. You shouldn't be calling this yourself and if you do
|
||||
# you should be able to understand the code yourself, right?
|
||||
def stub_out(object, stubs = {})
|
||||
return if stubs.nil?
|
||||
stubs.each do |method, value|
|
||||
if value
|
||||
object.stub!(method).and_return(value)
|
||||
else
|
||||
object.stub!(method)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Stubs out ActiveRecord::Base methods like #save, #update_attributes, etc
|
||||
# that may be called on a found or instantiated mock_model instance.
|
||||
def stub_ar_method(object, method, return_value)
|
||||
object.stub!(method).and_return(return_value ? false : true)
|
||||
end
|
||||
end
|
||||
end
|
||||
326
vendor/plugins/skinny_spec/lib/lucky_sneaks/model_spec_helpers.rb
vendored
Normal file
326
vendor/plugins/skinny_spec/lib/lucky_sneaks/model_spec_helpers.rb
vendored
Normal file
|
|
@ -0,0 +1,326 @@
|
|||
$:.unshift File.join(File.dirname(__FILE__), "..")
|
||||
require "skinny_spec"
|
||||
|
||||
module LuckySneaks
|
||||
# These methods are designed to be used in your example [read: "it"] blocks
|
||||
# to make your model specs a little more DRY. You might also be interested
|
||||
# in checking out the example block [read: "describe"] level versions in of these
|
||||
# methods which can DRY things up even more:
|
||||
# LuckySneaks::ModelSpecHelpers::ExampleGroupLevelMethods
|
||||
#
|
||||
# <b>Note:</b> The validation matchers are only meant to be used for simple validation checking
|
||||
# not as a one-size-fits-all solution.
|
||||
module ModelSpecHelpers
|
||||
include LuckySneaks::CommonSpecHelpers
|
||||
|
||||
def self.included(base) # :nodoc:
|
||||
base.extend ExampleGroupLevelMethods
|
||||
end
|
||||
|
||||
class AssociationMatcher # :nodoc:
|
||||
def initialize(associated, macro)
|
||||
@associated = associated
|
||||
@macro = macro
|
||||
@options = {}
|
||||
end
|
||||
|
||||
def matches?(main_model)
|
||||
unless main_model.respond_to?(:reflect_on_association)
|
||||
if main_model.class.respond_to?(:reflect_on_association)
|
||||
main_model = main_model.class
|
||||
else
|
||||
@not_model = main_model
|
||||
return false
|
||||
end
|
||||
end
|
||||
if @association = main_model.reflect_on_association(@associated)
|
||||
@options.all?{|k, v| @association.options[k] == v ||
|
||||
[@association.options[k]] == v} # Stupid to_a being obsoleted!
|
||||
end
|
||||
end
|
||||
|
||||
def failure_message
|
||||
if @not_model
|
||||
" expected: #{@not_model} to be a subclass of ActiveRecord::Base class, but was not"
|
||||
elsif @association
|
||||
" expected: #{association_with(@options)}\n got: #{association_with(@association.options)}"
|
||||
else
|
||||
" expected: #{association_with(@options)}, but the association does not exist"
|
||||
end
|
||||
end
|
||||
|
||||
def negative_failure_message
|
||||
if @association
|
||||
" expected: #{association_with(@options)}\n got: #{association_with(@association.options)}"
|
||||
else
|
||||
" expected: #{association_with(@options)} to not occur but it does"
|
||||
end
|
||||
end
|
||||
|
||||
# The following public methods are chainable extensions on the main matcher
|
||||
# Examples:
|
||||
#
|
||||
# Foo.should have_many(:bars).through(:foobars).with_dependent(:destroy)
|
||||
# Bar.should belong_to(:baz).with_class_name("Unbaz")
|
||||
def through(through_model)
|
||||
@options[:through] = through_model
|
||||
self
|
||||
end
|
||||
|
||||
def and_includes(included_models)
|
||||
@options[:include] = included_models
|
||||
self
|
||||
end
|
||||
|
||||
def and_extends(*modules)
|
||||
@options[:extends] = modules
|
||||
self
|
||||
end
|
||||
|
||||
def with_counter_cache(counter_cache = false)
|
||||
if counter_cache
|
||||
@options[:counter_cache] = counter_cache
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
def uniq(*irrelevant_args)
|
||||
@options[:uniq] = true
|
||||
self
|
||||
end
|
||||
alias and_is_unique uniq
|
||||
alias with_unique uniq
|
||||
|
||||
def polymorphic(*irrelevant_args)
|
||||
@options[:polymorphic] = true
|
||||
self
|
||||
end
|
||||
alias and_is_polymorphic polymorphic
|
||||
alias with_polymorphic polymorphic
|
||||
|
||||
def as(interface)
|
||||
@options[:as] = interface
|
||||
end
|
||||
|
||||
# Use this to just specify the options as a hash.
|
||||
# Note: It will completely override any previously set options
|
||||
def with_options(options = {})
|
||||
options.each{|k, v| @options[k] = v}
|
||||
self
|
||||
end
|
||||
|
||||
private
|
||||
# Takes care of methods like with_dependent(:destroy)
|
||||
def method_missing(method_id, *args, &block)
|
||||
method_name = method_id.to_s
|
||||
if method_name =~ /^with_(.*)/
|
||||
@options[$1.to_sym] = args
|
||||
self
|
||||
else
|
||||
super method_id, *args, &block
|
||||
end
|
||||
end
|
||||
|
||||
def association_with(options)
|
||||
option_string = (options.nil? || options.empty?) ? "" : options.inspect
|
||||
unless option_string.blank?
|
||||
option_string.sub! /^\{(.*)\}$/, ', \1'
|
||||
option_string.gsub! /\=\>/, ' => '
|
||||
end
|
||||
"#{@macro} :#{@associated}#{option_string}"
|
||||
end
|
||||
end
|
||||
|
||||
# Creates matcher that checks if the receiver has a <tt>belongs_to</tt> association
|
||||
# with the specified model.
|
||||
#
|
||||
# <b>Note:</b> The argument should be a symbol as in the model's association definition
|
||||
# and not the model's class name.
|
||||
def belong_to(model)
|
||||
AssociationMatcher.new model, :belongs_to
|
||||
end
|
||||
|
||||
# Creates matcher that checks if the receiver has a <tt>have_one</tt> association
|
||||
# with the specified model.
|
||||
#
|
||||
# <b>Note:</b> The argument should be a symbol as in the model's association definition
|
||||
# and not the model's class name.
|
||||
def have_one(model)
|
||||
AssociationMatcher.new model, :has_one
|
||||
end
|
||||
|
||||
# Creates matcher that checks if the receiver has a <tt>have_many</tt> association
|
||||
# with the specified model.
|
||||
#
|
||||
# <b>Note:</b> The argument should be a symbol as in the model's association definition
|
||||
# and not the model's class name.
|
||||
def have_many(models)
|
||||
AssociationMatcher.new models, :has_many
|
||||
end
|
||||
|
||||
# Creates matcher that checks if the receiver has a <tt>have_and_belong_to_many</tt> association
|
||||
# with the specified model.
|
||||
#
|
||||
# <b>Note:</b> The argument should be a symbol as in the model's association definition
|
||||
# and not the model's class name.
|
||||
def have_and_belong_to_many(models)
|
||||
AssociationMatcher.new models, :has_and_belongs_to_many
|
||||
end
|
||||
|
||||
private
|
||||
def class_or_instance
|
||||
@model_spec_class_or_instance ||= class_for(self.class.description_text) || instance
|
||||
end
|
||||
|
||||
def instance
|
||||
@model_spec_instance ||= instance_for(self.class.description_text)
|
||||
end
|
||||
|
||||
# These methods are designed to be used at the example group [read: "describe"] level
|
||||
# to simplify and DRY up common expectations. Most of these methods are wrappers for
|
||||
# matchers which can also be used on the example level [read: within an "it" block]. See
|
||||
# LuckySneaks::ModelSpecHelpers for more information.
|
||||
module ExampleGroupLevelMethods
|
||||
# Creates an expectation that the current model being spec'd has a <tt>belongs_to</tt>
|
||||
# association with the specified model.
|
||||
#
|
||||
# <b>Note:</b> The argument should be a symbol as in the model's association definition
|
||||
# and not the model's class name.
|
||||
def it_should_belong_to(model)
|
||||
it "should belong to a #{model}" do
|
||||
class_or_instance.should belong_to(model)
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the current model being spec'd has a <tt>have_one</tt>
|
||||
# association with the specified model.
|
||||
#
|
||||
# <b>Note:</b> The argument should be a symbol as in the model's association definition
|
||||
# and not the model's class name.
|
||||
def it_should_have_one(model)
|
||||
it "should have one #{model}" do
|
||||
class_or_instance.should have_one(model)
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the current model being spec'd has a <tt>have_many</tt>
|
||||
# association with the specified model.
|
||||
#
|
||||
# <b>Note:</b> The argument should be a symbol as in the model's association definition
|
||||
# and not the model's class name.
|
||||
def it_should_have_many(models)
|
||||
it "should have many #{models}" do
|
||||
class_or_instance.should have_many(models)
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the current model being spec'd has a <tt>have_and_belong_to_many</tt>
|
||||
# association with the specified model.
|
||||
#
|
||||
# <b>Note:</b> The argument should be a symbol as in the model's association definition
|
||||
# and not the model's class name.
|
||||
def it_should_have_and_belong_to_many(models)
|
||||
it "should have and belong to many #{models}" do
|
||||
class_or_instance.should have_and_belong_to_many(models)
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the current model being spec'd <tt>validates_presence_of</tt>
|
||||
# the specified attribute. Takes an optional custom message to match the one in the model's
|
||||
# validation.
|
||||
def it_should_validate_presence_of(attribute, message = ActiveRecord::Errors.default_error_messages[:blank])
|
||||
it "should not be valid if #{attribute} is blank" do
|
||||
instance.send "#{attribute}=", nil
|
||||
instance.errors_on(attribute).should include(message)
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the current model being spec'd <tt>validates_numericality_of</tt>
|
||||
# the specified attribute. Takes an optional custom message to match the one in the model's
|
||||
# validation.
|
||||
def it_should_validate_numericality_of(attribute, message = ActiveRecord::Errors.default_error_messages[:not_a_number])
|
||||
it "should validate #{attribute} is a numeric" do
|
||||
instance.send "#{attribute}=", "NaN"
|
||||
instance.errors_on(attribute).should include(message)
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the current model being spec'd <tt>validates_confirmation_of</tt>
|
||||
# the specified attribute. Takes an optional custom message to match the one in the model's
|
||||
# validation.
|
||||
def it_should_validate_confirmation_of(attribute, message = ActiveRecord::Errors.default_error_messages[:confirmation])
|
||||
it "should validate confirmation of #{attribute}" do
|
||||
dummy_value = dummy_value_for(instance, attribute) || "try a string"
|
||||
instance.send "#{attribute}=", dummy_value
|
||||
instance.send "#{attribute}_confirmation=", dummy_value.succ
|
||||
instance.errors_on(attribute).should include(message)
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the current model being spec'd <tt>validates_uniqueness_of</tt>
|
||||
# the specified attribute. Takes an optional custom message to match the one in the model's
|
||||
# validation.
|
||||
#
|
||||
# <b>Note:</b> This method will fail completely if <tt>valid_attributes</tt>
|
||||
# does not provide all the attributes needed to create a valid record.
|
||||
def it_should_validate_uniqueness_of(attribute, message = ActiveRecord::Errors.default_error_messages[:taken])
|
||||
it "should validate #{attribute} confirmation" do
|
||||
previous_instance = class_for(self.class.description_text).create!(valid_attributes)
|
||||
instance.attributes = valid_attributes
|
||||
instance.errors_on(attribute).should include(message)
|
||||
previous_instance.destroy
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the current model being spec'd accepts the specified values as
|
||||
# valid for the specified attribute. This is most likely used with <tt>validates_format_of</tt>
|
||||
# but there's nothing saying it couldn't be another validation.
|
||||
def it_should_accept_as_valid(attribute, *values)
|
||||
values.each do |value|
|
||||
value_inspect = case value
|
||||
when String : "'#{value}'"
|
||||
when NilClass : "nil"
|
||||
else value
|
||||
end
|
||||
it "should accept #{value_inspect} as a valid #{attribute}" do
|
||||
instance.send "#{attribute}=", value
|
||||
instance.errors_on(attribute).should == []
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the current model being spec'd does not accept the specified
|
||||
# values as valid for the specified attribute. This is most likely used with
|
||||
# <tt>validates_format_of</tt> but there's nothing saying it couldn't be another validation.
|
||||
# Takes an optional argument <tt>:message => "some custom error messsage"</tt> for
|
||||
# spec'ing the actual error message.
|
||||
def it_should_not_accept_as_valid(attribute, *values)
|
||||
options = values.extract_options!
|
||||
values.each do |value|
|
||||
value_inspect = case value
|
||||
when String : "'#{value}'"
|
||||
when NilClass : "nil"
|
||||
else value
|
||||
end
|
||||
it "should not accept #{value_inspect} as a valid #{attribute}" do
|
||||
instance.send "#{attribute}=", value
|
||||
if options[:message]
|
||||
instance.errors_on(attribute).should include(options[:message])
|
||||
else
|
||||
instance.should have_at_least(1).errors_on(attribute)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
# Creates an expectation that the current model being spec'd doesn't allow mass-assignment
|
||||
# of the specified attribute.
|
||||
def it_should_not_mass_assign(attribute)
|
||||
it "should not allow mass-assignment of #{attribute}" do
|
||||
lambda {
|
||||
instance.send :attributes=, {attribute => dummy_value_for(instance, attribute)}
|
||||
}.should_not change(instance, attribute)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
460
vendor/plugins/skinny_spec/lib/lucky_sneaks/view_spec_helpers.rb
vendored
Normal file
460
vendor/plugins/skinny_spec/lib/lucky_sneaks/view_spec_helpers.rb
vendored
Normal file
|
|
@ -0,0 +1,460 @@
|
|||
$:.unshift File.join(File.dirname(__FILE__), "..")
|
||||
require "skinny_spec"
|
||||
|
||||
module LuckySneaks
|
||||
# These methods are designed to be used in your example [read: "it"] blocks
|
||||
# to make your view specs less brittle and more DRY. You might also be interested
|
||||
# in checking out the example block [read: "describe"] level versions in of these
|
||||
# methods which can DRY things up even more:
|
||||
# LuckySneaks::ViewSpecHelpers::ExampleGroupLevelMethods
|
||||
module ViewSpecHelpers
|
||||
include LuckySneaks::CommonSpecHelpers
|
||||
include ActionController::PolymorphicRoutes
|
||||
|
||||
def self.included(base) # :nodoc:
|
||||
base.extend ExampleGroupLevelMethods
|
||||
end
|
||||
|
||||
# Wraps a matcher that checks if the receiver contains a <tt>FORM</tt> element with
|
||||
# its <tt>action</tt> attribute set to the specified path.
|
||||
def submit_to(path)
|
||||
have_tag("form[action=#{path}]")
|
||||
end
|
||||
|
||||
# Wraps a matcher that checks is the receiver contains any of several form elements
|
||||
# that would return sufficient named parameters to allow editing of the specified
|
||||
# attribute on the specified instance. Example:
|
||||
#
|
||||
# response.should allow_editing(@foo, "bar")
|
||||
#
|
||||
# can be satisfied by any of the following HTML elements:
|
||||
#
|
||||
# <input name="foo[bar]" type="text" />
|
||||
# <input name="foo[bar]" type="checkbox" />
|
||||
# <input name="foo[bar_ids][]" type="checkbox" />
|
||||
# <select name="foo[bar]"></select>
|
||||
# <textarea name="foo[bar]"></textarea>
|
||||
def allow_editing(instance, attribute)
|
||||
instance_name = instance.class.name.underscore.downcase
|
||||
if instance.send(attribute).is_a?(Time)
|
||||
have_tag(
|
||||
"input[name='#{instance_name}[#{attribute}]'],
|
||||
select[name=?]", /#{instance_name}\[#{attribute}\(.*\)\]/
|
||||
)
|
||||
else
|
||||
have_tag(
|
||||
"input[type='text'][name='#{instance_name}[#{attribute}]'],
|
||||
input[type='password'][name='#{instance_name}[#{attribute}]'],
|
||||
select[name='#{instance_name}[#{attribute}]'],
|
||||
textarea[name='#{instance_name}[#{attribute}]'],
|
||||
input[type='checkbox'][name='#{instance_name}[#{attribute}]'],
|
||||
input[type='checkbox'][name='#{instance_name}[#{attribute.to_s.tableize.singularize}_ids][]']"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
# Wraps a matcher that checks if the receiver contains an <tt>A</tt> element (link)
|
||||
# whose <tt>href</tt> attribute is set to the specified path or a <tt>FORM</tt>
|
||||
# element whose <tt>action</tt> attribute is set to the specified path.
|
||||
def have_link_or_button_to(path)
|
||||
have_tag(
|
||||
"a[href='#{path}'],
|
||||
form[action='#{path}'] input,
|
||||
form[action='#{path}'] button"
|
||||
)
|
||||
end
|
||||
alias have_link_to have_link_or_button_to
|
||||
alias have_button_to have_link_or_button_to
|
||||
|
||||
# Wraps <tt>have_link_or_button_to new_polymorphic_path<tt> for the specified class which
|
||||
# corresponds with the <tt>new</tt> method of the controller.
|
||||
#
|
||||
# <b>Note:</b> This method may takes a string or symbol representing the model's name
|
||||
# to send to <tt>have_link_or_button_to_show</tt> or the model's name itself.
|
||||
def have_link_or_button_to_new(name)
|
||||
have_link_or_button_to new_polymorphic_path(name.is_a?(ActiveRecord::Base) ? name : class_for(name))
|
||||
end
|
||||
|
||||
# Wraps <tt>have_link_or_button_to polymorphic_path(instance)<tt> which
|
||||
# corresponds with the <tt>show</tt> method of the controller.
|
||||
def have_link_or_button_to_show(instance)
|
||||
have_link_or_button_to polymorphic_path(instance)
|
||||
end
|
||||
alias have_link_to_show have_link_or_button_to_show
|
||||
alias have_button_to_show have_link_or_button_to_show
|
||||
|
||||
# Wraps <tt>have_link_or_button_to edit_polymorphic_path(instance)<tt> which
|
||||
# corresponds with the <tt>edit</tt> method of the controller.
|
||||
def have_link_or_button_to_edit(instance)
|
||||
have_link_or_button_to edit_polymorphic_path(instance)
|
||||
end
|
||||
alias have_link_to_edit have_link_or_button_to_edit
|
||||
alias have_button_to_edit have_link_or_button_to_edit
|
||||
|
||||
# Wraps a matcher that checks if the receiver contains the HTML created by Rails'
|
||||
# <tt>button_to</tt> helper: to wit, a <tt>FORM</tt> element whose <tt>action</tt>
|
||||
# attribute is pointed at the <tt>polymorphic_path</tt> of the instance
|
||||
# and contains an <tt>INPUT</tt> named "_method" with a value of "delete".
|
||||
def have_button_to_delete(instance)
|
||||
path = polymorphic_path(instance)
|
||||
have_tag(
|
||||
"form[action='#{path}'] input[name='_method'][value='delete'] + input,
|
||||
form[action='#{path}'] input[name='_method'][value='delete'] + button"
|
||||
)
|
||||
end
|
||||
|
||||
# Creates a <tt>mock_model</tt> instance and adds it to the <tt>assigns</tt> collection
|
||||
# using either the name passed as the first argument or the underscore version
|
||||
# of its class name. Accepts optional arguments to stub out additional methods
|
||||
# (and their return values) on the <tt>mock_model</tt> instance. Example:
|
||||
#
|
||||
# mock_and_assign(Foo, :stub => {:bar => "bar"})
|
||||
#
|
||||
# is the same as running <tt>assigns[:foo] = mock_model(Foo, :bar => "bar")</tt>.
|
||||
#
|
||||
# mock_and_assign(Foo, "special_foo", :stub => {:bar => "baz"})
|
||||
#
|
||||
# is the same as running <tt>assigns[:special_foo] = mock_model(Foo, :bar => "baz").
|
||||
#
|
||||
# <b>Note:</b> Adding to the assigns collection returns the object added, so this can
|
||||
# be chained a la <tt>@foo = mock_and_assign(Foo)</tt>.
|
||||
def mock_and_assign(klass, *args)
|
||||
options = args.extract_options!
|
||||
mocked = if options[:stub]
|
||||
mock_model(klass, options[:stub])
|
||||
else
|
||||
mock_model(klass)
|
||||
end
|
||||
yield mocked if block_given?
|
||||
self.assigns[args.first || "#{klass}".underscore] = mocked
|
||||
end
|
||||
|
||||
# Creates an array of <tt>mock_model</tt> instances in the manner of
|
||||
# <tt>mock_and_assign</tt>. Accepts <tt>option[:size]</tt> which sets the size
|
||||
# of the array (default is 3).
|
||||
def mock_and_assign_collection(klass, *args)
|
||||
options = args.dup.extract_options!
|
||||
return_me = Array.new(options[:size] || 3) do
|
||||
mocked = if options[:stub]
|
||||
mock_model(klass, options[:stub])
|
||||
else
|
||||
mock_model(klass)
|
||||
end
|
||||
yield mocked if block_given?
|
||||
mocked
|
||||
end
|
||||
self.assigns[args.first || "#{klass}".tableize] = return_me
|
||||
end
|
||||
|
||||
private
|
||||
def do_render
|
||||
if @the_template
|
||||
render @the_template
|
||||
elsif File.exists?(File.join(RAILS_ROOT, "app/views", self.class.description_text))
|
||||
render self.class.description_text
|
||||
else
|
||||
error_message = "Cannot determine template for render. "
|
||||
error_message << "Please define @the_template in the before block "
|
||||
error_message << "or name your describe block so that it indicates the correct template."
|
||||
raise NameError, error_message
|
||||
end
|
||||
end
|
||||
|
||||
# These methods are designed to be used at the example group [read: "describe"] level
|
||||
# to simplify and DRY up common expectations. Most of these methods are wrappers for
|
||||
# matchers which can also be used on the example level [read: within an "it" block]. See
|
||||
# LuckySneaks::ViewSpecHelpers for more information.
|
||||
module ExampleGroupLevelMethods
|
||||
include LuckySneaks::CommonSpecHelpers
|
||||
|
||||
# Creates an expectation which calls <tt>submit_to</tt> on the response
|
||||
# from rendering the template. See that method for more details.
|
||||
#
|
||||
# <b>Note:</b> This method takes a Proc to evaluate the route not simply a named route
|
||||
# helper, which would be undefined in the scope of the example block.
|
||||
def it_should_submit_to(hint = nil, &route)
|
||||
if hint.nil? && route.respond_to?(:to_ruby)
|
||||
hint = route.to_ruby.gsub(/(^proc \{)|(\}$)/, '').strip
|
||||
end
|
||||
it "should submit to #{(hint || route)}" do
|
||||
do_render
|
||||
response.should submit_to(instance_eval(&route))
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the template uses Rails' <tt>form_for</tt> to generate
|
||||
# the proper form action and method to create or update the specified object.
|
||||
#
|
||||
# <b>Note:</b> This method takes a string or symbol representing the instance
|
||||
# variable's name to create the expectation for <tt>form_for</tt>
|
||||
# not an instance variable, which would be nil in the scope of the example block.
|
||||
# If you use namespacing for your <tt>form_for</tt>, you'll have to manually write out
|
||||
# a similar spec.
|
||||
def it_should_have_form_for(name)
|
||||
it "should have a form_for(@#{name})" do
|
||||
template.should_receive(:form_for).with(instance_for(name))
|
||||
do_render
|
||||
end
|
||||
end
|
||||
|
||||
# Negative version of <tt>it_should_have_form_for</tt>. See that method for more
|
||||
# details.
|
||||
def it_should_not_have_form_for(name)
|
||||
it "should not have a form_for(@#{name})" do
|
||||
template.should_not_receive(:form_for).with(instance_for(name))
|
||||
do_render
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation which calls <tt>allow_editing</tt> on the response
|
||||
# from rendering the template. See that method for more details.
|
||||
#
|
||||
# <b>Note:</b> This method takes a string or symbol representing the instance
|
||||
# variable's name to send to <tt>allow_editing</tt>
|
||||
# not an instance variable, which would be nil in the scope of the example block.
|
||||
def it_should_allow_editing(name, method)
|
||||
it "should allow editing of @#{name}##{method}" do
|
||||
do_render
|
||||
response.should allow_editing(instance_for(name), method)
|
||||
end
|
||||
end
|
||||
|
||||
# Negative version of <tt>it_should_allow_editing</tt>. See that method for more
|
||||
# details.
|
||||
def it_should_not_allow_editing(name, method)
|
||||
it "should not allow editing of @#{name}##{method}" do
|
||||
do_render
|
||||
response.should_not allow_editing(instance_for(name), method)
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the rendered template contains a <tt>FORM</tt> element
|
||||
# (<tt>INPUT</tt>, <tt>TEXTAREA</tt>, or <tt>SELECT</tt>) with the specified name.
|
||||
def it_should_have_form_element_for(name)
|
||||
it "should have a form element named '#{name}'" do
|
||||
do_render
|
||||
response.should have_tag(
|
||||
"form input[name='#{name}'],
|
||||
form textarea[name='#{name}'],
|
||||
form select[name='#{name}']"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
# Negative version of <tt>it_should_have_form_element_for</tt>. See that method
|
||||
# for more details.
|
||||
def it_should_not_have_form_element_for(name)
|
||||
it "should not have a form element named '#{name}'" do
|
||||
do_render
|
||||
response.should_not have_tag(
|
||||
"form input[name='#{name}'],
|
||||
form textarea[name='#{name}'],
|
||||
form select[name='#{name}']"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation which calls <tt>have_link_or_button_to</tt> on the response
|
||||
# from rendering the template. See that method for more details.
|
||||
#
|
||||
# <b>Note:</b> This method takes a block to evaluate the route in the example context
|
||||
# instead of the example group context.
|
||||
def it_should_link_to(hint = nil, &route)
|
||||
if hint.nil? && route.respond_to?(:to_ruby)
|
||||
hint = route.to_ruby.gsub(/(^proc \{)|(\}$)/, '').strip
|
||||
end
|
||||
it "should have a link/button to #{(hint || route)}" do
|
||||
do_render
|
||||
response.should have_link_or_button_to(instance_eval(&route))
|
||||
end
|
||||
end
|
||||
alias it_should_have_link_to it_should_link_to
|
||||
alias it_should_have_button_to it_should_link_to
|
||||
alias it_should_have_button_or_link_to it_should_link_to
|
||||
|
||||
# Negative version of <tt>it_should_link_to</tt>. See that method
|
||||
# for more details.
|
||||
def it_should_not_link_to(hint = nil, &route)
|
||||
if hint.nil? && route.respond_to?(:to_ruby)
|
||||
hint = route.to_ruby.gsub(/(^proc \{)|(\}$)/, '').strip
|
||||
end
|
||||
it "should have a link/button to #{(hint || route)}" do
|
||||
do_render
|
||||
response.should_not have_link_or_button_to(instance_eval(&route))
|
||||
end
|
||||
end
|
||||
alias it_should_not_have_link_to it_should_not_link_to
|
||||
alias it_should_not_have_button_to it_should_not_link_to
|
||||
alias it_should_not_have_button_or_link_to it_should_not_link_to
|
||||
|
||||
# Creates an expectation which calls <tt>have_link_or_button_to_new</tt> on the response
|
||||
# from rendering the template. See that method for more details.
|
||||
#
|
||||
# <b>Note:</b> This method may takes a string or symbol representing the model's name
|
||||
# to send to <tt>have_link_or_button_to_show</tt> or the model's name itself.
|
||||
def it_should_link_to_new(name)
|
||||
it "should have a link/button to create a new #{name}" do
|
||||
do_render
|
||||
response.should have_link_or_button_to_new(name)
|
||||
end
|
||||
end
|
||||
alias it_should_have_link_to_new it_should_link_to_new
|
||||
alias it_should_have_button_to_new it_should_link_to_new
|
||||
alias it_should_have_button_or_link_to_new it_should_link_to_new
|
||||
|
||||
# Negative version of <tt>it_should_link_to_show</tt>. See that method
|
||||
# for more details.
|
||||
def it_should_not_link_to_new(name)
|
||||
it "should have a link/button to create a new #{name}" do
|
||||
do_render
|
||||
response.should_not have_link_or_button_to_new(name)
|
||||
end
|
||||
end
|
||||
alias it_should_not_have_link_to_new it_should_not_link_to_new
|
||||
alias it_should_not_have_button_to_new it_should_not_link_to_new
|
||||
alias it_should_not_have_button_or_link_to_new it_should_not_link_to_new
|
||||
|
||||
# Creates an expectation which calls <tt>have_link_or_button_to_show</tt> on the response
|
||||
# from rendering the template. See that method for more details.
|
||||
#
|
||||
# <b>Note:</b> This method takes a string or symbol representing the instance
|
||||
# variable's name to send to <tt>have_link_or_button_to_show</tt>
|
||||
# not an instance variable, which would be nil in the scope of the example block.
|
||||
def it_should_link_to_show(name)
|
||||
it "should have a link/button to show @#{name}" do
|
||||
do_render
|
||||
response.should have_link_or_button_to_show(instance_for(name))
|
||||
end
|
||||
end
|
||||
alias it_should_have_link_to_show it_should_link_to_show
|
||||
alias it_should_have_button_to_show it_should_link_to_show
|
||||
alias it_should_have_button_or_link_to_show it_should_link_to_show
|
||||
|
||||
# Negative version of <tt>it_should_link_to_show</tt>. See that method
|
||||
# for more details.
|
||||
def it_should_not_link_to_show(name)
|
||||
it "should have a link/button to show @#{name}" do
|
||||
do_render
|
||||
response.should_not have_link_or_button_to_show(instance_for(name))
|
||||
end
|
||||
end
|
||||
alias it_should_not_have_link_to_show it_should_not_link_to_show
|
||||
alias it_should_not_have_button_to_show it_should_not_link_to_show
|
||||
alias it_should_not_have_button_or_link_to_show it_should_not_link_to_show
|
||||
|
||||
# Creates an expectation which calls <tt>have_link_or_button_to_show</tt>
|
||||
# for each member of the instance variable matching the specified name
|
||||
# on the response from rendering the template. See that method for more details.
|
||||
#
|
||||
# <b>Note:</b> This method takes a string or symbol representing the instance
|
||||
# variable's name and not an instance variable, which would be nil
|
||||
# in the scope of the example block.
|
||||
def it_should_link_to_show_each(name)
|
||||
it "should have a link/button to show each member of @#{name}" do
|
||||
do_render
|
||||
instance_for(name).each do |member|
|
||||
response.should have_link_or_button_to_show(member)
|
||||
end
|
||||
end
|
||||
end
|
||||
alias it_should_have_link_to_show_each it_should_link_to_show_each
|
||||
alias it_should_have_button_to_show_each it_should_link_to_show_each
|
||||
alias it_should_have_button_or_link_to_show_each it_should_link_to_show_each
|
||||
|
||||
# Creates an expectation which calls <tt>have_link_or_button_to_edit</tt> on the response
|
||||
# from rendering the template. See that method for more details.
|
||||
#
|
||||
# <b>Note:</b> This method takes a string or symbol representing the instance
|
||||
# variable's name to send to <tt>have_link_or_button_to_edit</tt>
|
||||
# not an instance variable, which would be nil in the scope of the example block.
|
||||
def it_should_link_to_edit(name)
|
||||
it "should have a link/button to edit @#{name}" do
|
||||
do_render
|
||||
response.should have_link_or_button_to_edit(instance_for(name))
|
||||
end
|
||||
end
|
||||
alias it_should_have_link_to_edit it_should_link_to_edit
|
||||
alias it_should_have_button_to_edit it_should_link_to_edit
|
||||
alias it_should_have_button_or_link_to_edit it_should_link_to_edit
|
||||
|
||||
# Negative version of <tt>it_should_link_to_edit</tt>. See that method
|
||||
# for more details.
|
||||
def it_should_not_link_to_edit(name)
|
||||
it "should have a link/button to edit @#{name}" do
|
||||
do_render
|
||||
response.should_not have_link_or_button_to_edit(instance_for(name))
|
||||
end
|
||||
end
|
||||
alias it_should_not_have_link_to_edit it_should_not_link_to_edit
|
||||
alias it_should_not_have_button_to_edit it_should_not_link_to_edit
|
||||
alias it_should_not_have_button_or_link_to_edit it_should_not_link_to_edit
|
||||
|
||||
|
||||
# Creates an expectation which calls <tt>have_link_or_button_to_edit</tt>
|
||||
# for each member of the instance variable matching the specified name
|
||||
# on the response from rendering the template. See that method for more details.
|
||||
#
|
||||
# <b>Note:</b> This method takes a string or symbol representing the instance
|
||||
# variable's name and not an instance variable, which would be nil
|
||||
# in the scope of the example block.
|
||||
def it_should_link_to_edit_each(name)
|
||||
it "should have a link/button to edit each member of @#{name}" do
|
||||
do_render
|
||||
instance_for(name).each do |member|
|
||||
response.should have_link_or_button_to_edit(member)
|
||||
end
|
||||
end
|
||||
end
|
||||
alias it_should_have_link_to_edit_each it_should_link_to_edit_each
|
||||
alias it_should_have_button_to_edit_each it_should_link_to_edit_each
|
||||
alias it_should_have_button_or_link_to_edit_each it_should_link_to_edit_each
|
||||
|
||||
# Creates an expectation which calls <tt>have_link_or_button_to_delete</tt> on the response
|
||||
# from rendering the template. See that method for more details.
|
||||
#
|
||||
# <b>Note:</b> This method takes a string or symbol representing the instance
|
||||
# variable's name to send to <tt>have_link_or_button_to_delete</tt>
|
||||
# not an instance variable, which would be nil in the scope of the example block.
|
||||
def it_should_link_to_delete(name)
|
||||
it "should have a link/button to delete @#{name}" do
|
||||
do_render
|
||||
response.should have_button_to_delete(instance_for(name))
|
||||
end
|
||||
end
|
||||
alias it_should_have_link_to_delete it_should_link_to_delete
|
||||
alias it_should_have_button_to_delete it_should_link_to_delete
|
||||
alias it_should_have_button_or_link_to_delete it_should_link_to_delete
|
||||
|
||||
# Negative version of <tt>it_should_link_to_delete</tt>. See that method
|
||||
# for more details.
|
||||
def it_should_not_link_to_delete(name)
|
||||
it "should not have a link/button to delete @#{name}" do
|
||||
do_render
|
||||
response.should_not have_button_to_delete(instance_for(name))
|
||||
end
|
||||
end
|
||||
alias it_should_not_have_link_to_delete it_should_not_link_to_delete
|
||||
alias it_should_not_have_button_to_delete it_should_not_link_to_delete
|
||||
alias it_should_not_have_button_or_link_to_delete it_should_not_link_to_delete
|
||||
|
||||
# Creates an expectation which calls <tt>have_link_or_button_to_delete</tt>
|
||||
# for each member of the instance variable matching the specified name
|
||||
# on the response from rendering the template. See that method for more details.
|
||||
#
|
||||
# <b>Note:</b> This method takes a string or symbol representing the instance
|
||||
# variable's name and not an instance variable, which would be nil
|
||||
# in the scope of the example block.
|
||||
def it_should_link_to_delete_each(name)
|
||||
it "should have a link/button to delete each member of @#{name}" do
|
||||
do_render
|
||||
instance_for(name).each do |member|
|
||||
response.should have_button_to_delete(member)
|
||||
end
|
||||
end
|
||||
end
|
||||
alias it_should_have_link_to_delete_each it_should_link_to_delete_each
|
||||
alias it_should_have_button_to_delete_each it_should_link_to_delete_each
|
||||
alias it_should_have_button_or_link_to_delete_each it_should_link_to_delete_each
|
||||
end
|
||||
end
|
||||
end
|
||||
26
vendor/plugins/skinny_spec/lib/skinny_spec.rb
vendored
Normal file
26
vendor/plugins/skinny_spec/lib/skinny_spec.rb
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# Let's make sure everyone else is loaded
|
||||
require File.expand_path(File.dirname(__FILE__) + "/../../../../config/environment")
|
||||
require 'spec'
|
||||
require 'spec/rails'
|
||||
begin
|
||||
require 'ruby2ruby'
|
||||
rescue
|
||||
puts "-----"
|
||||
puts "Attention: skinny_spec requires ruby2ruby for nicer route descriptions"
|
||||
puts "It is highly recommended that you install it: sudo gem install ruby2ruby"
|
||||
puts "-----"
|
||||
end
|
||||
|
||||
# Let's load our family now
|
||||
require "lucky_sneaks/common_spec_helpers"
|
||||
require "lucky_sneaks/controller_request_helpers"
|
||||
require "lucky_sneaks/controller_spec_helpers"
|
||||
require "lucky_sneaks/controller_stub_helpers"
|
||||
require "lucky_sneaks/model_spec_helpers"
|
||||
require "lucky_sneaks/view_spec_helpers"
|
||||
|
||||
# Let's all come together
|
||||
Spec::Rails::Example::ViewExampleGroup.send :include, LuckySneaks::ViewSpecHelpers
|
||||
Spec::Rails::Example::HelperExampleGroup.send :include, LuckySneaks::CommonSpecHelpers
|
||||
Spec::Rails::Example::ControllerExampleGroup.send :include, LuckySneaks::ControllerSpecHelpers
|
||||
Spec::Rails::Example::ModelExampleGroup.send :include, LuckySneaks::ModelSpecHelpers
|
||||
Loading…
Add table
Add a link
Reference in a new issue