├── VERSION ├── test ├── rails_app │ ├── lib │ │ ├── tasks │ │ │ └── .gitkeep │ │ └── assets │ │ │ └── .gitkeep │ ├── public │ │ ├── favicon.ico │ │ ├── robots.txt │ │ ├── 422.html │ │ ├── 404.html │ │ ├── 500.html │ │ └── index.html │ ├── app │ │ ├── mailers │ │ │ └── .gitkeep │ │ ├── models │ │ │ ├── .gitkeep │ │ │ ├── users.rb │ │ │ ├── order.rb │ │ │ ├── big_company.rb │ │ │ └── company.rb │ │ ├── views │ │ │ ├── companies │ │ │ │ ├── show.html.erb │ │ │ │ ├── index.html.erb │ │ │ │ ├── _step1_step.html.erb │ │ │ │ ├── _step2_step.html.erb │ │ │ │ ├── _step3_step.html.erb │ │ │ │ └── new.html.erb │ │ │ └── layouts │ │ │ │ └── application.html.erb │ │ ├── helpers │ │ │ └── application_helper.rb │ │ └── controllers │ │ │ ├── application_controller.rb │ │ │ ├── companies_controller.rb │ │ │ └── orders_controller.rb │ ├── config.ru │ ├── config │ │ ├── environment.rb │ │ ├── boot.rb │ │ ├── routes.rb │ │ ├── initializers │ │ │ ├── mime_types.rb │ │ │ ├── inflections.rb │ │ │ ├── backtrace_silencers.rb │ │ │ ├── session_store.rb │ │ │ ├── secret_token.rb │ │ │ └── wrap_parameters.rb │ │ ├── database.yml │ │ ├── locales │ │ │ └── en.yml │ │ ├── environments │ │ │ ├── development.rb │ │ │ ├── test.rb │ │ │ └── production.rb │ │ └── application.rb │ ├── Rakefile │ ├── script │ │ └── rails │ └── db │ │ ├── schema.rb │ │ └── migrate │ │ └── 20110928102949_create_tables.rb ├── models │ ├── assigns_test.rb │ ├── models_test.rb │ ├── validation_test.rb │ └── instance_test.rb ├── controllers │ ├── controller_additions_test.rb │ ├── controller_create_test.rb │ ├── redirect_test.rb │ ├── controller_invalid_params_test.rb │ ├── controller_test.rb │ ├── controller_update_test.rb │ └── controller_resource_test.rb ├── helper.rb ├── integration │ └── steps_test.rb └── helpers │ └── helper_test.rb ├── lib ├── stepper.rb └── stepper │ ├── exceptions.rb │ ├── engine.rb │ ├── railtie.rb │ ├── helper │ └── action_view_additions.rb │ ├── controllers │ ├── controller_resource.rb │ └── controller_additions.rb │ └── models │ └── active_record_additions.rb ├── .document ├── .travis.yml ├── config └── locales │ └── stepper.yml ├── .gitignore ├── Gemfile ├── app └── views │ └── stepper │ └── _fields.html.erb ├── LICENSE.txt ├── Rakefile ├── Gemfile.lock ├── README.md └── stepper.gemspec /VERSION: -------------------------------------------------------------------------------- 1 | 0.2.0 -------------------------------------------------------------------------------- /test/rails_app/lib/tasks/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/rails_app/public/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/rails_app/app/mailers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/rails_app/app/models/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/rails_app/lib/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/stepper.rb: -------------------------------------------------------------------------------- 1 | require 'stepper/railtie' -------------------------------------------------------------------------------- /test/rails_app/app/views/companies/show.html.erb: -------------------------------------------------------------------------------- 1 | show page -------------------------------------------------------------------------------- /test/rails_app/app/views/companies/index.html.erb: -------------------------------------------------------------------------------- 1 | index page -------------------------------------------------------------------------------- /.document: -------------------------------------------------------------------------------- 1 | lib/**/*.rb 2 | bin/* 3 | - 4 | features/**/*.feature 5 | LICENSE.txt 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 1.9.2 4 | - 1.9.3 5 | - ruby-head 6 | -------------------------------------------------------------------------------- /test/rails_app/app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /lib/stepper/exceptions.rb: -------------------------------------------------------------------------------- 1 | module Stepper 2 | class StepperException < RuntimeError 3 | end 4 | end -------------------------------------------------------------------------------- /test/rails_app/app/views/companies/_step1_step.html.erb: -------------------------------------------------------------------------------- 1 | <%= f.label :name %> 2 | <%= f.text_field :name %> -------------------------------------------------------------------------------- /test/rails_app/app/views/companies/_step2_step.html.erb: -------------------------------------------------------------------------------- 1 | <%= f.label :code %> 2 | <%= f.text_field :code %> -------------------------------------------------------------------------------- /test/rails_app/app/views/companies/_step3_step.html.erb: -------------------------------------------------------------------------------- 1 | <%= f.label :city %> 2 | <%= f.text_field :city %> -------------------------------------------------------------------------------- /lib/stepper/engine.rb: -------------------------------------------------------------------------------- 1 | module Stepper #:nodoc: 2 | class Engine < ::Rails::Engine #:nodoc: 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /test/rails_app/app/models/users.rb: -------------------------------------------------------------------------------- 1 | class User < ActiveRecord::Base 2 | has_steps :steps => ["step1", "step2"] 3 | end -------------------------------------------------------------------------------- /test/rails_app/app/models/order.rb: -------------------------------------------------------------------------------- 1 | class Order < ActiveRecord::Base 2 | has_steps :steps => ["step1", "step2", "step3"] 3 | end -------------------------------------------------------------------------------- /test/rails_app/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | protect_from_forgery 3 | end 4 | -------------------------------------------------------------------------------- /config/locales/stepper.yml: -------------------------------------------------------------------------------- 1 | en: 2 | stepper: 3 | next_step: 'Next' 4 | previous_step: 'Previous' 5 | save: 'Save' 6 | finish: 'Finish' -------------------------------------------------------------------------------- /test/rails_app/app/models/big_company.rb: -------------------------------------------------------------------------------- 1 | class BigCompany < Company 2 | has_steps :current_step_column => :my_step, :steps => ["step4", "step5", "step6"] 3 | 4 | end -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | coverage 2 | rdoc 3 | doc 4 | .yardoc 5 | .bundle 6 | pkg 7 | .DS_Store 8 | *.tmproj 9 | tmtags 10 | .idea 11 | .rvmrc 12 | test/rails_app/tmp 13 | test/rails_app/log -------------------------------------------------------------------------------- /test/rails_app/config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | run RailsApp::Application 5 | -------------------------------------------------------------------------------- /test/rails_app/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the rails application 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the rails application 5 | RailsApp::Application.initialize! 6 | -------------------------------------------------------------------------------- /test/rails_app/app/controllers/companies_controller.rb: -------------------------------------------------------------------------------- 1 | class CompaniesController < ApplicationController 2 | has_steps 3 | 4 | def show 5 | 6 | end 7 | 8 | def index 9 | 10 | end 11 | end -------------------------------------------------------------------------------- /test/rails_app/config/boot.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | 3 | # Set up gems listed in the Gemfile. 4 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 5 | 6 | require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE']) 7 | -------------------------------------------------------------------------------- /test/rails_app/config/routes.rb: -------------------------------------------------------------------------------- 1 | RailsApp::Application.routes.draw do 2 | resources :companies do 3 | get :next_step , :on => :member 4 | end 5 | 6 | resources :orders do 7 | get :next_step , :on => :member 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/rails_app/public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-Agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /test/rails_app/config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | # Mime::Type.register_alias "text/html", :iphone 6 | -------------------------------------------------------------------------------- /test/rails_app/config/database.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: sqlite3 3 | database: db/development.sqlite3 4 | pool: 5 5 | timeout: 5000 6 | 7 | test: 8 | adapter: sqlite3 9 | database: ":memory:" 10 | timeout: 500 11 | 12 | production: 13 | adapter: sqlite3 14 | database: ":memory:" 15 | -------------------------------------------------------------------------------- /test/rails_app/Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | # Add your own tasks in files placed in lib/tasks ending in .rake, 3 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 4 | 5 | require File.expand_path('../config/application', __FILE__) 6 | 7 | RailsApp::Application.load_tasks 8 | -------------------------------------------------------------------------------- /test/rails_app/app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RailsApp 5 | <%= stylesheet_link_tag "application" %> 6 | <%= javascript_include_tag "application" %> 7 | <%= csrf_meta_tags %> 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/rails_app/script/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. 3 | 4 | APP_PATH = File.expand_path('../../config/application', __FILE__) 5 | require File.expand_path('../../config/boot', __FILE__) 6 | require 'rails/commands' 7 | -------------------------------------------------------------------------------- /test/rails_app/app/controllers/orders_controller.rb: -------------------------------------------------------------------------------- 1 | class OrdersController < ApplicationController 2 | has_steps :redirect_to => { 3 | :after_save => {:action => :index}, 4 | :after_finish => proc { |controller, resource| controller.order_url(resource.id) } 5 | } 6 | 7 | def show 8 | 9 | end 10 | 11 | def index 12 | 13 | end 14 | end -------------------------------------------------------------------------------- /test/rails_app/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Sample localization file for English. Add more files in this directory for other locales. 2 | # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points. 3 | 4 | en: 5 | hello: "Hello world" 6 | stepper: 7 | next_step: 'Next step' 8 | previous_step: 'Previous step' 9 | save: 'Finish later' 10 | finish: 'Finish form' -------------------------------------------------------------------------------- /test/rails_app/app/models/company.rb: -------------------------------------------------------------------------------- 1 | class Company < ActiveRecord::Base 2 | has_steps :current_step_column => :my_step, :steps => ["step1", "step2", "step3"] 3 | 4 | private 5 | 6 | def validate_step1 7 | self.validates_presence_of :name 8 | end 9 | 10 | def validate_step2 11 | self.validates_numericality_of :code 12 | end 13 | 14 | def validate_step3 15 | self.validates_presence_of :city 16 | end 17 | 18 | end -------------------------------------------------------------------------------- /test/rails_app/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format 4 | # (all these examples are active by default): 5 | # ActiveSupport::Inflector.inflections do |inflect| 6 | # inflect.plural /^(ox)$/i, '\1en' 7 | # inflect.singular /^(ox)en/i, '\1' 8 | # inflect.irregular 'person', 'people' 9 | # inflect.uncountable %w( fish sheep ) 10 | # end 11 | -------------------------------------------------------------------------------- /test/rails_app/app/views/companies/new.html.erb: -------------------------------------------------------------------------------- 1 | <% if @company.errors.any? %> 2 |
3 |

