wicked-wizard-validations 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 25eca985f7b7e66f68b8165e46c744d34eed8fd1
4
+ data.tar.gz: 9bf3a9ca457016634db345eef04e03378289f2dc
5
+ SHA512:
6
+ metadata.gz: e65f8a0903dfdaf3bad7001695f3551930ede69bd99298552fabe5c676b5e1744e42c95ae3141836c1bbdef6ecd3bea4398ff427d7ce5829430d4bd9b6773954
7
+ data.tar.gz: 3ea04955be321a0d66174f177da9054d244b7a5067324f83e0c66235623df4703836be688251015313c5b9325c3348c1761c06b4101eb498f615435cef48a6cc
@@ -0,0 +1,23 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
23
+ .idea
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in wicked-wizard-validations.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Error Ltd.
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,247 @@
1
+ # Wicked::Wizard::Validations - a validation mixin for Wicked
2
+ This is a mixin for [the Wicked wizard gem](https://github.com/schneems/wicked) which makes it easier to conditionally validate your models based on where the user is in the wizard process.
3
+
4
+ ## Why would I want to use this?
5
+ We often come up against a situation where you want to validate the data is entering into a wizard form, but only the fields they have seen already. Imagine this 3-step process:
6
+
7
+ 1. First name, last name, email
8
+ 2. Password, Password Confirmation
9
+ 3. Contact details
10
+
11
+ If the user were at step 1, it would be useless having a basic model validation requiring password be completed: they haven't seen that field yet.
12
+
13
+ So what we want to do is _conditionally validate_ the fields, based on the user's progress. We do this by creating a class method for each step you want to validate, with a hash to pass to the validator.
14
+
15
+ __Note:__ this requires the step the user is on to be stored in the user model.
16
+
17
+ ## Installation
18
+
19
+ Add this line to your application's Gemfile:
20
+
21
+ gem 'wicked-wizard-validations'
22
+
23
+ And then execute:
24
+
25
+ $ bundle
26
+
27
+ Or install it yourself as:
28
+
29
+ $ gem install wicked-wizard-validations
30
+
31
+ ## Usage
32
+ For the purposes of this demo, we'll assume you're validating a model called `User`. Of course, your model could be called anything, and there's no reason why you can't validate several models at one using a common set of steps.
33
+
34
+ ### Create your model
35
+ Create an ActiveRecord model as normal. This gem looks for a string field called `current_step` (which is configurable - see below).
36
+
37
+ ### Define your wizard steps.
38
+ The easiest place to define your wizard steps is in your model. This is a departure from the normal Wicked::Wizard way of doing it, which is in the controller
39
+
40
+ ```
41
+ class User < ActiveRecord::Base
42
+ include Wicked::Wizard::Validations
43
+
44
+ #This method defines the step names. You still need to call `step` in the controller.
45
+ def self.wizard_steps
46
+ [
47
+ "basic_details",
48
+ "password",
49
+ "contact_details"
50
+ ]
51
+ end
52
+ ```
53
+
54
+ ```
55
+ class UsersController < ApplicationController
56
+ include Wicked::Wizard
57
+ # This is the 'normal' place to define Wicked::Wizard steps.
58
+ # We just call the steps we defined above in User.
59
+ steps(*User.wizard_steps)
60
+ end
61
+ ```
62
+
63
+ ### Add your validations for each step
64
+ Ok, so now you have steps, and the controller knows about them. How do you add validations?
65
+
66
+ You create _class_ methods on `User` which correspond to the name of the step, with `_validations` at the end. This method needs to return a hash of field names and keys, the latter of which which is passed straight to [Activerecord Validations](http://guides.rubyonrails.org/active_record_validations.html).
67
+
68
+ ```
69
+ class User < ActiveRecord::Base
70
+ include Wicked::Wizard::Validations
71
+
72
+ #This method defines the step names
73
+ def self.wizard_steps
74
+ [
75
+ "basic_details",
76
+ "password",
77
+ "contact_details"
78
+ ]
79
+ end
80
+
81
+
82
+ def self.basic_details_validations # validations for the basic_details step.
83
+ {
84
+ first_name: {
85
+ presence: {
86
+ message: "Don't be shy! We need your first name."
87
+ }
88
+ },
89
+ last_name: {
90
+ presence: true #just the default ActiveRecord validation message
91
+ },
92
+ email: {
93
+ presence: true, on: :update #this validation only happens on update, not create.
94
+ }
95
+ }
96
+ end
97
+ ```
98
+
99
+ ### Set up validations
100
+ The last stage is to set up the validations when the model is loaded. That's a one-liner in the model:
101
+
102
+ ```
103
+ class User < ActiveRecord::Base
104
+ include Wicked::Wizard::Validations
105
+
106
+ # Setup the validations when this class is loaded
107
+ self.setup_validations!
108
+
109
+ #other stuff in here
110
+ end
111
+
112
+ ```
113
+
114
+ And that's it! For a given step, defined validations will apply whenever a user is at or past that step.
115
+
116
+ ### Customising the `current_step` and `wizard_steps` methods.
117
+ You might want to have a different attribute on your model to store the current step. That's easy:
118
+
119
+ ```
120
+ class User < ActiveRecord::Base
121
+ include Wicked::Wizard::Validations
122
+
123
+ self.current_step_method = :my_current_step_attribute #the current step will be stored in this attribute.
124
+
125
+ #other stuff in here
126
+ end
127
+ ```
128
+
129
+ Likewise, the you might want to define a different method for the wizard steps:
130
+
131
+ ```
132
+ class User < ActiveRecord::Base
133
+ include Wicked::Wizard::Validations
134
+
135
+ self.wizard_steps_method = :my_amazing_steps # User.my_amazing_steps needs to return an array of steps
136
+
137
+ #other stuff in here
138
+ end
139
+ ```
140
+
141
+ ### Validating more than one model in a wizard
142
+ You might have a relationship between models, and be collecting data for both in a wizard. That's pretty easy to support: you just have to remember to use the same list of wizard steps.
143
+
144
+ Say, for example, that your user has an `address`, you could require a house name and postcode when they get to the `contact_details` step (or after it).
145
+
146
+ ```
147
+ class Address < ActiveRecord::Base
148
+ include Wicked::Wizard::Validations
149
+
150
+ belongs_to :user
151
+
152
+ #returns the current step for the associated user
153
+ def current_step
154
+ user.current_step 
155
+ end
156
+
157
+ # returns the wizard steps for the User class
158
+ def wizard_steps
159
+ User.wizard_steps
160
+ end
161
+
162
+ # Specify validations on Address which should apply when the user is on or past
163
+ # the address_details step
164
+ def address_details_validations
165
+ house_name_or_number: {
166
+ presence: {
167
+ message: "Please give us your house name or number"
168
+ }
169
+ },
170
+ postcode: {
171
+ format: {
172
+ with: /^([A-PR-UWYZ0-9][A-HK-Y0-9][AEHMNPRTVXY0-9]?[ABEHMNPRVWXY0-9]? {1,2}[0-9][ABD-HJLN-UW-Z]{2}|GIR 0AA)$/
173
+ }
174
+ }
175
+ end
176
+ end
177
+
178
+ ```
179
+
180
+ ### Utility instance methods
181
+ There are a couple of instance methods on objects which have this mixin applied.
182
+
183
+ ```
184
+ u = User.find(123)
185
+ u.current_wizard_step # get the current step
186
+ u.previous_wizard_steps #the steps before the one the user is on
187
+ u.current_and_previous_wizard_steps #an array of steps the user has been through
188
+ ```
189
+
190
+ ### Redirecting to the right step on login
191
+ Because we're storing the current step of the user, you get the ability to allow the user to jump back to the step they were on when they log in. Very useful for big multi-page forms where the user might need to come back later.
192
+
193
+ In the controller you're using for your wizard, you need this in the `show` method:
194
+
195
+ ```
196
+ class UsersController < ApplicationController
197
+ include Wicked::Wizard
198
+
199
+ # other stuff
200
+
201
+ def show
202
+ @user = current_user
203
+
204
+ # Redirect to the user's current step - useful for logging in a second time
205
+ if @user.current_wizard_step.present? && !@user.current_and_previous_wizard_steps.include?(step)
206
+ jump_to(@user.current_wizard_step)
207
+ end
208
+ end
209
+ end
210
+ ```
211
+
212
+ #### Allowing users to go back in the process
213
+ If you redirect the user to their previously-stored step, you've just stopped them from going back in the process. So to get around that, we need to update their current step when they change it.
214
+
215
+ Note that this only allows the user to go to steps earlier than the one they're on.
216
+
217
+ ```
218
+ class UsersController < ApplicationController
219
+ include Wicked::Wizard
220
+
221
+ # other stuff
222
+
223
+ def show
224
+ @user = current_user
225
+
226
+ # Redirect to the user's current step - useful for logging in a second time
227
+ if @user.current_wizard_step.present? && !@user.current_and_previous_wizard_steps.include?(step)
228
+ jump_to(@user.current_wizard_step)
229
+ end
230
+
231
+ # if the step we're rendering is before the users last known current step, assume they've clicked their
232
+ # browser's back button, update their current_step and render that page of the wizard
233
+ if (User.wizard_steps.index(step) < User.wizard_steps.index(@user.current_wizard_step.to_sym))
234
+ @user.update_attribute(:current_step, step) # if your attribute is called something else, you'll need to amend this.
235
+ end
236
+ end
237
+ end
238
+ ```
239
+
240
+ ## Contributing
241
+
242
+ 1. Fork it ( https://github.com/errorstudio/wicked-wizard-validations/fork )
243
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
244
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
245
+ 4. Squash your commits into logical changesets.
246
+ 5. Push to the branch (`git push origin my-new-feature`)
247
+ 6. Create a new Pull Request
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,109 @@
1
+ require "wicked/wizard/validations/version"
2
+
3
+ module Wicked
4
+ module Wizard
5
+ module Validations
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ class << self
10
+ # Set up a class-level instance method to hold the name of a method to call
11
+ # to get the current_step for this model.
12
+ attr_accessor :current_step_method, :wizard_steps_method
13
+ end
14
+ end
15
+
16
+ #
17
+ #Class methods on the included model
18
+ #
19
+ module ClassMethods
20
+
21
+ # @return [Array] a list of wizard steps for the class. Calls the method specified in the class instance attribute
22
+ # `wizard_steps_method`; returns an empty [Array] otherwise.
23
+ def all_wizard_steps
24
+ meth = self.wizard_steps_method
25
+ if meth.present?
26
+ case meth.class.to_s
27
+ when "Symbol"
28
+ self.send(meth)
29
+ else
30
+ raise ArgumentError, "wizard_steps_method accepts only a symbol, which should be a class method name"
31
+ end
32
+ else
33
+ begin
34
+ wizard_steps #if no wizard_steps_method set, assume `wizard_steps`.
35
+ rescue
36
+ []
37
+ end
38
+ end
39
+
40
+ end
41
+
42
+ # get previous steps for a given step
43
+ # @param step [Symbol] the name of the step you're on now
44
+ # @return [Array] the steps prior to this one
45
+ def previous_wizard_steps(step)
46
+ #cast the incoming step to a symbol
47
+ step = step.to_sym if step.is_a?(String)
48
+ self.all_wizard_steps.slice(0,self.all_wizard_steps.index(step))
49
+ end
50
+
51
+ # This is where the meat of the work happens.
52
+ # We call this in the class, and it iterates through all the wizard steps, calling `[wizard_step_name]_validations`
53
+ # on the class. If it responds, it should return a hash which can be passed straight into a `validates()` call
54
+ def setup_validations!
55
+ # Iterate through each step in the validations hash, and call a class method called [step]_step_validations
56
+ # if it exists. For example, if step were called foo_details, the method would be called foo_details_validations
57
+ self.all_wizard_steps.each do |step|
58
+ validation_method_name = "#{step}_validations".to_sym
59
+ if self.respond_to?(validation_method_name)
60
+ #if the method responds, we're expecting a hash in the following format:
61
+ # {
62
+ # field_name: {
63
+ # presence: true
64
+ # }
65
+ # }
66
+ self.send(validation_method_name).each do |field,validations|
67
+ # validate the field, using the validations hash, but merge in a lambda which checks whether the object
68
+ # is at the step yet, or not. If it's not, the validation isn't applied.
69
+ validates field, validations.merge({if: ->{ self.current_and_previous_wizard_steps.include?(step)}})
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ #
77
+ #instance methods on the model follow
78
+ #
79
+
80
+ # Get the current wizard step by calling the instance method specified
81
+ # in the class; fall back to calling `current_step` on the instance.
82
+ def current_wizard_step
83
+ meth = self.class.current_step_method
84
+ if meth.present?
85
+ case meth.class.to_s
86
+ when "Symbol"
87
+ self.send(meth)
88
+ else
89
+ raise ArgumentError, "current_step_method accepts a symbol, which should be the name of a callable instance method"
90
+ end
91
+ else
92
+ #assume the method is called current_step() and call that
93
+ current_step
94
+ end
95
+ end
96
+
97
+ # Call the `previous_wizard_steps` class method, passing in the current step for this instance
98
+ # @return [Array] an ordered list of wizard steps which happen before the current one
99
+ def previous_wizard_steps
100
+ self.class.previous_wizard_steps(current_wizard_step.to_sym)
101
+ end
102
+
103
+ # @return [Array] an ordered list of wizard steps, up to and including this one
104
+ def current_and_previous_wizard_steps
105
+ previous_wizard_steps.push(current_wizard_step.to_sym)
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,7 @@
1
+ module Wicked
2
+ module Wizard
3
+ module Validations
4
+ VERSION = "0.0.1"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'wicked/wizard/validations/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "wicked-wizard-validations"
8
+ spec.version = Wicked::Wizard::Validations::VERSION
9
+ spec.authors = ["Ed Jones"]
10
+ spec.email = ["ed@errorstudio.co.uk"]
11
+ spec.summary = %q{A validation mixin for Wicked.}
12
+ spec.description = %q{This gem allows you to conditionally validate your models, based on where in a multi-step process the user is.}
13
+ spec.homepage = "https://github.com/errorstudio/wicked-wizard-validations"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.6"
22
+ spec.add_development_dependency "rake"
23
+
24
+ spec.add_dependency 'wicked', '~> 1.2.1'
25
+ end
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: wicked-wizard-validations
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Ed Jones
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-11-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: wicked
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 1.2.1
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 1.2.1
55
+ description: This gem allows you to conditionally validate your models, based on where
56
+ in a multi-step process the user is.
57
+ email:
58
+ - ed@errorstudio.co.uk
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - .gitignore
64
+ - Gemfile
65
+ - LICENSE.txt
66
+ - README.md
67
+ - Rakefile
68
+ - lib/wicked/wizard/validations.rb
69
+ - lib/wicked/wizard/validations/version.rb
70
+ - wicked-wizard-validations.gemspec
71
+ homepage: https://github.com/errorstudio/wicked-wizard-validations
72
+ licenses:
73
+ - MIT
74
+ metadata: {}
75
+ post_install_message:
76
+ rdoc_options: []
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ requirements: []
90
+ rubyforge_project:
91
+ rubygems_version: 2.2.2
92
+ signing_key:
93
+ specification_version: 4
94
+ summary: A validation mixin for Wicked.
95
+ test_files: []