validates_captcha 0.9.2 → 0.9.3
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/CHANGELOG.rdoc +6 -1
- data/README.rdoc +85 -49
- data/lib/validates_captcha.rb +18 -113
- data/lib/validates_captcha/form_builder.rb +4 -4
- data/lib/validates_captcha/form_helper.rb +13 -25
- data/lib/validates_captcha/image_generator/simple.rb +7 -6
- data/lib/validates_captcha/model_validation.rb +9 -9
- data/lib/validates_captcha/provider/image.rb +244 -0
- data/lib/validates_captcha/provider/question.rb +110 -0
- data/lib/validates_captcha/reversible_encrypter/simple.rb +3 -2
- data/lib/validates_captcha/string_generator/simple.rb +3 -2
- data/lib/validates_captcha/version.rb +1 -1
- data/rails/init.rb +1 -1
- data/test/cases/controller_validation_test.rb +79 -22
- data/test/cases/{image_generator_test.rb → image_generator/simple_test.rb} +11 -10
- data/test/cases/model_validation_test.rb +131 -58
- data/test/cases/provider/image_test.rb +103 -0
- data/test/cases/provider/question_test.rb +41 -0
- data/test/cases/{reversible_encrypter_test.rb → reversible_encrypter/simple_test.rb} +3 -2
- data/test/cases/{string_generator_test.rb → string_generator/simple_test.rb} +1 -0
- data/test/cases/validates_captcha_test.rb +9 -116
- metadata +17 -14
- data/lib/validates_captcha/middleware/simple.rb +0 -108
- data/test/cases/middleware_test.rb +0 -71
data/CHANGELOG.rdoc
CHANGED
data/README.rdoc
CHANGED
@@ -1,12 +1,15 @@
|
|
1
1
|
= Validates Captcha
|
2
2
|
|
3
|
-
|
3
|
+
A captcha verification approach for Rails apps, directly integrated into
|
4
4
|
ActiveRecord's validation mechanism and providing helpers for ActionController
|
5
5
|
and ActionView.
|
6
6
|
|
7
7
|
RDoc documentation (including this README as start page) can be found at
|
8
8
|
http://m4n.github.com/validates_captcha
|
9
9
|
|
10
|
+
Validates Captcha uses question/answer captchas by default. But you can
|
11
|
+
also use the built-in image captcha provider -- or implement your own.
|
12
|
+
|
10
13
|
|
11
14
|
|
12
15
|
== Basic Usage
|
@@ -15,7 +18,7 @@ Validates Captcha extends ActiveRecord, ActionController and ActionView with
|
|
15
18
|
helper methods that make it a snap to integrate captcha verification in your
|
16
19
|
Rails application.
|
17
20
|
|
18
|
-
Step #1
|
21
|
+
<b>Step #1:</b> Extend the form of your view with the necessary captcha display
|
19
22
|
and input logic.
|
20
23
|
|
21
24
|
# app/views/comments/new.html.erb
|
@@ -32,8 +35,8 @@ and input logic.
|
|
32
35
|
<!-- now something new: -->
|
33
36
|
<p>
|
34
37
|
<%= f.label :captcha %><br />
|
35
|
-
<%= f.
|
36
|
-
<%= f.captcha_field %>
|
38
|
+
<%= f.captcha_challenge # displays the question or image %>
|
39
|
+
<%= f.captcha_field # displays the input field %>
|
37
40
|
</p>
|
38
41
|
|
39
42
|
<p>
|
@@ -41,7 +44,7 @@ and input logic.
|
|
41
44
|
</p>
|
42
45
|
<% end %>
|
43
46
|
|
44
|
-
Step #2
|
47
|
+
<b>Step #2:</b> Tell the controller that you want to validate
|
45
48
|
captchas.
|
46
49
|
|
47
50
|
class CommentsController < ApplicationController
|
@@ -57,11 +60,11 @@ captchas.
|
|
57
60
|
This activates captcha validation in every action of the controller
|
58
61
|
whenever an instance of class +Comment+ is saved.
|
59
62
|
|
60
|
-
Step #3
|
63
|
+
<b>Step #3:</b> There's no step three!
|
61
64
|
|
62
65
|
To summarize: Put the following in your view.
|
63
66
|
|
64
|
-
<%= f.
|
67
|
+
<%= f.captcha_challenge %>
|
65
68
|
<%= f.captcha_field %>
|
66
69
|
|
67
70
|
And what you see below in the corresponding controller.
|
@@ -91,48 +94,39 @@ You can customize the validated class using the +validates_captcha_of+ method.
|
|
91
94
|
end
|
92
95
|
|
93
96
|
Two kinds of errors are added to the model if captcha validation fails:
|
94
|
-
+:blank+ if no captcha
|
95
|
-
is submitted but does not
|
96
|
-
|
97
|
-
|
97
|
+
+:blank+ if no captcha solution is submitted and +:invalid+ if a captcha
|
98
|
+
solution is submitted but does not solve the captcha's challenge. You can
|
99
|
+
localize the error messages for the captcha as you usually do for the
|
100
|
+
other attributes.
|
98
101
|
|
99
102
|
models:
|
100
103
|
comment:
|
101
104
|
attributes:
|
102
|
-
|
105
|
+
captcha_solution:
|
103
106
|
blank: 'must not be empty'
|
104
107
|
invalid: 'does not match the code displayed on the image'
|
105
108
|
|
106
|
-
What if the captcha's text is unreadable
|
107
|
-
|
109
|
+
What if the image captcha's text is unreadable or a user does not know the
|
110
|
+
correct answer to the captcha question? There's also a form helper method
|
111
|
+
for captcha regeneration available. You can call it like this.
|
108
112
|
|
109
113
|
<p>
|
110
|
-
|
114
|
+
Don't know the answer? <%= f.regenerate_captcha_challenge_link %>
|
111
115
|
</p>
|
112
116
|
|
113
|
-
This generates an anchor tag that, when clicked, generates a new
|
114
|
-
|
115
|
-
new
|
117
|
+
This generates an anchor tag that, when clicked, generates a new captcha
|
118
|
+
challenge and updates the display. It makes an AJAX request to fetch a
|
119
|
+
new challenge and updates the question/image after the request is complete.
|
116
120
|
|
117
|
-
+
|
118
|
-
method. So it relies on the Prototype javascript framework to be
|
119
|
-
on the page.
|
121
|
+
+regenerate_captcha_challenge_link+ internally calls Rails' +link_to_remote+
|
122
|
+
helper method. So it relies on the Prototype javascript framework to be
|
123
|
+
available on the page.
|
120
124
|
|
121
|
-
The anchor's text defaults to '
|
125
|
+
The anchor's text defaults to 'New question' (question challenge) and
|
126
|
+
'Regenerate Captcha' (image challenge) respectively. You can set this to
|
122
127
|
a custom value by providing a +:text+ key in the options hash.
|
123
128
|
|
124
|
-
<%= f.
|
125
|
-
|
126
|
-
By default, captchas have a length of 6 characters and the text displayed
|
127
|
-
on the captcha image is created by randomly selecting characters from a
|
128
|
-
predefined alphabet constisting of visually distinguishable letters and digits.
|
129
|
-
|
130
|
-
The number of characters and the alphabet used when generating strings can
|
131
|
-
be customized. Just put the following in a Rails initializer and adjust the
|
132
|
-
values to your needs.
|
133
|
-
|
134
|
-
ValidatesCaptcha::StringGenerator::Simple.alphabet = '01'
|
135
|
-
ValidatesCaptcha::StringGenerator::Simple.length = 8
|
129
|
+
<%= f.regenerate_captcha_challenge_link :text => 'Another captcha, please' %>
|
136
130
|
|
137
131
|
Apart from controllers, you can activate captcha validation for a model
|
138
132
|
using the class level +with_captcha_validation+ method added to
|
@@ -146,28 +140,67 @@ ActiveRecord::Base.
|
|
146
140
|
This activates captcha validation on entering the block and deactivates it
|
147
141
|
on leaving the block.
|
148
142
|
|
149
|
-
Two new attribute
|
150
|
-
+
|
151
|
-
initialized to a randomly generated
|
152
|
-
|
143
|
+
Two new attribute-like methods are added to ActiveRecord: +captcha_challenge+
|
144
|
+
and +captcha_solution+. Those are made +attr_accessible+. The former is
|
145
|
+
initialized to a randomly generated captcha challenge on instantiation.
|
146
|
+
|
147
|
+
For a record to be valid, the value assigned to +captcha_solution=+ must
|
148
|
+
solve the return value of +captcha_challenge+. Within a +with_captcha_validation+
|
149
|
+
block, calling +valid?+ (as is done by +save+, +update_attributes+, etc.)
|
150
|
+
will also validate the value of +captcha_solution+ against +captcha_challenge+.
|
151
|
+
Outside +with_captcha_validation+, no captcha validation is performed.
|
152
|
+
|
153
|
+
|
154
|
+
|
155
|
+
== Question/answer challange captchas (default provider)
|
156
|
+
|
157
|
+
You can set the captcha provider to use question/answer challenges with
|
158
|
+
the code below. It is best to put this in a Rails initializer.
|
159
|
+
|
160
|
+
ValidatesCaptcha.provider = ValidatesCaptcha::Provider::Question.new # this is the default
|
161
|
+
|
162
|
+
If you want to replace the few default questions and answers, here's how
|
163
|
+
to do it.
|
153
164
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
165
|
+
ValidatesCaptcha::Provider::Question.questions_and_answers = {
|
166
|
+
"What's the opposite of bad?" => "good",
|
167
|
+
"What are the initials of the creator of Rails?" => "DHH",
|
168
|
+
"What's the sum of 3 and four?" => ["7", "seven"],
|
169
|
+
... }
|
170
|
+
|
171
|
+
|
172
|
+
|
173
|
+
== Image challenge captchas
|
174
|
+
|
175
|
+
You can set the captcha provider to use image challenges with
|
176
|
+
the code below. It is best to put this in a Rails initializer.
|
177
|
+
|
178
|
+
ValidatesCaptcha.provider = ValidatesCaptcha::Provider::Image.new
|
179
|
+
|
180
|
+
By default, image captchas have a length of 6 characters and the text displayed
|
181
|
+
on the captcha image is created by randomly selecting characters from a
|
182
|
+
predefined alphabet constisting of visually distinguishable letters and digits.
|
183
|
+
|
184
|
+
The number of characters and the alphabet used when generating strings can
|
185
|
+
be customized. Just put the following in a Rails initializer and adjust the
|
186
|
+
values to your needs.
|
187
|
+
|
188
|
+
ValidatesCaptcha::StringGenerator::Simple.alphabet = '01'
|
189
|
+
ValidatesCaptcha::StringGenerator::Simple.length = 8
|
160
190
|
|
161
191
|
|
162
192
|
|
163
193
|
== Extensibility
|
164
194
|
|
195
|
+
Don't like the built-in challenges? It's easy to extend them or to implement
|
196
|
+
your own.
|
197
|
+
|
165
198
|
Validates Captcha delegates tasks like string and image generation,
|
166
199
|
encryption/decryption of captcha codes, and responding to captcha requests
|
167
200
|
to dedicated backend classes.
|
168
201
|
|
169
|
-
Those classes can easily be replaced
|
170
|
-
you can achieve stronger encryption, can use a word list as captcha text
|
202
|
+
Those classes can easily be replaced with your custom implementations. So
|
203
|
+
you can achieve stronger encryption, can use a word list as image captcha text
|
171
204
|
generation source, or can replace the captcha image generator with one
|
172
205
|
that creates images that are harder to crack.
|
173
206
|
|
@@ -176,7 +209,10 @@ Please see the documentation of the following classes for further information.
|
|
176
209
|
* ValidatesCaptcha::StringGenerator::Simple
|
177
210
|
* ValidatesCaptcha::ReversibleEncrypter::Simple
|
178
211
|
* ValidatesCaptcha::ImageGenerator::Simple
|
179
|
-
|
212
|
+
|
213
|
+
Or you can implement a custom captcha challenge provider and assign it to
|
214
|
+
ValidatesCaptcha#provider=. See the documentation on ValidatesCaptcha::Provider
|
215
|
+
for an example.
|
180
216
|
|
181
217
|
|
182
218
|
|
@@ -185,8 +221,8 @@ Please see the documentation of the following classes for further information.
|
|
185
221
|
Using a Rack middleware to speed up the request/response cycle when fetching
|
186
222
|
captcha images, Validates Captcha requires Rails version 2.3 or greater.
|
187
223
|
|
188
|
-
The
|
189
|
-
|
224
|
+
The image captcha provider uses ImageMagick's +convert+ command to create
|
225
|
+
the captcha. So a recent and properly configured version of ImageMagick
|
190
226
|
must be installed on the system. The version used while developing was 6.4.5.
|
191
227
|
But you are not bound to ImageMagick. If you want to provide a custom image
|
192
228
|
generator, take a look at the documentation for
|
data/lib/validates_captcha.rb
CHANGED
@@ -22,17 +22,11 @@
|
|
22
22
|
#++
|
23
23
|
|
24
24
|
|
25
|
-
# This module contains the
|
26
|
-
#
|
27
|
-
# allows you to replace them with your custom implementations. For more
|
25
|
+
# This module contains the getter and setter for the captcha provider.
|
26
|
+
# This allows you to replace it with your custom implementation. For more
|
28
27
|
# information on how to bring Validates Captcha to use your own
|
29
28
|
# implementation instead of the default one, consult the documentation
|
30
|
-
# for the
|
31
|
-
#
|
32
|
-
# This module also contains convenience wrapper methods for all the
|
33
|
-
# methods provided by the configured backend classes. These wrapper
|
34
|
-
# methods form the API that is visible to the outside world and that
|
35
|
-
# all backend classes use for internal communication.
|
29
|
+
# for the default provider.
|
36
30
|
module ValidatesCaptcha
|
37
31
|
autoload :ModelValidation, 'validates_captcha/model_validation'
|
38
32
|
autoload :ControllerValidation, 'validates_captcha/controller_validation'
|
@@ -41,8 +35,9 @@ module ValidatesCaptcha
|
|
41
35
|
autoload :TestCase, 'validates_captcha/test_case'
|
42
36
|
autoload :VERSION, 'validates_captcha/version'
|
43
37
|
|
44
|
-
module
|
45
|
-
autoload :
|
38
|
+
module Provider
|
39
|
+
autoload :Question, 'validates_captcha/provider/question'
|
40
|
+
autoload :Image, 'validates_captcha/provider/image'
|
46
41
|
end
|
47
42
|
|
48
43
|
module StringGenerator
|
@@ -53,117 +48,27 @@ module ValidatesCaptcha
|
|
53
48
|
autoload :Simple, 'validates_captcha/reversible_encrypter/simple'
|
54
49
|
end
|
55
50
|
|
56
|
-
module
|
57
|
-
autoload :Simple, 'validates_captcha/
|
58
|
-
end
|
51
|
+
module ImageGenerator
|
52
|
+
autoload :Simple, 'validates_captcha/image_generator/simple'
|
53
|
+
end
|
59
54
|
|
60
|
-
@@
|
61
|
-
@@string_generator = nil
|
62
|
-
@@reversible_encrypter = nil
|
63
|
-
@@middleware = nil
|
55
|
+
@@provider = nil
|
64
56
|
|
65
57
|
class << self
|
66
|
-
# Returns
|
58
|
+
# Returns Validates Captcha's current version number.
|
67
59
|
def version
|
68
60
|
ValidatesCaptcha::VERSION::STRING
|
69
61
|
end
|
70
62
|
|
71
|
-
# Returns the current captcha
|
72
|
-
#
|
73
|
-
def
|
74
|
-
@@
|
75
|
-
end
|
76
|
-
|
77
|
-
# Sets the current captcha image generator. Used to set a custom
|
78
|
-
# image generator.
|
79
|
-
def image_generator=(generator)
|
80
|
-
@@image_generator = generator
|
81
|
-
end
|
82
|
-
|
83
|
-
# Returns the current captcha string generator. Defaults to an
|
84
|
-
# instance of the ValidatesCaptcha::StringGenerator::Simple class.
|
85
|
-
def string_generator
|
86
|
-
@@string_generator ||= StringGenerator::Simple.new
|
87
|
-
end
|
88
|
-
|
89
|
-
# Sets the current captcha string generator. Used to set a
|
90
|
-
# custom string generator.
|
91
|
-
def string_generator=(generator)
|
92
|
-
@@string_generator = generator
|
93
|
-
end
|
94
|
-
|
95
|
-
# Returns the current captcha reversible encrypter. Defaults to an
|
96
|
-
# instance of the ValidatesCaptcha::ReversibleEncrypter::Simple class.
|
97
|
-
def reversible_encrypter
|
98
|
-
@@reversible_encrypter ||= ReversibleEncrypter::Simple.new
|
99
|
-
end
|
100
|
-
|
101
|
-
# Sets the current captcha reversible encrypter. Used to set a
|
102
|
-
# custom reversible encrypter.
|
103
|
-
def reversible_encrypter=(encrypter)
|
104
|
-
@@reversible_encrypter = encrypter
|
105
|
-
end
|
106
|
-
|
107
|
-
# Returns the current captcha middleware. Defaults to the
|
108
|
-
# ValidatesCaptcha::Middleware::Simple class.
|
109
|
-
def middleware
|
110
|
-
@@middleware ||= Middleware::Simple.new
|
63
|
+
# Returns the current captcha challenge provider. Defaults to an instance of
|
64
|
+
# the ValidatesCaptcha::Provider::Question class.
|
65
|
+
def provider
|
66
|
+
@@provider ||= Provider::Question.new
|
111
67
|
end
|
112
68
|
|
113
|
-
# Sets the current captcha
|
114
|
-
|
115
|
-
|
116
|
-
@@middleware = middleware
|
117
|
-
end
|
118
|
-
|
119
|
-
# Randomly generates a string which can be used as the code
|
120
|
-
# displayed on captcha images. This method internally calls
|
121
|
-
# +string_generator.generate+.
|
122
|
-
def generate_captcha_code
|
123
|
-
string_generator.generate
|
124
|
-
end
|
125
|
-
|
126
|
-
# Returns the image data of the generated captcha image. This
|
127
|
-
# method internally calls +image_generator.generate+.
|
128
|
-
def generate_captcha_image(code)
|
129
|
-
image_generator.generate(code)
|
130
|
-
end
|
131
|
-
|
132
|
-
# Returns the image file extension of the captcha images. This
|
133
|
-
# method internally calls +image_generator.image_file_extension+.
|
134
|
-
def captcha_image_file_extension
|
135
|
-
image_generator.image_file_extension
|
136
|
-
end
|
137
|
-
|
138
|
-
# Returns the image mime type of the captcha images. This
|
139
|
-
# method internally calls +image_generator.image_mime_type+.
|
140
|
-
def captcha_image_mime_type
|
141
|
-
image_generator.image_mime_type
|
142
|
-
end
|
143
|
-
|
144
|
-
# Returns the encryption of a cleartext captcha code. This
|
145
|
-
# method internally calls +reversible_encrypter.encrypt+.
|
146
|
-
def encrypt_captcha_code(code)
|
147
|
-
reversible_encrypter.encrypt(code)
|
148
|
-
end
|
149
|
-
|
150
|
-
# Returns the decryption of an encrypted captcha code. This
|
151
|
-
# method internally calls +reversible_encrypter.decrypt+.
|
152
|
-
def decrypt_captcha_code(encrypted_code)
|
153
|
-
reversible_encrypter.decrypt(encrypted_code)
|
154
|
-
end
|
155
|
-
|
156
|
-
# Returns the captcha image path for a given encrypted code. This
|
157
|
-
# method internally calls +middleware.image_path+.
|
158
|
-
def captcha_image_path(encrypted_code)
|
159
|
-
middleware.image_path(encrypted_code)
|
160
|
-
end
|
161
|
-
|
162
|
-
# Returns the path that is used when requesting the regeneration
|
163
|
-
# of a captcha image. This method internally calls
|
164
|
-
# +middleware.regenerate_path+.
|
165
|
-
def regenerate_captcha_path
|
166
|
-
middleware.regenerate_path
|
69
|
+
# Sets the current captcha challenge provider. Used to set a custom provider.
|
70
|
+
def provider=(provider)
|
71
|
+
@@provider = provider
|
167
72
|
end
|
168
73
|
end
|
169
74
|
end
|
@@ -1,15 +1,15 @@
|
|
1
1
|
module ValidatesCaptcha
|
2
2
|
module FormBuilder #:nodoc:
|
3
|
-
def
|
4
|
-
@template.
|
3
|
+
def captcha_challenge(options = {}) #:nodoc:
|
4
|
+
@template.captcha_challenge @object_name, options.merge(:object => @object)
|
5
5
|
end
|
6
6
|
|
7
7
|
def captcha_field(options = {}) #:nodoc:
|
8
8
|
@template.captcha_field @object_name, options.merge(:object => @object)
|
9
9
|
end
|
10
10
|
|
11
|
-
def
|
12
|
-
@template.
|
11
|
+
def regenerate_captcha_challenge_link(options = {}, html_options = {}) #:nodoc:
|
12
|
+
@template.regenerate_captcha_challenge_link @object_name, options.merge(:object => @object), html_options
|
13
13
|
end
|
14
14
|
end
|
15
15
|
end
|
@@ -1,50 +1,38 @@
|
|
1
1
|
module ValidatesCaptcha
|
2
2
|
module FormHelper
|
3
|
-
# Returns
|
3
|
+
# Returns the captcha challenge.
|
4
4
|
#
|
5
|
-
# Internally calls
|
6
|
-
|
7
|
-
|
5
|
+
# Internally calls the +render_challenge+ method of ValidatesCaptcha#provider.
|
6
|
+
def captcha_challenge(object_name, options = {})
|
7
|
+
options.symbolize_keys!
|
8
|
+
|
8
9
|
object = options.delete(:object)
|
9
|
-
src = ValidatesCaptcha.captcha_image_path(object.encrypted_captcha)
|
10
10
|
sanitized_object_name = object_name.to_s.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
|
11
11
|
|
12
|
-
|
13
|
-
options[:id] = "#{sanitized_object_name}_captcha_image"
|
14
|
-
|
15
|
-
image_tag src, options
|
12
|
+
ValidatesCaptcha.provider.render_challenge sanitized_object_name, object, options
|
16
13
|
end
|
17
14
|
|
18
|
-
# Returns an input tag of the "text" type tailored for entering the captcha
|
15
|
+
# Returns an input tag of the "text" type tailored for entering the captcha solution.
|
19
16
|
#
|
20
17
|
# Internally calls Rails' #text_field helper method, passing the +object_name+ and
|
21
18
|
# +options+ arguments.
|
22
19
|
def captcha_field(object_name, options = {})
|
23
20
|
options.delete(:id)
|
24
21
|
|
25
|
-
hidden_field(object_name, :
|
22
|
+
hidden_field(object_name, :captcha_challenge, options) + text_field(object_name, :captcha_solution, options)
|
26
23
|
end
|
27
24
|
|
28
|
-
#
|
29
|
-
# the
|
25
|
+
# By default, returns an anchor tag that makes an AJAX request to fetch a new captcha challenge and updates
|
26
|
+
# the current challenge after the request is complete.
|
30
27
|
#
|
31
|
-
# Internally calls
|
32
|
-
|
33
|
-
# to be available on the web page.
|
34
|
-
#
|
35
|
-
# The anchor text defaults to 'Regenerate Captcha'. You can set this to a custom value
|
36
|
-
# providing a +:text+ key in the +options+ hash.
|
37
|
-
def regenerate_captcha_link(object_name, options = {}, html_options = {})
|
28
|
+
# Internally calls +render_regenerate_challenge_link+ method of ValidatesCaptcha#provider.
|
29
|
+
def regenerate_captcha_challenge_link(object_name, options = {}, html_options = {})
|
38
30
|
options.symbolize_keys!
|
39
31
|
|
40
32
|
object = options.delete(:object)
|
41
|
-
text = options.delete(:text) || 'Regenerate Captcha'
|
42
33
|
sanitized_object_name = object_name.to_s.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
|
43
34
|
|
44
|
-
|
45
|
-
success = "var result = request.responseJSON; $('#{sanitized_object_name}_captcha_image').src = result.captcha_image_path; $('#{sanitized_object_name}_encrypted_captcha').value = result.encrypted_captcha_code;"
|
46
|
-
|
47
|
-
link_to_remote text, options.reverse_merge(:url => url, :method => :get, :success => success), html_options
|
35
|
+
ValidatesCaptcha.provider.render_regenerate_challenge_link sanitized_object_name, object, options, html_options
|
48
36
|
end
|
49
37
|
end
|
50
38
|
end
|