<%= pluralize(@company.errors.count, "error") %> prohibited this company from being saved:

4 | 5 | 10 |
11 | <% end %> 12 | <%= form_for @company do |f|%> 13 | <%= stepper f %> 14 | <% end %> -------------------------------------------------------------------------------- /test/rails_app/config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /test/rails_app/config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | RailsApp::Application.config.session_store :cookie_store, key: '_rails_app_session' 4 | 5 | # Use the database for sessions instead of the cookie-based default, 6 | # which shouldn't be used to store highly confidential information 7 | # (create the session table with "rails generate session_migration") 8 | # RailsApp::Application.config.session_store :active_record_store 9 | -------------------------------------------------------------------------------- /test/models/assigns_test.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | class AssignsModelTest < ActiveSupport::TestCase 3 | 4 | test "should assign default column" do 5 | assert_equal User.stepper_current_step_column, :current_step 6 | end 7 | 8 | test "should assign column from options" do 9 | assert_equal Company.stepper_current_step_column, :my_step 10 | end 11 | 12 | test "should assign default steps" do 13 | company = Company.new 14 | assert_equal company.stepper_steps, ["step1", "step2", "step3"] 15 | end 16 | end -------------------------------------------------------------------------------- /test/rails_app/config/initializers/secret_token.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | # Make sure the secret is at least 30 characters and all random, 6 | # no regular words or you'll be exposed to dictionary attacks. 7 | RailsApp::Application.config.secret_token = '0870c81cd2bbca0f24ee0b2970e925f920c8142f80acb1b4d70ea308eb19d4b7c05632320860965dea4f78e9eee47305bbb6157efe6945af6b500c11c874d467' 8 | -------------------------------------------------------------------------------- /test/rails_app/db/schema.rb: -------------------------------------------------------------------------------- 1 | ActiveRecord::Schema.define(:version => 20110928102949) do 2 | 3 | create_table "companies", :force => true do |t| 4 | t.string "name" 5 | t.string "my_step" 6 | t.integer "code" 7 | t.string "city" 8 | t.datetime "created_at" 9 | t.datetime "updated_at" 10 | end 11 | 12 | create_table "users", :force => true do |t| 13 | t.string "first_name" 14 | t.string "current_step" 15 | t.integer "last_name" 16 | t.datetime "created_at" 17 | t.datetime "updated_at" 18 | end 19 | 20 | end -------------------------------------------------------------------------------- /test/rails_app/config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | # 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # Disable root element in JSON by default. 12 | ActiveSupport.on_load(:active_record) do 13 | self.include_root_in_json = false 14 | end 15 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | gem "rails", ">= 3.1.0" 3 | 4 | group :development, :test do 5 | unless ENV["CI"] 6 | gem "ruby-debug19", :require => "ruby-debug", :platforms => [:ruby_19] 7 | gem "ruby-debug", :platforms => [:ruby_18] 8 | end 9 | 10 | gem "sqlite3" 11 | gem "shoulda", ">= 0" 12 | gem "bundler", ">= 1.0.0" 13 | gem "jeweler", ">= 1.6.4" 14 | gem "rcov", ">= 0", :platforms => :ruby_18 15 | gem "simplecov", ">= 0", :platforms => :ruby_19, :require => "false" 16 | gem "mocha" 17 | gem "capybara" 18 | gem "launchy" 19 | end 20 | 21 | -------------------------------------------------------------------------------- /test/controllers/controller_additions_test.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | class ControllerAdditionsTest < ActiveSupport::TestCase 4 | setup do 5 | @controller_class = Class.new 6 | @controller = @controller_class.new 7 | @controller_class.send(:include, Stepper::ControllerAdditions) 8 | end 9 | 10 | test "should has_steps setup before filter which passes call to ControllerResource" do 11 | Stepper::ControllerResource.stubs(:new).with(@controller, nil).stubs(:load_resource) 12 | @controller_class.stubs(:before_filter).with(:only => [:create, :update, :new, :next_step]) 13 | @controller_class.has_steps 14 | end 15 | 16 | end -------------------------------------------------------------------------------- /test/rails_app/db/migrate/20110928102949_create_tables.rb: -------------------------------------------------------------------------------- 1 | class CreateTables < ActiveRecord::Migration 2 | def self.up 3 | create_table "companies", :force => true do |t| 4 | t.string "name" 5 | t.string "my_step" 6 | t.integer "code" 7 | t.string "city" 8 | t.datetime "created_at" 9 | t.datetime "updated_at" 10 | end 11 | 12 | create_table "users", :force => true do |t| 13 | t.string "first_name" 14 | t.string "current_step" 15 | t.integer "last_name" 16 | t.datetime "created_at" 17 | t.datetime "updated_at" 18 | end 19 | end 20 | 21 | def self.down 22 | drop_table "companies" 23 | drop_table "users" 24 | end 25 | end -------------------------------------------------------------------------------- /test/helper.rb: -------------------------------------------------------------------------------- 1 | ENV["RAILS_ENV"] = "test" 2 | require 'rubygems' 3 | require 'bundler' 4 | begin 5 | Bundler.setup(:default, :development) 6 | rescue Bundler::BundlerError => e 7 | $stderr.puts e.message 8 | $stderr.puts "Run `bundle install` to install missing gems" 9 | exit e.status_code 10 | end 11 | 12 | if ENV["COVERAGE"] 13 | require "simplecov" 14 | SimpleCov.start "rails" do 15 | add_filter "/test/" 16 | end 17 | end 18 | 19 | require 'test/unit' 20 | require 'ruby-debug' unless ENV["CI"] 21 | require 'shoulda' 22 | require 'stepper' 23 | require "rails_app/config/environment" 24 | require "rails/test_help" 25 | require 'capybara/rails' 26 | 27 | ActiveRecord::Migrator.migrate(File.expand_path("../rails_app/db/migrate/", __FILE__)) 28 | -------------------------------------------------------------------------------- /test/models/models_test.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | class ActiveRecordTest < ActiveSupport::TestCase 3 | 4 | test "should have method" do 5 | assert_respond_to ActiveRecord::Base, :has_steps 6 | end 7 | 8 | test "should raise exception if options isn't hash" do 9 | assert_raise Stepper::StepperException do 10 | ActiveRecord::Base.has_steps "something" 11 | end 12 | end 13 | 14 | test "should raise exception if options is wrong" do 15 | assert_raise Stepper::StepperException do 16 | ActiveRecord::Base.has_steps :some => "some", :steps => ["step1", "step2"] 17 | end 18 | end 19 | 20 | test "should raise exception if options haven't :steps" do 21 | assert_raise Stepper::StepperException do 22 | ActiveRecord::Base.has_steps 23 | end 24 | end 25 | 26 | end -------------------------------------------------------------------------------- /app/views/stepper/_fields.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= f.hidden_field current_step_column, :value => resource.next_step %> 3 | <%= render "#{resource.next_step}_step", :f => f %> 4 | 5 | 16 |
17 | 18 | -------------------------------------------------------------------------------- /test/rails_app/public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The change you wanted was rejected.

23 |

Maybe you tried to change something you didn't have access to.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /test/rails_app/public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The page you were looking for doesn't exist.

23 |

You may have mistyped the address or the page may have moved.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /test/rails_app/public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

We're sorry, but something went wrong.

23 |

