jeffp-wizardly 0.1.7 → 0.1.8

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.rdoc CHANGED
@@ -117,17 +117,66 @@ for both cases
117
117
  act_wizardly_for :user, :redirect=>'/main'
118
118
  end
119
119
 
120
- ==== Other options
120
+ ==== Options For act_wizardly_for
121
121
 
122
122
  Here's a list of options you can use in the macro
123
123
 
124
124
  :completed => '/main/finished'
125
125
  :canceled => {:controller=>'main', :action=>'canceled'}
126
126
  :skip => true
127
+ :guard => false
127
128
  :mask_fields => [:password, :password_confirmation] (by default)
129
+ :persist_model => {:once|:per_page}
130
+ :form_data => {:sandbox|:session}
128
131
 
129
132
  Setting the :skip option to +true+ tells the scaffold helpers to include or exclude a skip button on each page.
130
133
  The :mask_fields options tells the scaffold generator which fields to generate as 'type=password' fields.
134
+ :persist_model and :form_data are explained below.
135
+
136
+
137
+ ==== Preserving Form Field Data
138
+
139
+ The :form_data option controls how the form data is preserved between
140
+ page requests that call outside the wizard controller. The default option setting,
141
+ :session, keeps the form data until the wizard is complete regardless of
142
+ whether the user leaves the wizard and returns later. The form
143
+ data is preserved for the life of the session or until the user completes the wizard.
144
+
145
+ The other option setting, :sandbox, clears the form data whenever
146
+ the user leaves the wizard before the wizard is complete. This includes pressing
147
+ a :cancel button, a hyperlink or plainly navigating somewhere else.
148
+ Upon returning to the wizard, the form is reset and the user starts fresh.
149
+
150
+ The form data is always cleared once the user has completed the wizard and the
151
+ database record has been created.
152
+
153
+ ==== Guarding Wizard Entry
154
+
155
+ The :guard option controls how a user may enter the wizard. If set to true, the
156
+ default, the wizard is guarded from entry anywhere except the first page. The wizard
157
+ controller will automatically redirect to the first page. When set to false, entry
158
+ may occur at any point. This may be useful for testing purposes and instances where
159
+ the application needs to navigate away and return to the wizard.
160
+
161
+ The guarding behavior works a little differently depending on the :form_data setting.
162
+ When :form_data is set to :session (the default behavior), guarding only occurs
163
+ for the initial entry. Once a user has entered the form and started it, while
164
+ form data is being kept, the application may thereafter enter anywhere. On the
165
+ contrary, if :form_data is set to :sandbox, entry is always guarded, and once the user
166
+ leaves the wizard, entry may only occur at the initial page (as the form data has
167
+ been reset).
168
+
169
+ ==== Saving The Model
170
+
171
+ The :persist_model option controls how the model is saved, either :once or :per_page.
172
+ The default option :once, only saves the model when the wizard is completed, by the
173
+ user pressing a :finish button or :next button on the final page. This method
174
+ prevents numerous incomplete models and possibly invalid models being saved to the
175
+ database.
176
+
177
+ The other option setting, :per_page, saves the model incrementally for each time
178
+ the form data validates as the user moves through the pages.
179
+
131
180
 
132
181
  === Buttons
133
182
 
@@ -263,6 +312,24 @@ call back for this.
263
312
  end
264
313
  end
265
314
 
315
+ === Creating Scaffolds
316
+
317
+ Wizard scaffolds can be created for any wizardly controller (one using the acts_wizardly_for
318
+ macro).
319
+
320
+ ./script/generate wizardly_scaffold controller_name --haml
321
+
322
+ The wizardly_scaffold generator will create HTML view scaffolds by default. Append a
323
+ --haml option to create scaffolds in HAML.
324
+
325
+ Sometimes you have already edited views from a scaffold but want to regenerate the
326
+ scaffold because of changes to your model without overwriting the current views.
327
+ Use the --underscore option to create corresponding views with an underscore prefixing
328
+ each page.
329
+
330
+ ./script/generate wizardly_scaffold controller_name --underscore
331
+
332
+
266
333
  == Advanced Configuration
267
334
 
268
335
  To be provided
@@ -8,14 +8,20 @@ module Wizardly
8
8
  module Wizard
9
9
  class Configuration
10
10
  attr_reader :pages, :completed_redirect, :canceled_redirect, :controller_name, :page_order
11
+
12
+ #enum_attr :persistance, %w(sandbox session database)
11
13
 
12
14
  def initialize(controller_name, opts) #completed_redirect = nil, canceled_redirect = nil)
13
15
  @controller_name = controller_name
14
16
  @completed_redirect = opts[:redirect] || opts[:completed] || opts[:when_completed] #format_redirect(completed_redirect)
15
17
  @canceled_redirect = opts[:redirect] || opts[:canceled] || opts[:when_canceled]
16
18
  @allow_skipping = opts[:skip] || opts[:allow_skip] || opts[:allow_skipping] || false
17
- @guard_entry = opts.key?(:guard) ? opts[:guard] : true
19
+ @guard_entry = opts.key?(:guard) ? opts[:guard] : true
18
20
  @password_fields = opts[:mask_fields] || opts[:mask_passwords] || [:password, :password_confirmation]
