diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 938dd7b4..a9acc7d3 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -4,6 +4,7 @@ ENV["RAILS_ENV"] = "test"
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
require 'spec'
require 'spec/rails'
+require 'skinny_spec'
Spec::Runner.configure do |config|
# If you're not using ActiveRecord you should remove these
diff --git a/vendor/plugins/skinny_spec/.gitignore b/vendor/plugins/skinny_spec/.gitignore
new file mode 100644
index 00000000..28f7b7da
--- /dev/null
+++ b/vendor/plugins/skinny_spec/.gitignore
@@ -0,0 +1,2 @@
+.DS_Store
+doc
\ No newline at end of file
diff --git a/vendor/plugins/skinny_spec/README.rdoc b/vendor/plugins/skinny_spec/README.rdoc
new file mode 100644
index 00000000..49cfbf60
--- /dev/null
+++ b/vendor/plugins/skinny_spec/README.rdoc
@@ -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 is 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
+script/generate scaffold. 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
+valid_attributes. This will be used later by the create and update
+specs to more accurately represent how the controller works in actual practice by supplying
+somewhat real data for the params coming from the HTML forms.
+
+Next we find an example group for GET :index. That stub_index 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 stub_new) but I will point out that the
+methods named stub_controller_method should only be used for stubbing and
+mocking the main object of the method. To create mocks for other ancillary objects, please
+use stub_find_all, stub_find_one, and stub_initialize. 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 define_request in those example groups to define an explicit
+request. You can also define a method called shared_request to "share a
+define_request" 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 do_request 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 User.should_receive(:find).with(:all)
+and another that the instance variable @users 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 it_should_find for more information. You might have guessed that
+it_should_initialize_assign and it_should_render_template 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 with_default_restful_actions
+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 it_should_redirect_to 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 stub_index methods in the controller
+specs, the view specs have a shorthand mock and stub helpers: mock_and_assign and
+mock_and_assign_collection. 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
+it_should_have_form_for. 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 it_should_allow_editing 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 name attribute set in such away that it will
+generate the proper params 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
+it_should_have_form_element_for which is roughly equivalent for those times when you use
+form_tag instead.
+
+Finally let's look at those it_should_link_to_controller_method helpers.
+These methods (and there's one each for the controller methods
+new, edit, show, and delete) point to instance variables
+which you should be created in the "before" blocks with mock_and_assign. The other is
+it_should_allow_editing which is likewise covered extensively in the documentation and
+I will just point out here that, like it_should_link_to_edit and such, it takes a
+symbol for the name of the instance variable it refers to and additionally 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 render @that_template you can simply call
+do_render 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 it_should_validate_presence_of,
+it_should_validate_uniqueness_of, it_should_not_mass_assign. 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 [new, edit, etc] use a shared
+ form and leverage form_for 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 BUTTON elements which are much
+ easier to style than "button" typed INPUT. 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 label method which uses
+ #titleize instead of humanize 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.
\ No newline at end of file
diff --git a/vendor/plugins/skinny_spec/Rakefile b/vendor/plugins/skinny_spec/Rakefile
new file mode 100644
index 00000000..b0adbc43
--- /dev/null
+++ b/vendor/plugins/skinny_spec/Rakefile
@@ -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
\ No newline at end of file
diff --git a/vendor/plugins/skinny_spec/additional/helper_overrides.txt b/vendor/plugins/skinny_spec/additional/helper_overrides.txt
new file mode 100644
index 00000000..a15ec2d3
--- /dev/null
+++ b/vendor/plugins/skinny_spec/additional/helper_overrides.txt
@@ -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)
+
+ "
"
+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
\ No newline at end of file
diff --git a/vendor/plugins/skinny_spec/generators/skinny_scaffold/skinny_scaffold_generator.rb b/vendor/plugins/skinny_spec/generators/skinny_scaffold/skinny_scaffold_generator.rb
new file mode 100644
index 00000000..7f6f2b25
--- /dev/null
+++ b/vendor/plugins/skinny_spec/generators/skinny_scaffold/skinny_scaffold_generator.rb
@@ -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
\ No newline at end of file
diff --git a/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/controller.rb b/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/controller.rb
new file mode 100644
index 00000000..ea9b617d
--- /dev/null
+++ b/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/controller.rb
@@ -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
diff --git a/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/controller_spec.rb b/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/controller_spec.rb
new file mode 100644
index 00000000..dc6d5599
--- /dev/null
+++ b/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/controller_spec.rb
@@ -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
diff --git a/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/form.html.erb b/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/form.html.erb
new file mode 100644
index 00000000..ac30dc1b
--- /dev/null
+++ b/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/form.html.erb
@@ -0,0 +1,25 @@
+
diff --git a/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/show.html.haml b/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/show.html.haml
new file mode 100644
index 00000000..d8afe80a
--- /dev/null
+++ b/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/show.html.haml
@@ -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"
\ No newline at end of file
diff --git a/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/show.html_spec.rb b/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/show.html_spec.rb
new file mode 100644
index 00000000..1c6c7aa8
--- /dev/null
+++ b/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/show.html_spec.rb
@@ -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
\ No newline at end of file
diff --git a/vendor/plugins/skinny_spec/init.rb b/vendor/plugins/skinny_spec/init.rb
new file mode 100644
index 00000000..4906d276
--- /dev/null
+++ b/vendor/plugins/skinny_spec/init.rb
@@ -0,0 +1,3 @@
+if RAILS_ENV == "test"
+ require "skinny_spec"
+end
\ No newline at end of file
diff --git a/vendor/plugins/skinny_spec/lib/lucky_sneaks/common_spec_helpers.rb b/vendor/plugins/skinny_spec/lib/lucky_sneaks/common_spec_helpers.rb
new file mode 100644
index 00000000..2506accb
--- /dev/null
+++ b/vendor/plugins/skinny_spec/lib/lucky_sneaks/common_spec_helpers.rb
@@ -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 A element (link)
+ # whose href 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
\ No newline at end of file
diff --git a/vendor/plugins/skinny_spec/lib/lucky_sneaks/controller_request_helpers.rb b/vendor/plugins/skinny_spec/lib/lucky_sneaks/controller_request_helpers.rb
new file mode 100644
index 00000000..a7cd5b94
--- /dev/null
+++ b/vendor/plugins/skinny_spec/lib/lucky_sneaks/controller_request_helpers.rb
@@ -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 }
+ #
+ # Note: The following methods all define implicit requests: stub_index, stub_new,
+ # stub_create, stub_show, stub_edit, stub_update, and
+ # stub_destroy. Using them in your before blocks will allow you to forego
+ # defining explicit requests using define_request. 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
\ No newline at end of file
diff --git a/vendor/plugins/skinny_spec/lib/lucky_sneaks/controller_spec_helpers.rb b/vendor/plugins/skinny_spec/lib/lucky_sneaks/controller_spec_helpers.rb
new file mode 100644
index 00000000..43210f93
--- /dev/null
+++ b/vendor/plugins/skinny_spec/lib/lucky_sneaks/controller_spec_helpers.rb
@@ -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 ActiveRecord::Base.find.
+ # 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")
+ #
+ # Note: 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 ActiveRecord::Base.new.
+ # Takes optional params 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 ActiveRecord::Base#save on the
+ # named instance. Example:
+ #
+ # it_should_save :foo # => @foo.should_receive(:save).and_return(true)
+ #
+ # Note: This helper should not be used to spec a failed save call. Use it_should_assign
+ # 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 ActiveRecord::Base#update_attributes
+ # on the named instance. Takes optional argument for params 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)
+ #
+ # Note: This helper should not be used to spec a failed update_attributes call. Use
+ # it_should_assign 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 ActiveRecord::Base#destroy on the named
+ # instance. Example:
+ #
+ # it_should_destroy :foo # => @foo.should_receive(:destroy).and_return(true)
+ #
+ # Note: This helper should not be used to spec a failed destroy call. Use
+ # it_should_assign 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 it_should_find and it_should_assign
+ # 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 it_should_initialize and it_should_assign
+ # 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.
+ #
+ # Note: This method is used for controller methods like new, where the instance
+ # is initialized without being saved (this includes failed create requests).
+ # If you want to spec that the controller method successfully saves the instance,
+ # please use it_should_initialize_and_save.
+ 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 it_should_initialize and it_should_save
+ # 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.
+ #
+ # Note: This method is used for controller methods like create, where the instance
+ # is initialized and successfully saved. If you want to spec that the instance is created
+ # but not saved, just use it_should_initialize_and_assign.
+ 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 it_should_find and it_should_update
+ # 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.
+ #
+ # Note: This method is used for controller methods like update, 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 it_should_find_and_assign.
+ 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 it_should_find and it_should_destroy
+ # 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 (flash or session)
+ # contains the specified key and value. To specify that the collection should be set
+ # to nil, 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 it_should_set :flash. To specify that the collection should be set
+ # to nil, specify the value as :nil instead.
+ def it_should_set_flash(name, value = nil, &block)
+ it_should_set :flash, name, value, &block
+ end
+
+ # Wraps it_should_set :session. To specify that the collection should be set
+ # to nil, 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 it_should_render_foo methods:
+ # it_should_render_template, it_should_render_partial,
+ # it_should_render_xml, it_should_render_json,
+ # it_should_render_formatted, and it_should_render_nothing.
+ 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.
+ #
+ # :content_type:: Creates an expectation that the Content-Type header for the response
+ # matches the one specified
+ # :status:: 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.
+ #
+ # :content_type:: Creates an expectation that the Content-Type header for the response
+ # matches the one specified
+ # :status:: 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 to_xml.
+ # Accepts the following options which create additional expectations.
+ #
+ # :content_type:: Creates an expectation that the Content-Type header for the response
+ # matches the one specified
+ # :status:: 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 to_json.
+ # Accepts the following options which create additional expectations.
+ #
+ # :content_type:: Creates an expectation that the Content-Type header for the response
+ # matches the one specified
+ # :status:: 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 it_should_render_xml and it_should_render_json
+ # but should not really be called much externally unless you have defined your own
+ # formats with a matching to_foo 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 }
+ #
+ # Note: 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
\ No newline at end of file
diff --git a/vendor/plugins/skinny_spec/lib/lucky_sneaks/controller_stub_helpers.rb b/vendor/plugins/skinny_spec/lib/lucky_sneaks/controller_stub_helpers.rb
new file mode 100644
index 00000000..950c7a17
--- /dev/null
+++ b/vendor/plugins/skinny_spec/lib/lucky_sneaks/controller_stub_helpers.rb
@@ -0,0 +1,199 @@
+module LuckySneaks # :nodoc:
+ # These methods are designed to be used in your example before 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 stub_create and stub_update benefit from having a valid_attributes
+ # 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 skinny_scaffold or
+ # skinny_resourceful 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 find :all and returns a collection of mock_model
+ # instances of that class. Accepts the following options:
+ #
+ # :format:: Format of the request. Used to only add to_xml and
+ # to_json when actually needed.
+ # :size:: Number of instances to return in the result. Default is 3.
+ # :stub:: 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 it_should_find 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 stub_find_all but additionally defines an implicit request get :index.
+ def stub_index(klass, options = {})
+ define_implicit_request :index
+ stub_find_all klass, options
+ end
+
+ # Stubs out new method and returns a mock_model instance marked as a new record.
+ # Accepts the following options:
+ #
+ # :format:: Format of the request. Used to only add to_xml and
+ # to_json when actually needed.
+ # :stub:: Additional methods to stub on the instances
+ #
+ # It also accepts some options used to stub out save with a specified true
+ # or false but you should be using stub_create 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 stub_initialize which additionally defines an implicit request get :new.
+ def stub_new(klass, options = {})
+ define_implicit_request :new
+ stub_initialize klass, options
+ end
+
+ # Alias for stub_initialize which additionally defines an implicit request post :create.
+ #
+ # Note: If stub_create is provided an optional :params hash
+ # or the method valid_attributes is defined within its scope,
+ # those params will be added to the example's params object. If neither
+ # are provided an ArgumentError 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 find and returns a single mock_model
+ # instances of that class. Accepts the following options:
+ #
+ # :format:: Format of the request. Used to only add to_xml and
+ # to_json when actually needed.
+ # :stub:: Additional methods to stub on the instances
+ #
+ # Any additional options will be passed as arguments to find.You will want
+ # to make sure to pass those arguments to the it_should_find spec as well.
+ #
+ # Note: The option :stub_ar is used internally by stub_update
+ # and stub_destroy. If you need to stub update_attributes or
+ # destroy 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 stub_find_one which additionally defines an implicit request get :show.
+ def stub_show(klass, options = {})
+ define_implicit_request :show
+ stub_find_one klass, options.merge(:current_object => true)
+ end
+
+ # Alias for stub_find_one which additionally defines an implicit request get :edit.
+ def stub_edit(klass, options = {})
+ define_implicit_request :edit
+ stub_find_one klass, options.merge(:current_object => true)
+ end
+
+ # Alias for stub_find_one which additionally defines an implicit request put :update
+ # and stubs out the update_attribute method on the instance as well.
+ #
+ # Note: If stub_update is provided an optional :params hash
+ # or the method valid_attributes is defined within its scope,
+ # those params will be added to the example's params object. If neither
+ # are provided an ArgumentError 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 stub_find_one which additionally defines an implicit request delete :destroy
+ # and stubs out the destroy 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 to_xml or to_json respectively based on format 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
diff --git a/vendor/plugins/skinny_spec/lib/lucky_sneaks/model_spec_helpers.rb b/vendor/plugins/skinny_spec/lib/lucky_sneaks/model_spec_helpers.rb
new file mode 100644
index 00000000..4d65f276
--- /dev/null
+++ b/vendor/plugins/skinny_spec/lib/lucky_sneaks/model_spec_helpers.rb
@@ -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
+ #
+ # Note: 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 belongs_to association
+ # with the specified model.
+ #
+ # Note: 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 have_one association
+ # with the specified model.
+ #
+ # Note: 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 have_many association
+ # with the specified model.
+ #
+ # Note: 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 have_and_belong_to_many association
+ # with the specified model.
+ #
+ # Note: 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 belongs_to
+ # association with the specified model.
+ #
+ # Note: 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 have_one
+ # association with the specified model.
+ #
+ # Note: 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 have_many
+ # association with the specified model.
+ #
+ # Note: 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 have_and_belong_to_many
+ # association with the specified model.
+ #
+ # Note: 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 validates_presence_of
+ # 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 validates_numericality_of
+ # 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 validates_confirmation_of
+ # 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 validates_uniqueness_of
+ # the specified attribute. Takes an optional custom message to match the one in the model's
+ # validation.
+ #
+ # Note: This method will fail completely if valid_attributes
+ # 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 validates_format_of
+ # 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
+ # validates_format_of but there's nothing saying it couldn't be another validation.
+ # Takes an optional argument :message => "some custom error messsage" 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
\ No newline at end of file
diff --git a/vendor/plugins/skinny_spec/lib/lucky_sneaks/view_spec_helpers.rb b/vendor/plugins/skinny_spec/lib/lucky_sneaks/view_spec_helpers.rb
new file mode 100644
index 00000000..eb532554
--- /dev/null
+++ b/vendor/plugins/skinny_spec/lib/lucky_sneaks/view_spec_helpers.rb
@@ -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 FORM element with
+ # its action 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:
+ #
+ #
+ #
+ #
+ #
+ #
+ 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 A element (link)
+ # whose href attribute is set to the specified path or a FORM
+ # element whose action 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 have_link_or_button_to new_polymorphic_path for the specified class which
+ # corresponds with the new method of the controller.
+ #
+ # Note: This method may takes a string or symbol representing the model's name
+ # to send to have_link_or_button_to_show 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 have_link_or_button_to polymorphic_path(instance) which
+ # corresponds with the show 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 have_link_or_button_to edit_polymorphic_path(instance) which
+ # corresponds with the edit 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'
+ # button_to helper: to wit, a FORM element whose action
+ # attribute is pointed at the polymorphic_path of the instance
+ # and contains an INPUT 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 mock_model instance and adds it to the assigns 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 mock_model instance. Example:
+ #
+ # mock_and_assign(Foo, :stub => {:bar => "bar"})
+ #
+ # is the same as running assigns[:foo] = mock_model(Foo, :bar => "bar").
+ #
+ # mock_and_assign(Foo, "special_foo", :stub => {:bar => "baz"})
+ #
+ # is the same as running assigns[:special_foo] = mock_model(Foo, :bar => "baz").
+ #
+ # Note: Adding to the assigns collection returns the object added, so this can
+ # be chained a la @foo = mock_and_assign(Foo).
+ 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 mock_model instances in the manner of
+ # mock_and_assign. Accepts option[:size] 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 submit_to on the response
+ # from rendering the template. See that method for more details.
+ #
+ # Note: 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' form_for to generate
+ # the proper form action and method to create or update the specified object.
+ #
+ # Note: This method takes a string or symbol representing the instance
+ # variable's name to create the expectation for form_for
+ # not an instance variable, which would be nil in the scope of the example block.
+ # If you use namespacing for your form_for, 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 it_should_have_form_for. 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 allow_editing on the response
+ # from rendering the template. See that method for more details.
+ #
+ # Note: This method takes a string or symbol representing the instance
+ # variable's name to send to allow_editing
+ # 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 it_should_allow_editing. 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 FORM element
+ # (INPUT, TEXTAREA, or SELECT) 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 it_should_have_form_element_for. 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 have_link_or_button_to on the response
+ # from rendering the template. See that method for more details.
+ #
+ # Note: 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 it_should_link_to. 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 have_link_or_button_to_new on the response
+ # from rendering the template. See that method for more details.
+ #
+ # Note: This method may takes a string or symbol representing the model's name
+ # to send to have_link_or_button_to_show 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 it_should_link_to_show. 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 have_link_or_button_to_show on the response
+ # from rendering the template. See that method for more details.
+ #
+ # Note: This method takes a string or symbol representing the instance
+ # variable's name to send to have_link_or_button_to_show
+ # 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 it_should_link_to_show. 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 have_link_or_button_to_show
+ # for each member of the instance variable matching the specified name
+ # on the response from rendering the template. See that method for more details.
+ #
+ # Note: 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 have_link_or_button_to_edit on the response
+ # from rendering the template. See that method for more details.
+ #
+ # Note: This method takes a string or symbol representing the instance
+ # variable's name to send to have_link_or_button_to_edit
+ # 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 it_should_link_to_edit. 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 have_link_or_button_to_edit
+ # for each member of the instance variable matching the specified name
+ # on the response from rendering the template. See that method for more details.
+ #
+ # Note: 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 have_link_or_button_to_delete on the response
+ # from rendering the template. See that method for more details.
+ #
+ # Note: This method takes a string or symbol representing the instance
+ # variable's name to send to have_link_or_button_to_delete
+ # 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 it_should_link_to_delete. 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 have_link_or_button_to_delete
+ # for each member of the instance variable matching the specified name
+ # on the response from rendering the template. See that method for more details.
+ #
+ # Note: 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
\ No newline at end of file
diff --git a/vendor/plugins/skinny_spec/lib/skinny_spec.rb b/vendor/plugins/skinny_spec/lib/skinny_spec.rb
new file mode 100644
index 00000000..c7883884
--- /dev/null
+++ b/vendor/plugins/skinny_spec/lib/skinny_spec.rb
@@ -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
\ No newline at end of file