We've been notified about this issue and we'll take a look at it shortly.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/stepper/railtie.rb: -------------------------------------------------------------------------------- 1 | require 'rails' 2 | 3 | require 'stepper/engine' 4 | require 'stepper/controllers/controller_additions' 5 | require 'stepper/controllers/controller_resource' 6 | require 'stepper/exceptions' 7 | require 'stepper/models/active_record_additions' 8 | require 'stepper/helper/action_view_additions' 9 | 10 | module Stepper 11 | class Railtie < ::Rails::Railtie #:nodoc: 12 | initializer 'stepper' do |app| 13 | ActiveSupport.on_load(:active_record) do 14 | ::ActiveRecord::Base.send :include, Stepper::ActiveRecordAdditions 15 | end 16 | 17 | ActiveSupport.on_load(:action_controller) do 18 | ::ActionController::Base.send :include, Stepper::ControllerAdditions 19 | end 20 | 21 | ActiveSupport.on_load(:action_view) do 22 | ::ActionView::Base.send :include, Stepper::ActionViewAdditions::InstanceMethods 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /test/models/validation_test.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | class ValidationModelTest < ActiveSupport::TestCase 3 | setup do 4 | @company = Company.new 5 | end 6 | 7 | test "should validate step1" do 8 | @company.my_step = "step1" 9 | assert !@company.save 10 | assert_equal @company.errors.messages, { :name=>["can't be blank"] } 11 | end 12 | 13 | should "validate step 3 and previous steps" do 14 | @company.my_step = "step3" 15 | assert !@company.save 16 | assert_equal @company.errors.messages, { :name => ["can't be blank"], 17 | :code => ["is not a number"], 18 | :city => ["can't be blank"] } 19 | end 20 | 21 | should "not run method if it doesn't exists" do 22 | class << @company 23 | undef_method :validate_step2 24 | end 25 | 26 | @company.name = "name" 27 | @company.city = "Kiev" 28 | @company.my_step = "step3" 29 | 30 | assert_nothing_raised NoMethodError do 31 | @company.save! 32 | end 33 | end 34 | 35 | 36 | end 37 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Anton Versal 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /test/controllers/controller_create_test.rb: -------------------------------------------------------------------------------- 1 | require "helper" 2 | class CompaniesCreateControllerTest < ActionController::TestCase 3 | tests CompaniesController 4 | 5 | setup do 6 | Company.expects(:new).with({'name' => 'Hina'}).returns(mock_company) 7 | mock_company.expects(:attributes=).with({'name' => 'Hina'}).returns(true) 8 | mock_company.expects(:save).returns(true) 9 | mock_company.stubs(:id).returns(1) 10 | end 11 | 12 | test "should redirect to next step if commit 'Next step'" do 13 | mock_company.stubs(:stepper_current_step).returns("step1") 14 | post(:create, {:company => {:name => "Hina"}, :commit => "Next step"}) 15 | assert_response :redirect 16 | assert_redirected_to "http://test.host/companies/1/next_step" 17 | end 18 | 19 | test "should redirect to index if commit 'Finish later'" do 20 | post(:create, {:company => {:name => "Hina"}, :commit => "Finish later"}) 21 | assert_response :redirect 22 | assert_redirected_to "http://test.host/companies" 23 | end 24 | 25 | protected 26 | def mock_company(stubs={}) 27 | @mock_company ||= mock(stubs) 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /test/controllers/redirect_test.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | class RedirectControllerTest < ActionController::TestCase 4 | tests OrdersController 5 | 6 | setup do 7 | Order.expects(:find).with('1').returns(mock_order) 8 | mock_order.expects(:attributes=).with({'code' => '23'}).returns(true) 9 | mock_order.expects(:save).returns(true) 10 | mock_order.stubs(:id).returns(1) 11 | end 12 | 13 | test "should redirect to :action => :index if commit 'Save'" do 14 | mock_order.stubs(:stepper_current_step).returns("step2") 15 | mock_order.stubs(:previous_step!) 16 | put(:update, {:order => {:code => "23"}, :commit => "Finish later", :id => 1}) 17 | assert_response :redirect 18 | assert_redirected_to "http://test.host/orders" 19 | end 20 | 21 | test "should redirect to show if commit 'Finish' and option is Proc" do 22 | mock_order.stubs(:stepper_current_step).returns("step3") 23 | put(:update, {:order => {:code => "23"}, :commit => "Finish form", :id => 1}) 24 | assert_response :redirect 25 | assert_redirected_to "http://test.host/orders/1" 26 | end 27 | 28 | protected 29 | def mock_order(stubs={}) 30 | @mock_order ||= mock(stubs) 31 | end 32 | end 33 | 34 | -------------------------------------------------------------------------------- /test/rails_app/config/environments/development.rb: -------------------------------------------------------------------------------- 1 | RailsApp::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Log error messages when you accidentally call methods on nil. 10 | config.whiny_nils = true 11 | 12 | # Show full error reports and disable caching 13 | config.consider_all_requests_local = true 14 | config.action_controller.perform_caching = false 15 | 16 | # Don't care if the mailer can't send 17 | config.action_mailer.raise_delivery_errors = false 18 | 19 | # Print deprecation notices to the Rails logger 20 | config.active_support.deprecation = :log 21 | 22 | # Only use best-standards-support built into browsers 23 | config.action_dispatch.best_standards_support = :builtin 24 | 25 | # Do not compress assets 26 | config.assets.compress = false 27 | 28 | # Expands the lines which load the assets 29 | config.assets.debug = true 30 | end 31 | -------------------------------------------------------------------------------- /test/controllers/controller_invalid_params_test.rb: -------------------------------------------------------------------------------- 1 | require "helper" 2 | class CompaniesInvalidParamsControllerTest < ActionController::TestCase 3 | tests CompaniesController 4 | 5 | setup do 6 | @controller.expects(:render).at_least_once 7 | end 8 | 9 | test "should create action render to new action if object.save returns false" do 10 | Company.expects(:new).with({'name' => 'Hina'}).returns(mock_company) 11 | mock_company.expects(:attributes=).with({'name' => 'Hina'}).returns(true) 12 | mock_company.expects(:save).returns(false) 13 | mock_company.expects(:previous_step!) 14 | post(:create, {:company => {:name => "Hina"}, :commit => "Next step"}) 15 | assert_response :success 16 | end 17 | 18 | test "should update action redirect to new action if object.save returns false" do 19 | Company.expects(:find).with('1').returns(mock_company) 20 | mock_company.expects(:attributes=).with({"name" => "Hina"}).returns(true) 21 | mock_company.expects(:save).returns(false) 22 | mock_company.expects(:previous_step!) 23 | post(:update, {:company => {:name => "Hina"}, :id => 1, :commit => "Next step"}) 24 | assert_response :success 25 | end 26 | 27 | protected 28 | def mock_company(stubs={}) 29 | @mock_company ||= mock(stubs) 30 | end 31 | end -------------------------------------------------------------------------------- /test/integration/steps_test.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | class StepsTest < ActionController::IntegrationTest 4 | include Capybara::DSL 5 | 6 | test "fill all steps" do 7 | visit new_company_path 8 | fill_in "Name", :with => "My company" 9 | click_button "Next step" 10 | fill_in "Code", :with => "04108" 11 | click_button "Next step" 12 | fill_in "City", :with => "Kiev" 13 | click_button "Finish form" 14 | assert_equal page.current_path, company_path(Company.last) 15 | end 16 | 17 | 18 | test "previous step" do 19 | visit new_company_path 20 | fill_in "Name", :with => "My company" 21 | click_button "Next step" 22 | assert page.has_selector?('label', :text => 'Code') 23 | click_button "Previous step" 24 | assert page.has_selector?('label', :text => 'Name') 25 | assert page.has_selector?('#company_name', :value => 'My company') 26 | assert page.has_no_selector?('#error_explanation') 27 | end 28 | 29 | test "finish later" do 30 | visit new_company_path 31 | fill_in "Name", :with => "My company" 32 | click_button "Next step" 33 | assert page.has_selector?('label', :text => 'Code') 34 | click_button "Finish later" 35 | assert_equal page.current_path, companies_path 36 | assert page.has_no_selector?('#error_explanation') 37 | end 38 | 39 | end -------------------------------------------------------------------------------- /lib/stepper/helper/action_view_additions.rb: -------------------------------------------------------------------------------- 1 | module Stepper 2 | module ActionViewAdditions 3 | module InstanceMethods 4 | # Render partial from app/views/stepper/_fields 5 | # Adds buttons "Next", "Previous", "Save" and "Finish" to form and adds hidden field with current step name. 6 | # 7 | # Add to locales for changing step names: 8 | # en: 9 | # stepper: 10 | # next_step: 'Next step' 11 | # previous_step: 'Previous step' 12 | # save: 'Finish later' 13 | # finish: 'Finish' 14 | # 15 | # +next_step+ button validates, saves current step and renders next step of form; 16 | # +previous_step+ saves current step and renders previous step of form; 17 | # +save+ save current step and redirects to index page; 18 | # +finish+ is showed only for last step instead of +next_step+ button and it validates, saves last step and redirects to show. 19 | # 20 | # If you want to have other partial for buttons than add partial to: +app/views/stepper/_fields.html.erb+ 21 | def stepper(form) 22 | resource = self.instance_variable_get :@_stepper_resource_instance 23 | current_step_column = resource.stepper_current_step_column 24 | self.render(:partial => "stepper/fields", 25 | :locals => { :f => form, 26 | :resource => resource, 27 | :current_step_column => current_step_column }).to_s 28 | end 29 | end 30 | end 31 | end -------------------------------------------------------------------------------- /test/controllers/controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | class CompaniesControllerTest < ActionController::TestCase 4 | tests CompaniesController 5 | 6 | test "should raise error if commit is unknown" do 7 | Company.expects(:new).with({'name' => 'Hina'}).returns(mock_company) 8 | mock_company.expects(:attributes=).with({'name' => 'Hina'}).returns(true) 9 | mock_company.expects(:save).returns(true) 10 | mock_company.stubs(:id).returns(1) 11 | assert_raise Stepper::StepperException do 12 | post(:create, {:company => {:name => "Hina"}, :commit => "some commit"}) 13 | end 14 | end 15 | 16 | test "should assign resource if params[:id] exists" do 17 | @controller.stubs(:render) 18 | 19 | Company.expects(:find).with('1').returns(mock_company(:last_step? => false)) 20 | get :next_step, :id => 1 21 | assert_response :success 22 | assert_equal assigns(:company), mock_company 23 | end 24 | 25 | test "should get existing assigns" do 26 | @controller.stubs(:render) 27 | @controller.instance_variable_set(:@company, mock_company(:last_step? => false)) 28 | get :next_step, :id => 1 29 | assert_equal assigns(:company), mock_company 30 | end 31 | 32 | test "next_step action should redirect to show if company on at the last step" do 33 | @controller.instance_variable_set(:@company, mock_company(:last_step? => true, :id => "1")) 34 | get :next_step, :id => 1 35 | assert_response :redirect 36 | assert_redirected_to "http://test.host/companies/1" 37 | end 38 | 39 | protected 40 | def mock_company(stubs={}) 41 | @mock_company ||= mock(stubs) 42 | end 43 | end 44 | 45 | -------------------------------------------------------------------------------- /test/helpers/helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | class StepperButtonsTest < ActionController::IntegrationTest 4 | test "first step should have 'finish later' and 'next step' buttons" do 5 | get new_company_path 6 | assert_select "li.next_step" do 7 | assert_select "input[value='Next step']" 8 | end 9 | assert_select "li.save" do 10 | assert_select "input[value='Finish later']" 11 | end 12 | assert_select "li.previous_step", false, "This page must contain no previous button" 13 | end 14 | 15 | test "second step should have 'finish later', 'previous step' and 'next step' buttons" do 16 | company = Company.create!(:name => "My company", :my_step => "step1") 17 | get next_step_company_path(:id => company.id) 18 | 19 | assert_select "li.next_step" do 20 | assert_select "input[value='Next step']" 21 | end 22 | 23 | assert_select "li.save" do 24 | assert_select "input[value='Finish later']" 25 | end 26 | 27 | assert_select "li.previous_step" do 28 | assert_select "input[value='Previous step']" 29 | end 30 | end 31 | 32 | test "last step should have 'finish later', 'previous step' and 'finish' buttons" do 33 | company = Company.create!(:name => "My company", :code => "04108", :my_step => "step2") 34 | get next_step_company_path(:id => company.id) 35 | 36 | assert_select "li.finish" do 37 | assert_select "input[value='Finish form']" 38 | end 39 | 40 | assert_select "li.save" do 41 | assert_select "input[value='Finish later']" 42 | end 43 | 44 | assert_select "li.previous_step" do 45 | assert_select "input[value='Previous step']" 46 | end 47 | end 48 | 49 | end -------------------------------------------------------------------------------- /test/controllers/controller_update_test.rb: -------------------------------------------------------------------------------- 1 | require "helper" 2 | 3 | class CompaniesUpdateControllerTest < ActionController::TestCase 4 | tests CompaniesController 5 | 6 | setup do 7 | Company.expects(:find).with('1').returns(mock_company) 8 | mock_company.expects(:attributes=).with({'code' => '23'}).returns(true) 9 | mock_company.expects(:save).returns(true) 10 | mock_company.stubs(:id).returns(1) 11 | end 12 | 13 | test "should redirect to next step if commit 'Next step'" do 14 | mock_company.stubs(:stepper_current_step).returns("step2") 15 | put(:update, {:company => {:code => "23"}, :commit => "Next step", :id => 1}) 16 | assert_response :redirect 17 | assert_redirected_to "http://test.host/companies/1/next_step" 18 | end 19 | 20 | test "should redirect to index if commit 'Finish later'" do 21 | mock_company.stubs(:previous_step!) 22 | put(:update, {:company => {:code => "23"}, :commit => "Finish later", :id => 1}) 23 | assert_response :redirect 24 | assert_redirected_to "http://test.host/companies" 25 | end 26 | 27 | test "should redirect to previous step if commit 'Previous step'" do 28 | mock_company.expects(:previous_step!).returns(mock_company).at_least(2) 29 | mock_company.stubs(:current_step).returns("step2") 30 | put(:update, {:company => {:code => "23"}, :commit => "Previous step", :id => 1}) 31 | assert_response :redirect 32 | assert_redirected_to "http://test.host/companies/1/next_step" 33 | end 34 | 35 | test "should redirect to show if commit 'Finish form'" do 36 | put(:update, {:company => {:code => "23"}, :commit => "Finish form", :id => 1}) 37 | assert_response :redirect 38 | assert_redirected_to "http://test.host/companies/1" 39 | end 40 | 41 | protected 42 | def mock_company(stubs={}) 43 | @mock_company ||= mock(stubs) 44 | end 45 | end -------------------------------------------------------------------------------- /test/rails_app/config/application.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../boot', __FILE__) 2 | 3 | # Pick the frameworks you want: 4 | require "active_record/railtie" 5 | require "action_controller/railtie" 6 | require "action_mailer/railtie" 7 | require "active_resource/railtie" 8 | require "rails/test_unit/railtie" 9 | 10 | require "stepper" 11 | 12 | module RailsApp 13 | class Application < Rails::Application 14 | # Settings in config/environments/* take precedence over those specified here. 15 | # Application configuration should go into files in config/initializers 16 | # -- all .rb files in that directory are automatically loaded. 17 | 18 | # Custom directories with classes and modules you want to be autoloadable. 19 | # config.autoload_paths += %W(#{config.root}/extras) 20 | 21 | # Only load the plugins named here, in the order given (default is alphabetical). 22 | # :all can be used as a placeholder for all plugins not explicitly named. 23 | # config.plugins = [ :exception_notification, :ssl_requirement, :all ] 24 | 25 | # Activate observers that should always be running. 26 | # config.active_record.observers = :cacher, :garbage_collector, :forum_observer 27 | 28 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 29 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 30 | # config.time_zone = 'Central Time (US & Canada)' 31 | 32 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 33 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 34 | # config.i18n.default_locale = :de 35 | 36 | # Configure the default encoding used in templates for Ruby 1.9. 37 | config.encoding = "utf-8" 38 | 39 | # Configure sensitive parameters which will be filtered from the log file. 40 | config.filter_parameters += [:password] 41 | 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /test/rails_app/config/environments/test.rb: -------------------------------------------------------------------------------- 1 | RailsApp::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Configure static asset server for tests with Cache-Control for performance 11 | config.serve_static_assets = true 12 | config.static_cache_control = "public, max-age=3600" 13 | 14 | # Log error messages when you accidentally call methods on nil 15 | config.whiny_nils = true 16 | 17 | # Show full error reports and disable caching 18 | config.consider_all_requests_local = true 19 | config.action_controller.perform_caching = false 20 | 21 | # Raise exceptions instead of rendering exception templates 22 | config.action_dispatch.show_exceptions = false 23 | 24 | # Disable request forgery protection in test environment 25 | config.action_controller.allow_forgery_protection = false 26 | 27 | # Tell Action Mailer not to deliver emails to the real world. 28 | # The :test delivery method accumulates sent emails in the 29 | # ActionMailer::Base.deliveries array. 30 | config.action_mailer.delivery_method = :test 31 | 32 | # Use SQL instead of Active Record's schema dumper when creating the test database. 33 | # This is necessary if your schema can't be completely dumped by the schema dumper, 34 | # like if you have constraints or database-specific column types 35 | # config.active_record.schema_format = :sql 36 | 37 | # Print deprecation notices to the stderr 38 | config.active_support.deprecation = :stderr 39 | 40 | # Allow pass debug_assets=true as a query parameter to load pages with unpackaged assets 41 | config.assets.allow_debugging = true 42 | end 43 | -------------------------------------------------------------------------------- /test/models/instance_test.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | class InstanceModelTest < ActiveSupport::TestCase 3 | setup do 4 | @company = Company.new 5 | end 6 | 7 | test "should have steeper_steps methods" do 8 | assert_equal @company.stepper_steps, ["step1", "step2", "step3"] 9 | end 10 | 11 | test "should have steps methods if steps method is free" do 12 | assert_equal @company.steps, ["step1", "step2", "step3"] 13 | end 14 | 15 | test "should check step is first" do 16 | assert @company.first_step?("step1") 17 | assert !@company.first_step?("step3") 18 | end 19 | 20 | test "should check step is last" do 21 | assert @company.last_step?("step3") 22 | assert !@company.last_step?("step1") 23 | end 24 | 25 | test "should check current step is first" do 26 | @company.my_step = "step1" 27 | assert @company.first_step? 28 | @company.my_step = "step3" 29 | assert !@company.first_step? 30 | end 31 | 32 | test "should check current step is last" do 33 | @company.my_step = "step3" 34 | assert @company.last_step? 35 | @company.my_step = "step1" 36 | assert !@company.last_step? 37 | end 38 | 39 | test "should return previous step" do 40 | assert_equal @company.previous_step, nil 41 | @company.my_step = "step1" 42 | assert_equal @company.previous_step, nil 43 | @company.my_step = "step2" 44 | assert_equal @company.previous_step, "step1" 45 | end 46 | 47 | test "should return next step" do 48 | assert_equal @company.next_step, "step1" 49 | @company.my_step = "step2" 50 | assert_equal @company.next_step, "step3" 51 | @company.my_step = "step3" 52 | assert_equal @company.next_step, nil 53 | end 54 | 55 | test "should next_step! change step" do 56 | @company.next_step! 57 | assert_equal @company.my_step, "step1" 58 | end 59 | 60 | test "should previous_step! change step" do 61 | @company.my_step = "step3" 62 | @company.previous_step! 63 | assert_equal @company.my_step, "step2" 64 | end 65 | end -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'rubygems' 4 | require 'bundler' 5 | begin 6 | Bundler.setup(:default, :development) 7 | rescue Bundler::BundlerError => e 8 | $stderr.puts e.message 9 | $stderr.puts "Run `bundle install` to install missing gems" 10 | exit e.status_code 11 | end 12 | require 'rake' 13 | 14 | require 'jeweler' 15 | Jeweler::Tasks.new do |gem| 16 | # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options 17 | gem.name = "stepper" 18 | gem.homepage = "http://github.com/antonversal/stepper" 19 | gem.license = "MIT" 20 | gem.summary = %Q{Stepper is multistep form (wizard) solution for Rails 3.} 21 | gem.description = %Q{Stepper is multistep form (wizard) solution for Rails 3. Stepper allows you to split up your large form into series of pages that users can navigate through to complete the form and save it state.} 22 | gem.email = "ant.ver@gmail.com" 23 | gem.authors = ["Anton Versal"] 24 | # dependencies defined in Gemfile 25 | end 26 | Jeweler::RubygemsDotOrgTasks.new 27 | 28 | require 'rake/testtask' 29 | Rake::TestTask.new(:test) do |test| 30 | test.libs << 'lib' << 'test' 31 | test.pattern = 'test/**/*_test.rb' 32 | test.verbose = true 33 | end 34 | 35 | task :default => :test 36 | 37 | begin 38 | require 'rcov/rcovtask' 39 | 40 | Rcov::RcovTask.new do |test| 41 | test.libs << 'test' 42 | test.pattern = 'test/**/test_*.rb' 43 | test.verbose = true 44 | test.rcov_opts << '--exclude "gems/*"' 45 | end 46 | rescue LoadError => e 47 | end 48 | 49 | begin 50 | require "simplecov" 51 | 52 | desc "Execute tests with coverage report" 53 | task :rcov do 54 | ENV["COVERAGE"]="true" 55 | Rake::Task["test"].execute 56 | end 57 | rescue LoadError 58 | end 59 | 60 | require 'rdoc/task' 61 | Rake::RDocTask.new do |rdoc| 62 | version = File.exist?('VERSION') ? File.read('VERSION') : "" 63 | 64 | rdoc.rdoc_dir = 'rdoc' 65 | rdoc.title = "stepper #{version}" 66 | rdoc.rdoc_files.include('README*') 67 | rdoc.rdoc_files.include('lib/**/*.rb') 68 | end 69 | -------------------------------------------------------------------------------- /test/controllers/controller_resource_test.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | class ControllerResourceTest < ActiveSupport::TestCase 4 | setup do 5 | @params = HashWithIndifferentAccess.new(:controller => "companies") 6 | @controller_class = Class.new 7 | @controller = @controller_class.new 8 | @controller.stubs(:params).returns(@params) 9 | end 10 | 11 | test "should load resource into instance variable if params[:id] is specified" do 12 | Company.stubs(:find).with(1).returns(mock_company(:id => 1)) 13 | @params.merge!(:action => "next_step", :id => mock_company.id) 14 | resource = Stepper::ControllerResource.new(@controller) 15 | resource.load_resource 16 | assert_equal @controller.instance_variable_get(:@company), mock_company 17 | end 18 | 19 | test "should build resource and load into instance variable if params[:id] is not specified" do 20 | Company.stubs(:new).returns(mock_company) 21 | @params.merge!(:action => "some_action") 22 | resource = Stepper::ControllerResource.new(@controller) 23 | resource.load_resource 24 | assert_equal @controller.instance_variable_get(:@company), mock_company 25 | end 26 | 27 | test "should load resource into instance variable(:new)" do 28 | Company.stubs(:new).returns(mock_company) 29 | @params.merge!(:action => "create") 30 | resource = Stepper::ControllerResource.new(@controller) 31 | resource.load_resource 32 | assert_equal @controller.instance_variable_get(:@company), mock_company 33 | end 34 | 35 | test "should not load resource into instance variable if instance variable exists" do 36 | @controller.instance_variable_set(:@company, mock_company) 37 | @params.merge!(:action => "next_step", :id => 15) 38 | resource = Stepper::ControllerResource.new(@controller) 39 | resource.load_resource 40 | assert_equal @controller.instance_variable_get(:@company), mock_company 41 | end 42 | 43 | protected 44 | def mock_company(stubs={}) 45 | @mock_company ||= mock(stubs) 46 | end 47 | 48 | 49 | end -------------------------------------------------------------------------------- /test/rails_app/config/environments/production.rb: -------------------------------------------------------------------------------- 1 | RailsApp::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb 3 | 4 | # Code is not reloaded between requests 5 | config.cache_classes = true 6 | 7 | # Full error reports are disabled and caching is turned on 8 | config.consider_all_requests_local = false 9 | config.action_controller.perform_caching = true 10 | 11 | # Disable Rails's static asset server (Apache or nginx will already do this) 12 | config.serve_static_assets = false 13 | 14 | # Compress JavaScripts and CSS 15 | config.assets.compress = true 16 | 17 | # Don't fallback to assets pipeline if a precompiled asset is missed 18 | config.assets.compile = false 19 | 20 | # Generate digests for assets URLs 21 | config.assets.digest = true 22 | 23 | # Defaults to Rails.root.join("public/assets") 24 | # config.assets.manifest = YOUR_PATH 25 | 26 | # Specifies the header that your server uses for sending files 27 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache 28 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx 29 | 30 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 31 | # config.force_ssl = true 32 | 33 | # See everything in the log (default is :info) 34 | # config.log_level = :debug 35 | 36 | # Use a different logger for distributed setups 37 | # config.logger = SyslogLogger.new 38 | 39 | # Use a different cache store in production 40 | # config.cache_store = :mem_cache_store 41 | 42 | # Enable serving of images, stylesheets, and JavaScripts from an asset server 43 | # config.action_controller.asset_host = "http://assets.example.com" 44 | 45 | # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added) 46 | # config.assets.precompile += %w( search.js ) 47 | 48 | # Disable delivery errors, bad email addresses will be ignored 49 | # config.action_mailer.raise_delivery_errors = false 50 | 51 | # Enable threaded mode 52 | # config.threadsafe! 53 | 54 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 55 | # the I18n.default_locale when a translation can not be found) 56 | config.i18n.fallbacks = true 57 | 58 | # Send deprecation notices to registered listeners 59 | config.active_support.deprecation = :notify 60 | end 61 | -------------------------------------------------------------------------------- /lib/stepper/controllers/controller_resource.rb: -------------------------------------------------------------------------------- 1 | module Stepper 2 | class ControllerResource 3 | # Sets up before filter in +controller_class+ for +create+, +update+, +new+ and +next_step+ actions. 4 | # First argument can be name of resource. 5 | # For example we have +CompaniesController+ and want to load or build resource to +@my_company+ variable: 6 | # add_before_filter CompanyController, :my_company 7 | # 8 | # First argument it isn't required: 9 | # add_before_filter CompanyController 10 | # In this case resource will be loaded or built into +@company+ variable 11 | # 12 | def self.add_before_filter(controller_class, *args) 13 | resource_name = args.first if args.first.is_a?(Symbol) or args.first.is_a?(String) 14 | options = args.extract_options! 15 | controller_class.send(:before_filter, :only => [:create, :update, :new, :next_step]) do |controller| 16 | controller.instance_variable_set :@_stepper_redirect_to, options[:redirect_to] || {} 17 | controller_resource = controller.class.stepper_resource_class.new(controller, resource_name) 18 | controller.instance_variable_set :@_stepper_resource_instance, controller_resource.load_resource 19 | controller.instance_variable_set :@_stepper_name, controller_resource.name 20 | end 21 | end 22 | 23 | def initialize(controller, *args) 24 | @controller = controller 25 | @params = controller.params 26 | @name = args.first 27 | end 28 | 29 | def load_resource 30 | self.resource_instance ||= load_resource_instance 31 | self.resource_instance.attributes = @params[name] unless @params[name].blank? 32 | self.resource_instance 33 | end 34 | 35 | def load_resource_instance 36 | if ['create', 'new'].include? @params[:action] 37 | resource = resource_class.new(@params[name] || {}) 38 | else 39 | resource = unless @params[:id].blank? 40 | resource_class.find(@params[:id]) 41 | else 42 | resource_class.new 43 | end 44 | end 45 | resource 46 | end 47 | 48 | def resource_class 49 | name.camelize.constantize 50 | end 51 | 52 | def name_from_controller 53 | @params[:controller].sub("Controller", "").underscore.split('/').last.singularize 54 | end 55 | 56 | def name 57 | @name || name_from_controller 58 | end 59 | 60 | def resource_instance=(object) 61 | @controller.instance_variable_set "@#{name}", object 62 | end 63 | 64 | def resource_instance 65 | @controller.instance_variable_get "@#{name}" 66 | end 67 | 68 | end 69 | end -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | actionmailer (3.2.1) 5 | actionpack (= 3.2.1) 6 | mail (~> 2.4.0) 7 | actionpack (3.2.1) 8 | activemodel (= 3.2.1) 9 | activesupport (= 3.2.1) 10 | builder (~> 3.0.0) 11 | erubis (~> 2.7.0) 12 | journey (~> 1.0.1) 13 | rack (~> 1.4.0) 14 | rack-cache (~> 1.1) 15 | rack-test (~> 0.6.1) 16 | sprockets (~> 2.1.2) 17 | activemodel (3.2.1) 18 | activesupport (= 3.2.1) 19 | builder (~> 3.0.0) 20 | activerecord (3.2.1) 21 | activemodel (= 3.2.1) 22 | activesupport (= 3.2.1) 23 | arel (~> 3.0.0) 24 | tzinfo (~> 0.3.29) 25 | activeresource (3.2.1) 26 | activemodel (= 3.2.1) 27 | activesupport (= 3.2.1) 28 | activesupport (3.2.1) 29 | i18n (~> 0.6) 30 | multi_json (~> 1.0) 31 | addressable (2.2.7) 32 | archive-tar-minitar (0.5.2) 33 | arel (3.0.2) 34 | builder (3.0.0) 35 | capybara (1.1.2) 36 | mime-types (>= 1.16) 37 | nokogiri (>= 1.3.3) 38 | rack (>= 1.0.0) 39 | rack-test (>= 0.5.4) 40 | selenium-webdriver (~> 2.0) 41 | xpath (~> 0.1.4) 42 | childprocess (0.3.1) 43 | ffi (~> 1.0.6) 44 | columnize (0.3.6) 45 | erubis (2.7.0) 46 | ffi (1.0.11) 47 | git (1.2.5) 48 | hike (1.2.1) 49 | i18n (0.6.0) 50 | jeweler (1.8.3) 51 | bundler (~> 1.0) 52 | git (>= 1.2.5) 53 | rake 54 | rdoc 55 | journey (1.0.3) 56 | json (1.6.5) 57 | json_pure (1.6.5) 58 | launchy (2.0.5) 59 | addressable (~> 2.2.6) 60 | linecache (0.46) 61 | rbx-require-relative (> 0.0.4) 62 | linecache19 (0.5.13) 63 | ruby_core_source (>= 0.1.4) 64 | mail (2.4.1) 65 | i18n (>= 0.4.0) 66 | mime-types (~> 1.16) 67 | treetop (~> 1.4.8) 68 | metaclass (0.0.1) 69 | mime-types (1.17.2) 70 | mocha (0.10.4) 71 | metaclass (~> 0.0.1) 72 | multi_json (1.1.0) 73 | nokogiri (1.5.0) 74 | polyglot (0.3.3) 75 | rack (1.4.1) 76 | rack-cache (1.1) 77 | rack (>= 0.4) 78 | rack-ssl (1.3.2) 79 | rack 80 | rack-test (0.6.1) 81 | rack (>= 1.0) 82 | rails (3.2.1) 83 | actionmailer (= 3.2.1) 84 | actionpack (= 3.2.1) 85 | activerecord (= 3.2.1) 86 | activeresource (= 3.2.1) 87 | activesupport (= 3.2.1) 88 | bundler (~> 1.0) 89 | railties (= 3.2.1) 90 | railties (3.2.1) 91 | actionpack (= 3.2.1) 92 | activesupport (= 3.2.1) 93 | rack-ssl (~> 1.3.2) 94 | rake (>= 0.8.7) 95 | rdoc (~> 3.4) 96 | thor (~> 0.14.6) 97 | rake (0.9.2.2) 98 | rbx-require-relative (0.0.6) 99 | rcov (1.0.0) 100 | rdoc (3.12) 101 | json (~> 1.4) 102 | ruby-debug (0.10.4) 103 | columnize (>= 0.1) 104 | ruby-debug-base (~> 0.10.4.0) 105 | ruby-debug-base (0.10.4) 106 | linecache (>= 0.3) 107 | ruby-debug-base19 (0.11.26) 108 | columnize (>= 0.3.1) 109 | linecache19 (>= 0.5.11) 110 | ruby_core_source (>= 0.1.4) 111 | ruby-debug19 (0.11.6) 112 | columnize (>= 0.3.1) 113 | linecache19 (>= 0.5.11) 114 | ruby-debug-base19 (>= 0.11.19) 115 | ruby_core_source (0.1.5) 116 | archive-tar-minitar (>= 0.5.2) 117 | rubyzip (0.9.6.1) 118 | selenium-webdriver (2.13.0) 119 | childprocess (>= 0.2.1) 120 | ffi (~> 1.0.9) 121 | json_pure 122 | rubyzip 123 | shoulda (2.11.3) 124 | simplecov (0.6.0) 125 | multi_json (~> 1.0) 126 | simplecov-html (~> 0.5.3) 127 | simplecov-html (0.5.3) 128 | sprockets (2.1.2) 129 | hike (~> 1.2) 130 | rack (~> 1.0) 131 | tilt (~> 1.1, != 1.3.0) 132 | sqlite3 (1.3.5) 133 | thor (0.14.6) 134 | tilt (1.3.3) 135 | treetop (1.4.10) 136 | polyglot 137 | polyglot (>= 0.3.1) 138 | tzinfo (0.3.31) 139 | xpath (0.1.4) 140 | nokogiri (~> 1.3) 141 | 142 | PLATFORMS 143 | ruby 144 | 145 | DEPENDENCIES 146 | bundler (>= 1.0.0) 147 | capybara 148 | jeweler (>= 1.6.4) 149 | launchy 150 | mocha 151 | rails (>= 3.1.0) 152 | rcov 153 | ruby-debug 154 | ruby-debug19 155 | shoulda 156 | simplecov 157 | sqlite3 158 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Stepper 2 | 3 | [![Build Status](https://secure.travis-ci.org/antonversal/stepper.png)](http://travis-ci.org/antonversal/stepper) 4 | 5 | Stepper is multistep form (wizard) solution for Rails 3.1. 6 | Stepper allows you to split up your large form into series of pages that users can navigate through to complete the form and saving it's state. 7 | 8 | ## Installation 9 | 10 | You can use it with Rails >= 3.1: 11 | 12 | `gem install stepper` 13 | 14 | ## Getting Started 15 | 16 | ### Configuring model 17 | 18 | Create migration for model: 19 | 20 | ```ruby 21 | add_column :companies, :current_step, :string 22 | add_index :companies, :current_step 23 | ``` 24 | 25 | Setup names of steps that you want to have: 26 | 27 | ```ruby 28 | class Company < ActiveRecord::Base 29 | has_steps :steps => %w{ description kind address } 30 | end 31 | ``` 32 | 33 | Setup validation for each step if necessary, method should have name like `validate_#{step_name}`: 34 | 35 | ```ruby 36 | def validate_description 37 | self.validates_presence_of :name 38 | self.validates_presence_of :desc 39 | end 40 | 41 | def validate_address 42 | self.validates_presence_of :city 43 | self.validates_presence_of :country 44 | self.validates_presence_of :address 45 | end 46 | 47 | def validate_kind 48 | self.validates_presence_of :kind 49 | end 50 | ``` 51 | 52 | Now your model supports a multistep form! 53 | 54 | ### Configuring controller 55 | 56 | Stepper uses `update`, `create`, `new` and `next_step` actions, so you should have the following routes: 57 | 58 | ```ruby 59 | resources :companies do 60 | get :next_step, :on => :member 61 | end 62 | ``` 63 | 64 | For your controller you need just add the `has_steps method: 65 | 66 | ```ruby 67 | class CompaniesController < ApplicationController 68 | has_steps 69 | end 70 | ``` 71 | 72 | And you should have a +show+ action because stepper redirects to it after finishing the last step by default. For more options see method documentation. 73 | 74 | ### Configuring view 75 | 76 | Add stepper helper method into the form in view that rendered by new action: 77 | 78 | ```erb 79 | <%= form_for(@company) do |f| %> 80 | <%= stepper f %> 81 | <% end %> 82 | ``` 83 | 84 | The `stepper` helper renders partial according to the current step of form. Partials should be named like `#{step_name}_step`: 85 | 86 | `_name_step.html.erb` 87 | 88 | ```erb 89 | <%= f.label :name %> 90 | <%= f.text_field :name %> 91 | <%= f.label :desc %> 92 | <%= f.text_field :desc %> 93 | ``` 94 | 95 | `_city_step.html.erb` 96 | 97 | ```erb 98 | <%= f.label :city %> 99 | <%= f.text_field :city %> 100 | <%= f.label :country %> 101 | <%= f.text_field :country %> 102 | <%= f.label :city %> 103 | <%= f.text_field :city %> 104 | ``` 105 | 106 | `_kind_step.html.erb` 107 | 108 | ```erb 109 | <%= f.label :kind %> 110 | <%= f.text_field :kind %> 111 | ``` 112 | 113 | `stepper` helper creates buttons "Next", "Previous", "Save" and "Finish" as well. You can change button names by adding the following to your locales: 114 | 115 | ```ruby 116 | en: 117 | stepper: 118 | next_step: 'Next step' 119 | previous_step: 'Previous step' 120 | save: 'Finish later' 121 | finish: 'Finish' 122 | ``` 123 | 124 | `next_step` button validates, saves current step and renders next step of form; 125 | `previous_step` saves current step and renders previous step of form; 126 | `save` save current step and redirects to index page; 127 | `finish` is showed only for last step instead of `next_step` button and it validates, saves last step and redirects to show. 128 | 129 | If you want to have other partials for buttons than add your partial to: `app/views/stepper/_fields.html.erb` 130 | 131 | ## Contributing to stepper 132 | 133 | * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet 134 | * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it 135 | * Fork the project 136 | * Start a feature/bugfix branch 137 | * Commit and push until you are happy with your contribution 138 | * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally. 139 | * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it. 140 | 141 | ## Copyright 142 | 143 | Copyright (c) 2012 Anton Versal. See LICENSE.txt for 144 | further details. 145 | 146 | -------------------------------------------------------------------------------- /lib/stepper/models/active_record_additions.rb: -------------------------------------------------------------------------------- 1 | module Stepper 2 | 3 | # This module is automatically included into all models. 4 | module ActiveRecordAdditions 5 | 6 | def self.included(base) 7 | base.extend ClassMethods 8 | end 9 | 10 | module ClassMethods 11 | # Sets up methods and define steps. 12 | # For example, you have model +Company+ and you want to fill it fields in few steps description, kind and address: 13 | # class Company < ActiveRecord::Base 14 | # has_steps :steps => %w{ description kind address } 15 | # end 16 | # 17 | # Model should have current step column, by default it name is +current_step+. 18 | # It should be added by migration: 19 | # add_column :companies, :current_step, :string 20 | # add_index :companies, :current_step 21 | # 22 | # The column name can be set up with option +current_step_column+. 23 | # 24 | # Options: 25 | # [:+steps+] 26 | # It is required option. Define steps for multistep form. 27 | # 28 | # [:+current_step_column+] 29 | # Define what field use for save current step of form. Default +current_step+ 30 | # 31 | 32 | def has_steps(options = {}) 33 | #check options 34 | raise Stepper::StepperException.new("Options for has_steps must be in a hash.") unless options.is_a? Hash 35 | options.each do |key, value| 36 | unless [:current_step_column, :steps].include? key 37 | raise Stepper::StepperException.new("Unknown option for has_steps: #{key.inspect} => #{value.inspect}.") 38 | end 39 | end 40 | 41 | raise Stepper::StepperException.new(":steps condition can't be blank") if options[:steps].blank? 42 | 43 | #set current step column 44 | class_attribute :stepper_current_step_column, :instance_writer => false 45 | self.stepper_current_step_column = options[:current_step_column] || :current_step 46 | 47 | class_attribute :stepper_options, :instance_writer => false 48 | self.stepper_options = options 49 | 50 | self.validate :current_step_validation 51 | 52 | include InstanceMethods 53 | end 54 | end 55 | 56 | module InstanceMethods 57 | def stepper_steps 58 | self.stepper_options[:steps] 59 | end 60 | 61 | unless self.respond_to? :steps 62 | define_method :steps do 63 | self.stepper_steps 64 | end 65 | end 66 | 67 | # returns index of current step in steps array 68 | def stepper_current_step_index 69 | stepper_steps.index(stepper_current_step) 70 | end 71 | 72 | # returns name of current step 73 | def stepper_current_step 74 | self.send(self.stepper_current_step_column) 75 | end 76 | 77 | # sets up name of current step 78 | def stepper_current_step=(step) 79 | self.send("#{self.stepper_current_step_column.to_s}=", step) 80 | end 81 | 82 | # Use to check current step or given step is last step 83 | # last_step?("address") 84 | def last_step?(step = stepper_current_step) 85 | step == self.stepper_steps.last 86 | end 87 | 88 | # Use to check current step or given step is first step 89 | # first_step?("address") 90 | def first_step?(step = stepper_current_step) 91 | (step == stepper_steps.first) or stepper_current_step.blank? && step.blank? 92 | end 93 | 94 | # returns previous step of current step 95 | def previous_step 96 | return nil if (first_step? or stepper_current_step.blank?) 97 | stepper_steps[stepper_steps.index(stepper_current_step) - 1] 98 | end 99 | 100 | # set previous step as current step 101 | def previous_step! 102 | self.stepper_current_step = self.previous_step 103 | self 104 | end 105 | 106 | # returns next step of current step 107 | def next_step 108 | return stepper_steps.first if self.stepper_current_step.blank? 109 | return nil if self.last_step? 110 | stepper_steps[stepper_steps.index(stepper_current_step) + 1] 111 | end 112 | 113 | # set next step as current step 114 | def next_step! 115 | self.stepper_current_step = self.next_step 116 | self 117 | end 118 | 119 | protected 120 | 121 | # Executes validation methods for current step and all previous steps if its exists. 122 | # You can set up what fields should be validated in methods for steps. For example: 123 | # 124 | # def validate_description 125 | # self.validates_presence_of :name 126 | # self.validates_presence_of :desc 127 | # end 128 | # 129 | # def validate_address 130 | # self.validates_presence_of :city 131 | # self.validates_presence_of :country 132 | # self.validates_presence_of :address 133 | # end 134 | # 135 | # def validate_kind 136 | # self.validates_presence_of :kind 137 | # end 138 | 139 | def current_step_validation 140 | return if stepper_current_step.blank? 141 | for i in 0..stepper_current_step_index do 142 | self.send("validate_#{stepper_steps[i]}") if self.respond_to?("validate_#{stepper_steps[i]}", true) 143 | end 144 | end 145 | 146 | end 147 | end 148 | end 149 | -------------------------------------------------------------------------------- /lib/stepper/controllers/controller_additions.rb: -------------------------------------------------------------------------------- 1 | module Stepper 2 | module ControllerAdditions 3 | 4 | def self.included(base) 5 | base.extend ClassMethods 6 | end 7 | 8 | module ClassMethods 9 | # Sets up +create+, +update+, +new+ actions for controller and before filter for load resource. 10 | # If you use cancan or load resource in other way it will get loaded resource. 11 | # 12 | # First parameters can be name of resource, for example: 13 | # 14 | # class CompaniesController < ApplicationController 15 | # has_steps :my_company 16 | # end 17 | # It will load or build resource in +@my_company+ variable 18 | # 19 | # First argument it isn't required: 20 | # class CompaniesController < ApplicationController 21 | # has_steps 22 | # end 23 | # In this case resource will be loaded or built into +@company+ variable 24 | # 25 | # You can setup redirection for each +save+, +previous_step+, +next_step+ and +finish+ step to other action than default, 26 | # options should have +after+ prefix: 27 | # 28 | # class CompaniesController < ApplicationController 29 | # has_steps :redirect_to => { :after_save => {:action => :new} } 30 | # end 31 | # 32 | # You can set proc that will be executed for current controller: 33 | # 34 | # class CompaniesController < ApplicationController 35 | # has_steps :redirect_to => { :after_finish => proc{|controller, resource| controller.show_companies_url(resource)} } 36 | # end 37 | 38 | def has_steps(*args) 39 | include InstanceMethods 40 | stepper_resource_class.add_before_filter(self, *args) 41 | end 42 | 43 | def stepper_resource_class 44 | ControllerResource 45 | end 46 | 47 | end 48 | 49 | module InstanceMethods 50 | 51 | # controller +create+ action 52 | # it supports only html responce format for now 53 | def create 54 | respond_to do |format| 55 | if @_stepper_resource_instance.save 56 | format.html { redirect_steps } 57 | else 58 | @_stepper_resource_instance.previous_step! 59 | format.html { render :action => "new" } 60 | end 61 | end 62 | end 63 | 64 | # controller +update+ action 65 | # it supports only html responce format for now 66 | def update 67 | @_stepper_resource_instance.previous_step!.previous_step! if params[:commit] == t('stepper.previous_step').html_safe 68 | 69 | @_stepper_resource_instance.previous_step! if params[:commit] == t('stepper.save').html_safe 70 | 71 | respond_to do |format| 72 | if @_stepper_resource_instance.save 73 | format.html { redirect_steps } 74 | else 75 | @_stepper_resource_instance.previous_step! 76 | format.html { render :action => "new" } 77 | end 78 | end 79 | end 80 | 81 | # controller +new+ action 82 | # it supports only html responce format for now 83 | def new 84 | 85 | end 86 | 87 | # controller +new+ action 88 | # it supports only html responce format for now 89 | def next_step 90 | if @_stepper_resource_instance.last_step? 91 | redirect_to :action => :show, :id => @_stepper_resource_instance.id 92 | else 93 | render :action => :new 94 | end 95 | end 96 | 97 | protected 98 | 99 | # default redirection actions 100 | def default_redirection 101 | { 102 | :next_step => {:action => "next_step", :id => @_stepper_resource_instance.id}, 103 | :previous_step => {:action => "next_step"}, 104 | :save => {:action => "index"}, 105 | :finish => {:action => "show", :id => @_stepper_resource_instance.id} 106 | } 107 | end 108 | 109 | # redirects to controller actions depends of commit value 110 | # save -> index 111 | # previous_step -> new 112 | # next_step -> new 113 | # finish -> show 114 | 115 | def redirect_steps 116 | options, response_status = redirect_steps_options 117 | redirect_to options, response_status 118 | end 119 | 120 | def redirect_steps_options 121 | case params[:commit] 122 | when t('stepper.save') 123 | [ extract_redirect_params(:save), {}] 124 | when t('stepper.previous_step') 125 | [ extract_redirect_params(:previous_step), {}] 126 | when t('stepper.next_step') 127 | [ extract_redirect_params(:next_step), {}] 128 | when t('stepper.finish') 129 | [ extract_redirect_params(:finish), {}] 130 | else 131 | raise Stepper::StepperException.new("Unknown commit: #{params[:commit]}") 132 | end 133 | end 134 | 135 | def extract_redirect_params(option) 136 | redirection = @_stepper_redirect_to["after_#{option.to_s}".to_sym] 137 | if redirection.is_a?(Proc) 138 | redirection.call(self, @_stepper_resource_instance) 139 | else 140 | redirection 141 | end || default_redirection[option] 142 | end 143 | 144 | # removes from params resource name, commit and id 145 | def sanitized_params 146 | params.except(@_stepper_name, :commit, :id) 147 | end 148 | end 149 | 150 | end 151 | 152 | end -------------------------------------------------------------------------------- /stepper.gemspec: -------------------------------------------------------------------------------- 1 | # Generated by jeweler 2 | # DO NOT EDIT THIS FILE DIRECTLY 3 | # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' 4 | # -*- encoding: utf-8 -*- 5 | 6 | Gem::Specification.new do |s| 7 | s.name = "stepper" 8 | s.version = "0.2.0" 9 | 10 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 11 | s.authors = ["Anton Versal"] 12 | s.date = "2012-02-24" 13 | s.description = "Stepper is multistep form (wizard) solution for Rails 3. Stepper allows you to split up your large form into series of pages that users can navigate through to complete the form and save it state." 14 | s.email = "ant.ver@gmail.com" 15 | s.extra_rdoc_files = [ 16 | "LICENSE.txt", 17 | "README.md" 18 | ] 19 | s.files = [ 20 | ".document", 21 | ".travis.yml", 22 | "Gemfile", 23 | "Gemfile.lock", 24 | "LICENSE.txt", 25 | "README.md", 26 | "Rakefile", 27 | "VERSION", 28 | "app/views/stepper/_fields.html.erb", 29 | "config/locales/stepper.yml", 30 | "lib/stepper.rb", 31 | "lib/stepper/controllers/controller_additions.rb", 32 | "lib/stepper/controllers/controller_resource.rb", 33 | "lib/stepper/engine.rb", 34 | "lib/stepper/exceptions.rb", 35 | "lib/stepper/helper/action_view_additions.rb", 36 | "lib/stepper/models/active_record_additions.rb", 37 | "lib/stepper/railtie.rb", 38 | "stepper.gemspec", 39 | "test/controllers/controller_additions_test.rb", 40 | "test/controllers/controller_create_test.rb", 41 | "test/controllers/controller_invalid_params_test.rb", 42 | "test/controllers/controller_resource_test.rb", 43 | "test/controllers/controller_test.rb", 44 | "test/controllers/controller_update_test.rb", 45 | "test/controllers/redirect_test.rb", 46 | "test/helper.rb", 47 | "test/helpers/helper_test.rb", 48 | "test/integration/steps_test.rb", 49 | "test/models/assigns_test.rb", 50 | "test/models/instance_test.rb", 51 | "test/models/models_test.rb", 52 | "test/models/validation_test.rb", 53 | "test/rails_app/Rakefile", 54 | "test/rails_app/app/controllers/application_controller.rb", 55 | "test/rails_app/app/controllers/companies_controller.rb", 56 | "test/rails_app/app/controllers/orders_controller.rb", 57 | "test/rails_app/app/helpers/application_helper.rb", 58 | "test/rails_app/app/mailers/.gitkeep", 59 | "test/rails_app/app/models/.gitkeep", 60 | "test/rails_app/app/models/big_company.rb", 61 | "test/rails_app/app/models/company.rb", 62 | "test/rails_app/app/models/order.rb", 63 | "test/rails_app/app/models/users.rb", 64 | "test/rails_app/app/views/companies/_step1_step.html.erb", 65 | "test/rails_app/app/views/companies/_step2_step.html.erb", 66 | "test/rails_app/app/views/companies/_step3_step.html.erb", 67 | "test/rails_app/app/views/companies/index.html.erb", 68 | "test/rails_app/app/views/companies/new.html.erb", 69 | "test/rails_app/app/views/companies/show.html.erb", 70 | "test/rails_app/app/views/layouts/application.html.erb", 71 | "test/rails_app/config.ru", 72 | "test/rails_app/config/application.rb", 73 | "test/rails_app/config/boot.rb", 74 | "test/rails_app/config/database.yml", 75 | "test/rails_app/config/environment.rb", 76 | "test/rails_app/config/environments/development.rb", 77 | "test/rails_app/config/environments/production.rb", 78 | "test/rails_app/config/environments/test.rb", 79 | "test/rails_app/config/initializers/backtrace_silencers.rb", 80 | "test/rails_app/config/initializers/inflections.rb", 81 | "test/rails_app/config/initializers/mime_types.rb", 82 | "test/rails_app/config/initializers/secret_token.rb", 83 | "test/rails_app/config/initializers/session_store.rb", 84 | "test/rails_app/config/initializers/wrap_parameters.rb", 85 | "test/rails_app/config/locales/en.yml", 86 | "test/rails_app/config/routes.rb", 87 | "test/rails_app/db/migrate/20110928102949_create_tables.rb", 88 | "test/rails_app/db/schema.rb", 89 | "test/rails_app/lib/assets/.gitkeep", 90 | "test/rails_app/lib/tasks/.gitkeep", 91 | "test/rails_app/public/404.html", 92 | "test/rails_app/public/422.html", 93 | "test/rails_app/public/500.html", 94 | "test/rails_app/public/favicon.ico", 95 | "test/rails_app/public/index.html", 96 | "test/rails_app/public/robots.txt", 97 | "test/rails_app/script/rails" 98 | ] 99 | s.homepage = "http://github.com/antonversal/stepper" 100 | s.licenses = ["MIT"] 101 | s.require_paths = ["lib"] 102 | s.rubygems_version = "1.8.16" 103 | s.summary = "Stepper is multistep form (wizard) solution for Rails 3." 104 | 105 | if s.respond_to? :specification_version then 106 | s.specification_version = 3 107 | 108 | if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then 109 | s.add_runtime_dependency(%q, [">= 3.1.0"]) 110 | s.add_development_dependency(%q, [">= 0"]) 111 | s.add_development_dependency(%q, [">= 0"]) 112 | s.add_development_dependency(%q, [">= 0"]) 113 | s.add_development_dependency(%q, [">= 0"]) 114 | s.add_development_dependency(%q, [">= 1.0.0"]) 115 | s.add_development_dependency(%q, [">= 1.6.4"]) 116 | s.add_development_dependency(%q, [">= 0"]) 117 | s.add_development_dependency(%q, [">= 0"]) 118 | s.add_development_dependency(%q, [">= 0"]) 119 | s.add_development_dependency(%q, [">= 0"]) 120 | s.add_development_dependency(%q, [">= 0"]) 121 | else 122 | s.add_dependency(%q, [">= 3.1.0"]) 123 | s.add_dependency(%q, [">= 0"]) 124 | s.add_dependency(%q, [">= 0"]) 125 | s.add_dependency(%q, [">= 0"]) 126 | s.add_dependency(%q, [">= 0"]) 127 | s.add_dependency(%q, [">= 1.0.0"]) 128 | s.add_dependency(%q, [">= 1.6.4"]) 129 | s.add_dependency(%q, [">= 0"]) 130 | s.add_dependency(%q, [">= 0"]) 131 | s.add_dependency(%q, [">= 0"]) 132 | s.add_dependency(%q, [">= 0"]) 133 | s.add_dependency(%q, [">= 0"]) 134 | end 135 | else 136 | s.add_dependency(%q, [">= 3.1.0"]) 137 | s.add_dependency(%q, [">= 0"]) 138 | s.add_dependency(%q, [">= 0"]) 139 | s.add_dependency(%q, [">= 0"]) 140 | s.add_dependency(%q, [">= 0"]) 141 | s.add_dependency(%q, [">= 1.0.0"]) 142 | s.add_dependency(%q, [">= 1.6.4"]) 143 | s.add_dependency(%q, [">= 0"]) 144 | s.add_dependency(%q, [">= 0"]) 145 | s.add_dependency(%q, [">= 0"]) 146 | s.add_dependency(%q, [">= 0"]) 147 | s.add_dependency(%q, [">= 0"]) 148 | end 149 | end 150 | 151 | -------------------------------------------------------------------------------- /test/rails_app/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Ruby on Rails: Welcome aboard 5 | 174 | 187 | 188 | 189 |
190 | 203 | 204 |
205 | 209 | 210 | 214 | 215 |
216 |

Getting started

217 |

Here’s how to get rolling:

218 | 219 |
    220 |
  1. 221 |

    Use rails generate to create your models and controllers

    222 |

    To see all available options, run it without parameters.

    223 |
  2. 224 | 225 |
  3. 226 |

    Set up a default route and remove public/index.html

    227 |

    Routes are set up in config/routes.rb.

    228 |
  4. 229 | 230 |
  5. 231 |

    Create your database

    232 |

    Run rake db:create to create your database. If you're not using SQLite (the default), edit config/database.yml with your username and password.

    233 |
  6. 234 |
235 |
236 |
237 | 238 | 239 |
240 | 241 | 242 | --------------------------------------------------------------------------------