21
+ @persist_model = opts[:persist_model] || :once
22
+ @form_data = opts[:form_data] || :session
23
+ raise(ArgumentError, ":persist_model option must be one of :once or :per_page", caller) unless [:once, :per_page].include?(@persist_model)
24
+ raise(ArgumentError, ":form_data option must be one of :sandbox or :session", caller) unless [:sandbox, :session].include?(@form_data)
19
25
  @page_order = []
20
26
  @pages = {}
21
27
  @buttons = nil
@@ -25,6 +31,8 @@ module Wizardly
25
31
  end
26
32
  end
27
33
 
34
+ def persist_model_per_page?; @persist_model == :per_page; end
35
+ def form_data_keep_in_session?; @form_data == :session; end
28
36
  def model; @wizard_model_sym; end
29
37
  def model_instance_variable; "@#{@wizard_model_sym.to_s}"; end
30
38
  def model_class_name; @wizard_model_class_name; end
@@ -37,6 +45,11 @@ module Wizardly
37
45
  index += 1 unless self.last_page?(name)
38
46
  @page_order[index]
39
47
  end
48
+ def previous_page(name)
49
+ index = @page_order.index(name)
50
+ index -= 1 unless self.first_page?(name)
51
+ @page_order[index]
52
+ end
40
53
  def button_for_function(name); @default_buttons[name]; end
41
54
  def buttons
42
55
  return @buttons if @buttons
@@ -49,9 +49,14 @@ MACRO
49
49
  def index
50
50
  redirect_to :action=>:#{self.page_order.first}
51
51
  end
52
+
52
53
  INDEX
53
54
  mb.string
54
55
  end
56
+
57
+ def persist_key;
58
+ @persist_key ||= "wizardly_#{controller_name.to_s.underscore}_#{model}".to_sym
59
+ end
55
60
 
56
61
  def print_page_action_method(id)
57
62
  page = @pages[id]
@@ -64,8 +69,8 @@ MACRO
64
69
  @wizard = wizard_config
65
70
  @title = '#{page.title}'
66
71
  @description = '#{page.description}'
