wizardry 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +77 -5
- data/lib/wizardry.rb +2 -1
- data/lib/wizardry/base.rb +5 -3
- data/lib/wizardry/routes.rb +34 -0
- data/lib/wizardry/version.rb +1 -1
- data/spec/dummy/app/controllers/products_controller.rb +2 -0
- data/spec/dummy/config/routes.rb +4 -56
- data/spec/spec_helper.rb +17 -1
- data/spec/wizardry/base_spec.rb +29 -10
- data/spec/wizardry/routes_spec.rb +72 -0
- data/wizardry.gemspec +2 -1
- metadata +25 -4
data/README.md
CHANGED
@@ -6,12 +6,84 @@ Simple step-by-step wizard for Rails.
|
|
6
6
|
|
7
7
|
Add this line to your application's Gemfile:
|
8
8
|
|
9
|
-
|
9
|
+
```ruby
|
10
|
+
gem 'wizardry'
|
11
|
+
```
|
10
12
|
|
11
|
-
And
|
12
|
-
|
13
|
-
$ bundle
|
13
|
+
And run the `bundle` command to install it.
|
14
14
|
|
15
15
|
## Usage
|
16
16
|
|
17
|
-
|
17
|
+
Bring wizardry to your model:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
class Product < ActiveRecord::Base
|
21
|
+
wizardry :initial, :middle, :final
|
22
|
+
end
|
23
|
+
```
|
24
|
+
|
25
|
+
And you will get a bunch of methods.
|
26
|
+
To get name of the current step:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
@product.current_step # => 'initial'
|
30
|
+
```
|
31
|
+
|
32
|
+
To change the current step name:
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
@product.current_step = :middle
|
36
|
+
```
|
37
|
+
|
38
|
+
Will be set only if new step name is included in wizardry steps (**initial**, **middle**, **final** in our example).
|
39
|
+
|
40
|
+
For the second step name:
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
@product.second_step # => 'middle'
|
44
|
+
```
|
45
|
+
|
46
|
+
Also `first_step`, `third_step`, etc. methods are available.
|
47
|
+
|
48
|
+
You can check position of the current step:
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
@product.second_step? # => true
|
52
|
+
# or
|
53
|
+
@product.current_step.middle? # => true
|
54
|
+
```
|
55
|
+
|
56
|
+
Also `first_step?`, `third_step?`, etc. methods are accessible.
|
57
|
+
|
58
|
+
To get name of the next and previous steps:
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
@product.next_step
|
62
|
+
# and
|
63
|
+
@product.previous_step
|
64
|
+
```
|
65
|
+
|
66
|
+
If not exists next/previous step then `nil` will be returned.
|
67
|
+
|
68
|
+
Class methods are accessible:
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
Product.steps # => ['initial', 'middle', 'final']
|
72
|
+
# and
|
73
|
+
Product.steps_regexp # => /initial|middle|final/
|
74
|
+
```
|
75
|
+
|
76
|
+
### Routing helpers
|
77
|
+
|
78
|
+
Wizardry includes some routing helpers:
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
resources :products do
|
82
|
+
has_wizardry
|
83
|
+
end
|
84
|
+
# same as
|
85
|
+
wizardry_resources :products
|
86
|
+
```
|
87
|
+
|
88
|
+
will replace default *:id/edit* path with *:id/edit/:step* (and `:step => /initial|middle|final/` in our example).
|
89
|
+
`has_wizardry` is also acceptable for singular resources. And `wizardry_resource` herper is here for that.
|
data/lib/wizardry.rb
CHANGED
data/lib/wizardry/base.rb
CHANGED
@@ -4,9 +4,10 @@ module Wizardry
|
|
4
4
|
|
5
5
|
module ClassMethods
|
6
6
|
def wizardry(*steps)
|
7
|
-
|
7
|
+
class_attribute :steps, :steps_regexp, instance_writer: false
|
8
8
|
|
9
|
-
self.steps = steps
|
9
|
+
self.steps = steps.map{ |s| s.to_s.inquiry }
|
10
|
+
self.steps_regexp = Regexp.new(steps.join('|'))
|
10
11
|
|
11
12
|
include WizardryMethods
|
12
13
|
|
@@ -30,7 +31,8 @@ module Wizardry
|
|
30
31
|
end
|
31
32
|
|
32
33
|
def current_step=(step)
|
33
|
-
|
34
|
+
step = step.to_s
|
35
|
+
@current_step = step.inquiry if steps.include?(step)
|
34
36
|
end
|
35
37
|
|
36
38
|
def next_step
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class ActionDispatch::Routing::Mapper
|
2
|
+
def has_wizardry
|
3
|
+
unless resource_scope?
|
4
|
+
raise ArgumentError, "can't use has_wizardry outside resource(s) scope"
|
5
|
+
end
|
6
|
+
|
7
|
+
options = @scope[:scope_level_resource].options
|
8
|
+
|
9
|
+
if options.has_key?(:only)
|
10
|
+
only = Array.wrap(options.delete(:only))
|
11
|
+
only.map!(&:to_sym).delete(:edit)
|
12
|
+
options.merge!(only: only) if only.present?
|
13
|
+
end
|
14
|
+
|
15
|
+
except = Array.wrap(options.delete(:except))
|
16
|
+
except.map!(&:to_sym) << :edit
|
17
|
+
options.merge!(except: except.uniq)
|
18
|
+
|
19
|
+
res = @scope[:scope_level_resource].instance_variable_get(:@name)
|
20
|
+
|
21
|
+
get 'edit/:step' => :edit, step: res.to_s.classify.constantize.steps_regexp, as: :edit, on: :member
|
22
|
+
end
|
23
|
+
|
24
|
+
[:resources, :resource].each do |method|
|
25
|
+
class_eval <<-EOT, __FILE__, __LINE__ + 1
|
26
|
+
def wizardry_#{method}(*res) # def wizardry_resources(*res)
|
27
|
+
#{method} *res do # resources *res do
|
28
|
+
has_wizardry # has_wizardry
|
29
|
+
yield if block_given? # yield if block_given?
|
30
|
+
end # end
|
31
|
+
end # end
|
32
|
+
EOT
|
33
|
+
end
|
34
|
+
end
|
data/lib/wizardry/version.rb
CHANGED
data/spec/dummy/config/routes.rb
CHANGED
@@ -1,58 +1,6 @@
|
|
1
1
|
Dummy::Application.routes.draw do
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
# match 'products/:id' => 'catalog#view'
|
7
|
-
# Keep in mind you can assign values other than :controller and :action
|
8
|
-
|
9
|
-
# Sample of named route:
|
10
|
-
# match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
|
11
|
-
# This route can be invoked with purchase_url(:id => product.id)
|
12
|
-
|
13
|
-
# Sample resource route (maps HTTP verbs to controller actions automatically):
|
14
|
-
# resources :products
|
15
|
-
|
16
|
-
# Sample resource route with options:
|
17
|
-
# resources :products do
|
18
|
-
# member do
|
19
|
-
# get 'short'
|
20
|
-
# post 'toggle'
|
21
|
-
# end
|
22
|
-
#
|
23
|
-
# collection do
|
24
|
-
# get 'sold'
|
25
|
-
# end
|
26
|
-
# end
|
27
|
-
|
28
|
-
# Sample resource route with sub-resources:
|
29
|
-
# resources :products do
|
30
|
-
# resources :comments, :sales
|
31
|
-
# resource :seller
|
32
|
-
# end
|
33
|
-
|
34
|
-
# Sample resource route with more complex sub-resources
|
35
|
-
# resources :products do
|
36
|
-
# resources :comments
|
37
|
-
# resources :sales do
|
38
|
-
# get 'recent', :on => :collection
|
39
|
-
# end
|
40
|
-
# end
|
41
|
-
|
42
|
-
# Sample resource route within a namespace:
|
43
|
-
# namespace :admin do
|
44
|
-
# # Directs /admin/products/* to Admin::ProductsController
|
45
|
-
# # (app/controllers/admin/products_controller.rb)
|
46
|
-
# resources :products
|
47
|
-
# end
|
48
|
-
|
49
|
-
# You can have the root of your site routed with "root"
|
50
|
-
# just remember to delete public/index.html.
|
51
|
-
# root :to => 'welcome#index'
|
52
|
-
|
53
|
-
# See how all your routes lay out with "rake routes"
|
54
|
-
|
55
|
-
# This is a legacy wild controller route that's not recommended for RESTful applications.
|
56
|
-
# Note: This route will make all actions in every controller accessible via GET requests.
|
57
|
-
# match ':controller(/:action(/:id))(.:format)'
|
2
|
+
resources :products, except: :destroy, path_names: { new: :make } do
|
3
|
+
get :commit, on: :collection
|
4
|
+
has_wizardry
|
5
|
+
end
|
58
6
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,4 +1,20 @@
|
|
1
1
|
ENV['RAILS_ENV'] = 'test'
|
2
2
|
|
3
|
-
require File.expand_path('../dummy/config/environment.rb',
|
3
|
+
require File.expand_path('../dummy/config/environment.rb', __FILE__)
|
4
4
|
require 'minitest/autorun'
|
5
|
+
require 'action_controller/test_case'
|
6
|
+
|
7
|
+
class MiniTest::Spec
|
8
|
+
include ActiveSupport::Testing::SetupAndTeardown
|
9
|
+
end
|
10
|
+
|
11
|
+
class RoutingSpec < Minitest::Spec
|
12
|
+
include ActionDispatch::Integration::Runner
|
13
|
+
include Rails.application.routes.url_helpers
|
14
|
+
|
15
|
+
before do
|
16
|
+
@routes = Rails.application.routes
|
17
|
+
end
|
18
|
+
|
19
|
+
register_spec_type(/Routing/, self)
|
20
|
+
end
|
data/spec/wizardry/base_spec.rb
CHANGED
@@ -7,28 +7,47 @@ describe Wizardry::Base do
|
|
7
7
|
end
|
8
8
|
|
9
9
|
it 'must have steps array' do
|
10
|
-
assert_equal Product.steps, [
|
10
|
+
assert_equal Product.steps, %w[initial middle final]
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'must have steps regexp' do
|
14
|
+
assert_equal Product.steps_regexp, /initial|middle|final/
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'must be instance of StringInquirer class in steps array' do
|
18
|
+
assert_equal Product.steps.map{ |s| s.instance_of?(ActiveSupport::StringInquirer) }, [true, true, true]
|
11
19
|
end
|
12
20
|
end
|
13
21
|
|
14
22
|
describe 'instance methods' do
|
15
|
-
before
|
23
|
+
before do
|
16
24
|
@product = Product.new
|
17
25
|
end
|
18
26
|
|
19
27
|
it 'must return current step as first step' do
|
20
|
-
assert_equal @product.current_step,
|
28
|
+
assert_equal @product.current_step, 'initial'
|
21
29
|
end
|
22
30
|
|
23
|
-
it 'must
|
31
|
+
it 'must update current step name with instance of StringInquirer' do
|
32
|
+
@product.current_step = 'middle'
|
33
|
+
assert_equal @product.current_step, 'middle'
|
34
|
+
assert_instance_of ActiveSupport::StringInquirer, @product.current_step
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'must update current step name when symbol is passed' do
|
24
38
|
@product.current_step = :middle
|
25
|
-
assert_equal @product.current_step,
|
39
|
+
assert_equal @product.current_step, 'middle'
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'must not update current step name' do
|
43
|
+
@product.current_step = :fictional
|
44
|
+
refute_equal @product.current_step, 'fictional'
|
26
45
|
end
|
27
46
|
|
28
47
|
it 'must have ordinal methods' do
|
29
|
-
assert_equal @product.first_step,
|
30
|
-
assert_equal @product.second_step,
|
31
|
-
assert_equal @product.last_step,
|
48
|
+
assert_equal @product.first_step, 'initial'
|
49
|
+
assert_equal @product.second_step, 'middle'
|
50
|
+
assert_equal @product.last_step, 'final'
|
32
51
|
|
33
52
|
assert @product.first_step?
|
34
53
|
refute @product.second_step?
|
@@ -42,7 +61,7 @@ describe Wizardry::Base do
|
|
42
61
|
end
|
43
62
|
|
44
63
|
it 'must return next step name' do
|
45
|
-
assert_equal @product.next_step,
|
64
|
+
assert_equal @product.next_step, 'middle'
|
46
65
|
end
|
47
66
|
|
48
67
|
it 'must return `nil` on last step' do
|
@@ -56,7 +75,7 @@ describe Wizardry::Base do
|
|
56
75
|
|
57
76
|
it 'must return previous step name' do
|
58
77
|
@product.current_step = :middle
|
59
|
-
assert_equal @product.previous_step,
|
78
|
+
assert_equal @product.previous_step, 'initial'
|
60
79
|
end
|
61
80
|
end
|
62
81
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Wizardry Routing' do
|
4
|
+
it 'must accept wizardry `edit` routes' do
|
5
|
+
assert_recognizes({ controller: 'products', action: 'edit', id: '1', step: 'initial'}, '/products/1/edit/initial')
|
6
|
+
assert_recognizes({ controller: 'products', action: 'edit', id: '1', step: 'middle'}, '/products/1/edit/middle')
|
7
|
+
assert_recognizes({ controller: 'products', action: 'edit', id: '1', step: 'final'}, '/products/1/edit/final')
|
8
|
+
assert '/products/1/initial', edit_product_path(id: 1, step: 'initial')
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'must accept only valid steps' do
|
12
|
+
assert_raises ActionController::RoutingError do
|
13
|
+
assert_recognizes({ controller: 'products', action: 'edit', id: '1', step: 'fictional'}, '/products/1/edit/fictional')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'must not accept default `edit` route' do
|
18
|
+
assert_raises ActionController::RoutingError do
|
19
|
+
assert_recognizes({ controller: 'products', action: 'edit', id: '1'}, '/products/1/edit')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'must not accept `destroy` route' do
|
24
|
+
assert_raises ActionController::RoutingError do
|
25
|
+
assert_recognizes({ controller: 'products', action: 'destroy', id: '1'}, { path: '/products/1', method: :delete })
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'must accept routes defined in block' do
|
30
|
+
assert_recognizes({ controller: 'products', action: 'commit' }, '/products/commit')
|
31
|
+
assert '/products/commit', commit_products_path
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'must have overriden `new` path name' do
|
35
|
+
assert_recognizes({ controller: 'products', action: 'new' }, '/products/make')
|
36
|
+
assert '/products/make', new_product_path
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'must have `wizardry_resources` helper' do
|
40
|
+
with_routing do |set|
|
41
|
+
set.draw{ wizardry_resources :products }
|
42
|
+
|
43
|
+
assert_recognizes({ controller: 'products', action: 'edit', id: '1', step: 'initial'}, '/products/1/edit/initial')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'must have `wizardry_resource` helper' do
|
48
|
+
with_routing do |set|
|
49
|
+
set.draw{ wizardry_resource :products }
|
50
|
+
|
51
|
+
assert_recognizes({ controller: 'products', action: 'edit', step: 'initial'}, '/products/edit/initial')
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'must not let to use `has_wizardry` outside resource(s) scope' do
|
56
|
+
with_routing do |set|
|
57
|
+
assert_raises ArgumentError do
|
58
|
+
set.draw{ has_wizardry }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'must not accept default `edit` route with `only` option' do
|
64
|
+
with_routing do |set|
|
65
|
+
set.draw{ wizardry_resources :products, only: [:edit, :update] }
|
66
|
+
|
67
|
+
assert_raises ActionController::RoutingError do
|
68
|
+
assert_recognizes({ controller: 'products', action: 'edit', id: '1'}, '/products/1/edit')
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/wizardry.gemspec
CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |gem|
|
|
8
8
|
|
9
9
|
gem.files = `git ls-files`.split($\)
|
10
10
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
11
|
-
gem.test_files = gem.files.grep(%r{^
|
11
|
+
gem.test_files = gem.files.grep(%r{^spec/})
|
12
12
|
gem.name = 'wizardry'
|
13
13
|
gem.require_paths = ['lib']
|
14
14
|
gem.version = Wizardry::VERSION
|
@@ -16,4 +16,5 @@ Gem::Specification.new do |gem|
|
|
16
16
|
gem.add_dependency 'rails', '~> 3.2'
|
17
17
|
|
18
18
|
gem.add_development_dependency 'sqlite3'
|
19
|
+
gem.add_development_dependency 'minitest'
|
19
20
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wizardry
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-08-
|
12
|
+
date: 2012-08-13 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -43,6 +43,22 @@ dependencies:
|
|
43
43
|
- - ! '>='
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: minitest
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
46
62
|
description: Simple step-by-step wizard for Rails
|
47
63
|
email:
|
48
64
|
- lexmag@gmail.com
|
@@ -57,11 +73,13 @@ files:
|
|
57
73
|
- Rakefile
|
58
74
|
- lib/wizardry.rb
|
59
75
|
- lib/wizardry/base.rb
|
76
|
+
- lib/wizardry/routes.rb
|
60
77
|
- lib/wizardry/version.rb
|
61
78
|
- spec/dummy/Rakefile
|
62
79
|
- spec/dummy/app/assets/javascripts/application.js
|
63
80
|
- spec/dummy/app/assets/stylesheets/application.css
|
64
81
|
- spec/dummy/app/controllers/application_controller.rb
|
82
|
+
- spec/dummy/app/controllers/products_controller.rb
|
65
83
|
- spec/dummy/app/helpers/application_helper.rb
|
66
84
|
- spec/dummy/app/mailers/.gitkeep
|
67
85
|
- spec/dummy/app/models/.gitkeep
|
@@ -94,6 +112,7 @@ files:
|
|
94
112
|
- spec/dummy/script/rails
|
95
113
|
- spec/spec_helper.rb
|
96
114
|
- spec/wizardry/base_spec.rb
|
115
|
+
- spec/wizardry/routes_spec.rb
|
97
116
|
- wizardry.gemspec
|
98
117
|
homepage: ''
|
99
118
|
licenses: []
|
@@ -109,7 +128,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
109
128
|
version: '0'
|
110
129
|
segments:
|
111
130
|
- 0
|
112
|
-
hash:
|
131
|
+
hash: -4094284250890369797
|
113
132
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
133
|
none: false
|
115
134
|
requirements:
|
@@ -118,7 +137,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
118
137
|
version: '0'
|
119
138
|
segments:
|
120
139
|
- 0
|
121
|
-
hash:
|
140
|
+
hash: -4094284250890369797
|
122
141
|
requirements: []
|
123
142
|
rubyforge_project:
|
124
143
|
rubygems_version: 1.8.24
|
@@ -130,6 +149,7 @@ test_files:
|
|
130
149
|
- spec/dummy/app/assets/javascripts/application.js
|
131
150
|
- spec/dummy/app/assets/stylesheets/application.css
|
132
151
|
- spec/dummy/app/controllers/application_controller.rb
|
152
|
+
- spec/dummy/app/controllers/products_controller.rb
|
133
153
|
- spec/dummy/app/helpers/application_helper.rb
|
134
154
|
- spec/dummy/app/mailers/.gitkeep
|
135
155
|
- spec/dummy/app/models/.gitkeep
|
@@ -162,3 +182,4 @@ test_files:
|
|
162
182
|
- spec/dummy/script/rails
|
163
183
|
- spec/spec_helper.rb
|
164
184
|
- spec/wizardry/base_spec.rb
|
185
|
+
- spec/wizardry/routes_spec.rb
|