67
- h = (flash[:wizard_model]||{}).merge(params[:#{self.model}] || {})
68
- @#{self.model} = #{self.model_class_name}.new(h)
72
+ h = (self.wizard_form_data||{}).merge(params[:#{self.model}] || {})
73
+ @#{self.model} = build_wizard_model(h)
69
74
  if request.post? && callback_performs_action?(:on_post_#{id}_form)
70
75
  raise CallbackError, "render or redirect not allowed in :on_post_#{id}_form callback", caller
71
76
  end
@@ -87,31 +92,33 @@ MACRO
87
92
  ONE
88
93
  if self.last_page?(id)
89
94
  mb << <<-TWO
90
- return if callback_performs_action?(:on_#{id}_form_#{finish_button})
95
+ callback_performs_action?(:on_#{id}_form_#{finish_button})
91
96
  _on_wizard_#{finish_button}
92
97
  redirect_to #{Utils.formatted_redirect(self.completed_redirect)} unless self.performed?
93
98
  TWO
94
99
  elsif self.first_page?(id)
95
100
  mb << <<-THREE
96
101
  if button_id == :#{finish_button}
97
- return if callback_performs_action?(:on_#{id}_form_#{finish_button})
102
+ callback_performs_action?(:on_#{id}_form_#{finish_button})
98
103
  _on_wizard_#{finish_button} if button_id == :#{finish_button}
99
104
  redirect_to #{Utils.formatted_redirect(self.completed_redirect)} unless self.performed?
100
105
  return
101
106
  end
102
- session[:progression] = [:#{id}]
107
+ save_wizard_model! if wizard_config.persist_model_per_page?
108
+ # session[:progression] = [:#{id}]
103
109
  return if callback_performs_action?(:on_#{id}_form_#{next_button})
104
110
  redirect_to :action=>:#{self.next_page(id)}
105
111
  THREE
106
112
  else
107
113
  mb << <<-FOUR
108
114
  if button_id == :#{finish_button}
109
- return if callback_performs_action?(:on_#{id}_form_#{finish_button})
115
+ callback_performs_action?(:on_#{id}_form_#{finish_button})
110
116
  _on_wizard_#{finish_button} if button_id == :#{finish_button}
111
117
  redirect_to #{Utils.formatted_redirect(self.completed_redirect)} unless self.performed?
112
118
  return
113
119
  end
114
- session[:progression].push(:#{id})
120
+ save_wizard_model! if wizard_config.persist_model_per_page?
121
+ # session[:progression].push(:#{id})
115
122
  return if callback_performs_action?(:on_#{id}_form_#{next_button})
116
123
  redirect_to :action=>:#{self.next_page(id)}
117
124
  FOUR
@@ -119,7 +126,7 @@ MACRO
119
126
 
120
127
  mb << <<-ENSURE
121
128
  ensure
122
- flash[:wizard_model] = h.merge(@#{self.model}.attributes)
129
+ self.wizard_form_data = h.merge(@#{self.model}.attributes) if (@#{self.model} && !@completed)
123
130
  end
124
131
  end
125
132
  ENSURE
@@ -135,6 +142,8 @@ ENSURE
135
142
  protected
136
143
  def _on_wizard_#{finish}
137
144
  @#{self.model}.save_without_validation!
145
+ @completed = true
146
+ reset_wizard_form_data
138
147
  _wizard_final_redirect_to(:completed)
139
148
  end
140
149
  def _on_wizard_#{skip}
@@ -142,13 +151,13 @@ ENSURE
142
151
  end
143
152
  def _on_wizard_#{back}
144
153
  # TODO: fix progression management
145
- redirect_to(:action=>((session[:progression]||[]).pop || :#{self.page_order.first})) unless self.performed?
154
+ # redirect_to(:action=>((session[:progression]||[]).pop || :#{self.page_order.first})) unless self.performed?
155
+ redirect_to(:action=>wizard_config.previous_page(@step)) unless self.performed?
146
156
  end
147
157
  def _on_wizard_#{cancel}
148
158
  _wizard_final_redirect_to(:canceled)
149
159
  end
150
160
  def _wizard_final_redirect_to(which_redirect)
151
- flash.discard(:wizard_model)
152
161
  initial_referer = reset_wizard_session_vars
153
162
  unless self.performed?
154
163
  redir = (which_redirect == :completed ? wizard_config.completed_redirect : wizard_config.canceled_redirect) || initial_referer
@@ -175,12 +184,53 @@ ENSURE
175
184
  else
176
185
  session[:initial_referer] = nil
177
186
  end
178
- flash.discard(:wizard_model)
187
+ if wizard_config.form_data_keep_in_session?
188
+ return if self.wizard_form_data # if it has an id we've started a wizard
189
+ else
190
+ reset_wizard_form_data
191
+ end
179
192
  #{guard_line}
180
193
  redirect_to :action=>:#{first_page} unless (params[:action] || '') == '#{first_page}'
181
- end
194
+ end
182
195
  hide_action :guard_entry
183
196
 
197
+ def save_wizard_model!
198
+ @#{self.model}.save_without_validation!
199
+ if wizard_config.form_data_keep_in_session?
200
+ h = self.wizard_form_data
201
+ h['id'] = @#{self.model}.id
202
+ self.wizard_form_data= h
203
+ end
204
+ end
205
+ def build_wizard_model(params)
206
+ if (wizard_config.persist_model_per_page? && (model_id = params['id']))
207
+ _model = #{self.model_class_name}.find(model_id)
208
+ _model.attributes = params
209
+ _model
210
+ else
211
+ #{self.model_class_name}.new(params)
212
+ end
213
+ end
214
+ hide_action :build_wizard_model, :save_wizard_model!
215
+
216
+ def wizard_form_data=(hash)
217
+ if wizard_config.form_data_keep_in_session?
218
+ session[:#{self.persist_key}] = hash
219
+ else
220
+ if hash
221
+ flash[:#{self.persist_key}] = hash
222
+ else
223
+ flash.discard(:#{self.persist_key})
224
+ end
225
+ end
226
+ end
227
+
228
+ def reset_wizard_form_data; self.wizard_form_data = nil; end
229
+ def wizard_form_data
230
+ wizard_config.form_data_keep_in_session? ? session[:#{self.persist_key}] : flash[:#{self.persist_key}]
231
+ end
232
+ hide_action :wizard_form_data, :wizard_form_data=, :reset_wizard_form_data
233
+
184
234
  def render_wizard_form
185
235
  end
186
236
  hide_action :render_wizard_form
@@ -16,6 +16,7 @@ class WizardlyScaffoldGenerator < Rails::Generator::Base
16
16
  def add_options!(opt)
17
17
  opt.on('--haml', 'Generate scaffold for haml wizard') { |v| options[:output] = :haml }
18
18
  opt.on('--ajax', 'Generate scaffold for ajax wizard') { |v| options[:output] = :ajax }
19
+ opt.on('--underscore', 'Append an underscore to front of each file') { |v| options[:underscore] = true }
19
20
  end
20
21
 
21
22
 
@@ -62,10 +63,11 @@ class WizardlyScaffoldGenerator < Rails::Generator::Base
62
63
  #m.directory(File.join('test/functional', controller_class_path))
63
64
  #m.directory(File.join('public/stylesheets', class_path))
64
65
 
66
+ underscore = options[:underscore] ? '_' : ''
65
67
  pages.each do |id, page|
66
68
  m.template(
67
69
  "form.#{view_file_ext.first}",
68
- File.join('app/views', controller_class_path, controller_name, "#{id}.#{view_file_ext.last}"),
70
+ File.join('app/views', controller_class_path, controller_name, "#{underscore}#{id}.#{view_file_ext.last}"),
69
71
  :assigns=>{:id=>id, :page=>page}
70
72
  )
71
73
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jeffp-wizardly
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeff Patmon
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-08-11 00:00:00 -07:00
12
+ date: 2009-08-12 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15