validates_captcha 0.9.3 → 0.9.4

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 CHANGED
@@ -1,3 +1,8 @@
1
+ == 0.9.4 (October 7, 2009)
2
+
3
+ * Include new StaticImage provider
4
+
5
+
1
6
  == 0.9.3 (September 29, 2009)
2
7
 
3
8
  * Change API, include new Question provider, make it the default one
data/README.rdoc CHANGED
@@ -5,10 +5,12 @@ 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
- http://m4n.github.com/validates_captcha
8
+ http://m4n.github.com/validates_captcha. Validates Captcha was first
9
+ announced {here}[http://m4n.github.com/2009/09/28/introducing-validates-captcha.html].
9
10
 
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.
11
+ By default, question/answer captcha challenges are used, but you can also switch
12
+ to the built-in image captcha providers. If you stick to ValidatesCaptcha’s API,
13
+ you can even implement your own captcha challenge provider.
12
14
 
13
15
 
14
16
 
@@ -18,8 +20,8 @@ Validates Captcha extends ActiveRecord, ActionController and ActionView with
18
20
  helper methods that make it a snap to integrate captcha verification in your
19
21
  Rails application.
20
22
 
21
- <b>Step #1:</b> Extend the form of your view with the necessary captcha display
22
- and input logic.
23
+ <b>Step #1:</b> Extend your view’s form with the necessary captcha display and
24
+ input logic.
23
25
 
24
26
  # app/views/comments/new.html.erb
25
27
  <% form_for @comment do |f| %>
@@ -152,7 +154,7 @@ Outside +with_captcha_validation+, no captcha validation is performed.
152
154
 
153
155
 
154
156
 
155
- == Question/answer challange captchas (default provider)
157
+ == Question/answer challenge captchas (default provider)
156
158
 
157
159
  You can set the captcha provider to use question/answer challenges with
158
160
  the code below. It is best to put this in a Rails initializer.
@@ -170,10 +172,11 @@ to do it.
170
172
 
171
173
 
172
174
 
173
- == Image challenge captchas
175
+ == Dynamic image challenge captchas
174
176
 
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
+ You can set the captcha provider to use dynamically created image challenges
178
+ with the code below. Dynamic means that the captcha image is created on invocation.
179
+ If you want to utilize this provider, it is best to put this in a Rails initializer.
177
180
 
178
181
  ValidatesCaptcha.provider = ValidatesCaptcha::Provider::Image.new
179
182
 
@@ -190,6 +193,29 @@ values to your needs.
190
193
 
191
194
 
192
195
 
196
+ == Static image challenge captchas
197
+
198
+ You can set the captcha provider to use static image challenges with
199
+ the code below. Static means that there exist some pre-created images
200
+ in a folder accessible by the web application. If you want to utilize
201
+ this provider, it is best to put this in a Rails initializer.
202
+
203
+ ValidatesCaptcha.provider = ValidatesCaptcha::Provider::StaticImage.new
204
+
205
+ There is a Rake tast for creating the static captcha images:
206
+
207
+ rake validates_captcha:create_static_images
208
+
209
+ This will create 3 images for you. To create a different number of images,
210
+ provide a COUNT argument:
211
+
212
+ rake validates_captcha:create_static_images COUNT=50
213
+
214
+ If you want to customize the path the images get saved to (or other stuff),
215
+ please see the documentation for ValidatesCaptcha::Provider::StaticImage.
216
+
217
+
218
+
193
219
  == Extensibility
194
220
 
195
221
  Don't like the built-in challenges? It's easy to extend them or to implement
@@ -1,5 +1,3 @@
1
- require 'openssl'
2
- require 'active_support/secure_random'
3
1
  require 'action_view/helpers'
4
2
 
5
3
  module ValidatesCaptcha
@@ -80,8 +78,6 @@ module ValidatesCaptcha
80
78
  class Image
81
79
  include ActionView::Helpers
82
80
 
83
- KEY = ::ActiveSupport::SecureRandom.hex(32).freeze
84
-
85
81
  @@string_generator = nil
86
82
  @@reversible_encrypter = nil
87
83
  @@image_generator = nil
@@ -0,0 +1,224 @@
1
+ require 'digest/sha1'
2
+ require 'action_view/helpers'
3
+
4
+ module ValidatesCaptcha
5
+ module Provider
6
+ # An image captcha provider that relies on pre-created captcha images.
7
+ #
8
+ # There is a Rake tast for creating the captcha images:
9
+ #
10
+ # rake validates_captcha:create_static_images
11
+ #
12
+ # This will create 3 images in #filesystem_dir. To create a
13
+ # different number of images, provide a COUNT argument:
14
+ #
15
+ # rake validates_captcha:create_static_images COUNT=50
16
+ #
17
+ # This class contains the getters and setters for the backend classes:
18
+ # image generator and string generator. This allows you to replace them
19
+ # with your custom implementations. For more information on how to bring
20
+ # the image provider to use your own implementation instead of the default
21
+ # one, consult the documentation for the specific default class.
22
+ #
23
+ # The default captcha image generator uses ImageMagick's +convert+ command to
24
+ # create the captcha. So a recent and properly configured version of ImageMagick
25
+ # must be installed on the system. The version used while developing was 6.4.5.
26
+ # But you are not bound to ImageMagick. If you want to provide a custom image
27
+ # generator, take a look at the documentation for
28
+ # ValidatesCaptcha::ImageGenerator::Simple on how to create your own.
29
+ class StaticImage
30
+ include ActionView::Helpers
31
+
32
+ SALT = "3f(61&831_fa0712d4a?b58-eb4b8$a2%.36378f".freeze
33
+
34
+ @@string_generator = nil
35
+ @@image_generator = nil
36
+ @@filesystem_dir = nil
37
+ @@web_dir = nil
38
+ @@salt = nil
39
+
40
+ class << self
41
+ # Returns the current captcha string generator. Defaults to an
42
+ # instance of the ValidatesCaptcha::StringGenerator::Simple class.
43
+ def string_generator
44
+ @@string_generator ||= ValidatesCaptcha::StringGenerator::Simple.new
45
+ end
46
+
47
+ # Sets the current captcha string generator. Used to set a
48
+ # custom string generator.
49
+ def string_generator=(generator)
50
+ @@string_generator = generator
51
+ end
52
+
53
+ # Returns the current captcha image generator. Defaults to an
54
+ # instance of the ValidatesCaptcha::ImageGenerator::Simple class.
55
+ def image_generator
56
+ @@image_generator ||= ValidatesCaptcha::ImageGenerator::Simple.new
57
+ end
58
+
59
+ # Sets the current captcha image generator. Used to set a custom
60
+ # image generator.
61
+ def image_generator=(generator)
62
+ @@image_generator = generator
63
+ end
64
+
65
+ # Returns the current captcha image file system directory. Defaults to
66
+ # +RAILS_ROOT/public/images/captchas+.
67
+ def filesystem_dir
68
+ @@filesystem_dir ||= ::File.join(::Rails.public_path, 'images', 'captchas')
69
+ end
70
+
71
+ # Sets the current captcha image file system directory. Used to set a custom
72
+ # image directory.
73
+ def filesystem_dir=(dir)
74
+ @@filesystem_dir = dir
75
+ end
76
+
77
+ # Returns the current captcha image web directory. Defaults to
78
+ # +/images/captchas+.
79
+ def web_dir
80
+ @@web_dir ||= '/images/captchas'
81
+ end
82
+
83
+ # Sets the current captcha image web directory. Used to set a custom
84
+ # image directory.
85
+ def web_dir=(dir)
86
+ @@web_dir = dir
87
+ end
88
+
89
+ # Returns the current salt used for encryption.
90
+ def salt
91
+ @@salt ||= SALT
92
+ end
93
+
94
+ # Sets the current salt used for encryption. Used to set a custom
95
+ # salt.
96
+ def salt=(salt)
97
+ @@salt = salt
98
+ end
99
+
100
+ # Return the encryption of the +code+ using #salt.
101
+ def encrypt(code)
102
+ ::Digest::SHA1.hexdigest "#{salt}--#{code}"
103
+ end
104
+
105
+ # Creates a captcha image in the #filesystem_dir and returns
106
+ # the path to it and the code displayed on the image.
107
+ def create_image
108
+ code = string_generator.generate
109
+ encrypted_code = encrypt(code)
110
+
111
+ image_filename = "#{encrypted_code}#{image_generator.file_extension}"
112
+ image_path = File.join(filesystem_dir, image_filename)
113
+ image_bytes = image_generator.generate(code)
114
+
115
+ File.open image_path, 'w' do |os|
116
+ os.write image_bytes
117
+ end
118
+
119
+ return image_path, code
120
+ end
121
+ end
122
+
123
+ # This method is the one called by Rack.
124
+ #
125
+ # It returns HTTP status 404 if the path is not recognized. If the path is
126
+ # recognized, it returns HTTP status 200 and delivers a new challenge in
127
+ # JSON format.
128
+ #
129
+ # Please take a look at the source code if you want to learn more.
130
+ def call(env)
131
+ if env['PATH_INFO'] == regenerate_path
132
+ captcha_challenge = generate_challenge
133
+ json = { :captcha_challenge => captcha_challenge, :captcha_image_path => image_path(captcha_challenge) }.to_json
134
+
135
+ [200, { 'Content-Type' => 'application/json' }, [json]]
136
+ else
137
+ [404, { 'Content-Type' => 'text/html' }, ['Not Found']]
138
+ end
139
+ end
140
+
141
+ # Returns an array containing the paths to the available captcha images.
142
+ def images
143
+ @images ||= Dir[File.join(filesystem_dir, "*#{image_file_extension}")]
144
+ end
145
+
146
+ # Returns an array containing the available challenges (encrypted captcha codes).
147
+ def challenges
148
+ @challenges ||= images.map { |path| File.basename(path, image_file_extension) }
149
+ end
150
+
151
+ # Returns a captcha challenge.
152
+ def generate_challenge
153
+ raise("no captcha images found in #{filesystem_dir}") if challenges.empty?
154
+
155
+ challenges[rand(challenges.size)]
156
+ end
157
+
158
+ # Returns true if the captcha was solved using the given +challenge+ and +solution+,
159
+ # otherwise false.
160
+ def solved?(challenge, solution)
161
+ challenge == encrypt(solution)
162
+ end
163
+
164
+ # Returns an image tag with the source set to the url of the captcha image.
165
+ #
166
+ # Internally calls Rails' +image_tag+ helper method, passing the +options+ argument.
167
+ def render_challenge(sanitized_object_name, object, options = {})
168
+ src = image_path(object.captcha_challenge)
169
+
170
+ options[:alt] ||= 'CAPTCHA'
171
+ options[:id] = "#{sanitized_object_name}_captcha_image"
172
+
173
+ image_tag src, options
174
+ end
175
+
176
+ # Returns an anchor tag that makes an AJAX request to fetch a new captcha code and updates
177
+ # the captcha image after the request is complete.
178
+ #
179
+ # Internally calls Rails' +link_to_remote+ helper method, passing the +options+ and
180
+ # +html_options+ arguments. So it relies on the Prototype javascript framework
181
+ # to be available on the web page.
182
+ #
183
+ # The anchor text defaults to 'Regenerate Captcha'. You can set this to a custom value
184
+ # providing a +:text+ key in the +options+ hash.
185
+ def render_regenerate_challenge_link(sanitized_object_name, object, options = {}, html_options = {})
186
+ text = options.delete(:text) || 'Regenerate Captcha'
187
+ success = "var result = request.responseJSON; $('#{sanitized_object_name}_captcha_image').src = result.captcha_image_path; $('#{sanitized_object_name}_captcha_challenge').value = result.captcha_challenge; $('#{sanitized_object_name}_captcha_solution').value = '';"
188
+
189
+ link_to_remote text, options.reverse_merge(:url => regenerate_path, :method => :get, :success => success), html_options
190
+ end
191
+
192
+ private
193
+ def regenerate_path #:nodoc:
194
+ '/captchas/regenerate'
195
+ end
196
+
197
+ def image_file_extension #:nodoc:
198
+ self.class.image_generator.file_extension
199
+ end
200
+
201
+ def image_path(encrypted_code) #:nodoc:
202
+ File.join(web_dir, "#{encrypted_code}#{image_file_extension}")
203
+ end
204
+
205
+ def encrypt(code) #:nodoc:
206
+ self.class.encrypt code
207
+ end
208
+
209
+ def filesystem_dir #:nodoc:
210
+ self.class.filesystem_dir
211
+ end
212
+
213
+ def web_dir #:nodoc:
214
+ self.class.web_dir
215
+ end
216
+
217
+ # This is needed by +link_to_remote+ called in +render_regenerate_link+.
218
+ def protect_against_forgery? #:nodoc:
219
+ false
220
+ end
221
+ end
222
+ end
223
+ end
224
+
@@ -2,7 +2,7 @@ module ValidatesCaptcha #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
4
  MINOR = 9
5
- TINY = 3
5
+ TINY = 4
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -38,6 +38,7 @@ module ValidatesCaptcha
38
38
  module Provider
39
39
  autoload :Question, 'validates_captcha/provider/question'
40
40
  autoload :Image, 'validates_captcha/provider/image'
41
+ autoload :StaticImage, 'validates_captcha/provider/static_image'
41
42
  end
42
43
 
43
44
  module StringGenerator
@@ -0,0 +1,33 @@
1
+ require 'validates_captcha'
2
+
3
+ namespace :validates_captcha do
4
+ desc "Create 3 static captcha images in RAILS_ROOT/public/images/captchas/, specify a different number with COUNT=n"
5
+ task :create_static_images => [:create_static_image_dir, :clear_static_image_dir] do
6
+ count = ENV['COUNT'] ? ENV['COUNT'].to_i : 3
7
+
8
+ count.times do
9
+ path, code = ValidatesCaptcha::Provider::StaticImage.create_image
10
+ puts "Created #{path} with code #{code}"
11
+ end
12
+ end
13
+
14
+ task :create_static_image_dir => :environment do
15
+ image_dir = ValidatesCaptcha::Provider::StaticImage.filesystem_dir
16
+
17
+ unless File.exist?(image_dir)
18
+ FileUtils.mkdir_p image_dir
19
+ puts "Created directory #{image_dir}"
20
+ end
21
+ end
22
+
23
+ task :clear_static_image_dir => :environment do
24
+ image_dir = ValidatesCaptcha::Provider::StaticImage.filesystem_dir
25
+ image_files = Dir[File.join(image_dir, '*')]
26
+
27
+ if image_files.any?
28
+ FileUtils.rm image_files
29
+ puts "Cleared directory #{image_dir}"
30
+ end
31
+ end
32
+ end
33
+
@@ -76,13 +76,21 @@ end
76
76
  class ControllerValidationTest < ActionController::TestCase
77
77
  tests WidgetsController
78
78
 
79
- def with_image_provider(&block)
79
+ def with_dynamic_image_provider(&block)
80
80
  old_provider = ValidatesCaptcha.provider
81
81
  provider = ValidatesCaptcha.provider = ValidatesCaptcha::Provider::Image.new
82
82
  yield provider
83
83
  ValidatesCaptcha.provider = old_provider
84
84
  end
85
85
 
86
+ def with_static_image_provider(&block)
87
+ old_provider = ValidatesCaptcha.provider
88
+ provider = ValidatesCaptcha.provider = ValidatesCaptcha::Provider::StaticImage.new
89
+ provider.instance_variable_set "@images", ["/path/to/#{provider.send(:encrypt, 'hello')}#{provider.send(:image_file_extension)}"]
90
+ yield provider
91
+ ValidatesCaptcha.provider = old_provider
92
+ end
93
+
86
94
  def with_question_provider(&block)
87
95
  old_provider = ValidatesCaptcha.provider
88
96
  provider = ValidatesCaptcha.provider = ValidatesCaptcha::Provider::Question.new
@@ -109,7 +117,7 @@ class ControllerValidationTest < ActionController::TestCase
109
117
  end
110
118
 
111
119
  test "calling #save method of controller should not assign @invalid" do
112
- with_image_provider do |provider|
120
+ with_dynamic_image_provider do |provider|
113
121
  challenge = provider.generate_challenge
114
122
  solution = provider.send(:decrypt, challenge)
115
123
 
@@ -117,6 +125,14 @@ class ControllerValidationTest < ActionController::TestCase
117
125
  assert_nil assigns(:invalid)
118
126
  end
119
127
 
128
+ with_static_image_provider do |provider|
129
+ challenge = provider.generate_challenge
130
+ solution = 'hello'
131
+
132
+ post :save, { 'widget' => { 'captcha_challenge' => challenge, 'captcha_solution' => solution } }
133
+ assert_nil assigns(:invalid)
134
+ end
135
+
120
136
  with_question_provider do |provider|
121
137
  challenge = provider.generate_challenge
122
138
  solution = provider.send(:solve, challenge)
@@ -127,7 +143,7 @@ class ControllerValidationTest < ActionController::TestCase
127
143
  end
128
144
 
129
145
  test "calling #store method of controller should assign @invalid" do
130
- with_image_provider do |provider|
146
+ with_dynamic_image_provider do |provider|
131
147
  challenge = provider.generate_challenge
132
148
  solution = provider.send(:decrypt, challenge).reverse
133
149
 
@@ -135,6 +151,14 @@ class ControllerValidationTest < ActionController::TestCase
135
151
  assert_not_nil assigns(:invalid)
136
152
  end
137
153
 
154
+ with_static_image_provider do |provider|
155
+ challenge = provider.generate_challenge
156
+ solution = '---'
157
+
158
+ post :store, { 'widget' => { 'captcha_challenge' => challenge, 'captcha_solution' => solution } }
159
+ assert_not_nil assigns(:invalid)
160
+ end
161
+
138
162
  with_question_provider do |provider|
139
163
  challenge = provider.generate_challenge
140
164
  solution = '---'
@@ -145,7 +169,7 @@ class ControllerValidationTest < ActionController::TestCase
145
169
  end
146
170
 
147
171
  test "calling #persist method of controller should assign @invalid" do
148
- with_image_provider do |provider|
172
+ with_dynamic_image_provider do |provider|
149
173
  challenge = provider.generate_challenge
150
174
  solution = provider.send(:decrypt, challenge).reverse
151
175
 
@@ -153,6 +177,14 @@ class ControllerValidationTest < ActionController::TestCase
153
177
  assert_not_nil assigns(:invalid)
154
178
  end
155
179
 
180
+ with_static_image_provider do |provider|
181
+ challenge = provider.generate_challenge
182
+ solution = '---'
183
+
184
+ post :persist, { 'widget' => { 'captcha_challenge' => challenge, 'captcha_solution' => solution } }
185
+ assert_not_nil assigns(:invalid)
186
+ end
187
+
156
188
  with_question_provider do |provider|
157
189
  challenge = provider.generate_challenge
158
190
  solution = '---'
@@ -163,7 +195,7 @@ class ControllerValidationTest < ActionController::TestCase
163
195
  end
164
196
 
165
197
  test "calling #bingo method of controller should not assign @invalid" do
166
- with_image_provider do |provider|
198
+ with_dynamic_image_provider do |provider|
167
199
  challenge = provider.generate_challenge
168
200
  solution = provider.send(:decrypt, challenge).reverse
169
201
 
@@ -171,6 +203,14 @@ class ControllerValidationTest < ActionController::TestCase
171
203
  assert_nil assigns(:invalid)
172
204
  end
173
205
 
206
+ with_static_image_provider do |provider|
207
+ challenge = provider.generate_challenge
208
+ solution = '---'
209
+
210
+ post :bingo, { 'widget' => { 'captcha_challenge' => challenge, 'captcha_solution' => solution } }
211
+ assert_nil assigns(:invalid)
212
+ end
213
+
174
214
  with_question_provider do |provider|
175
215
  challenge = provider.generate_challenge
176
216
  solution = '---'
@@ -1,13 +1,21 @@
1
1
  require 'test_helper'
2
2
 
3
3
  class ModelValidationTest < ValidatesCaptcha::TestCase
4
- def with_image_provider(&block)
4
+ def with_dynamic_image_provider(&block)
5
5
  old_provider = ValidatesCaptcha.provider
6
6
  provider = ValidatesCaptcha.provider = ValidatesCaptcha::Provider::Image.new
7
7
  yield provider
8
8
  ValidatesCaptcha.provider = old_provider
9
9
  end
10
10
 
11
+ def with_static_image_provider(&block)
12
+ old_provider = ValidatesCaptcha.provider
13
+ provider = ValidatesCaptcha.provider = ValidatesCaptcha::Provider::StaticImage.new
14
+ provider.instance_variable_set "@images", ["/path/to/#{provider.send(:encrypt, 'hello')}#{provider.send(:image_file_extension)}"]
15
+ yield provider
16
+ ValidatesCaptcha.provider = old_provider
17
+ end
18
+
11
19
  def with_question_provider(&block)
12
20
  old_provider = ValidatesCaptcha.provider
13
21
  provider = ValidatesCaptcha.provider = ValidatesCaptcha::Provider::Question.new
@@ -76,13 +84,20 @@ class ModelValidationTest < ValidatesCaptcha::TestCase
76
84
  end
77
85
 
78
86
  test "not within a #with_captcha_validation block, calling valid? should return true if an invalid captcha_solution is set" do
79
- with_image_provider do |provider|
87
+ with_dynamic_image_provider do |provider|
80
88
  widget = Widget.new
81
89
  widget.captcha_solution = provider.send(:decrypt, widget.captcha_challenge).reverse
82
90
 
83
91
  assert widget.valid?
84
92
  end
85
93
 
94
+ with_static_image_provider do |provider|
95
+ widget = Widget.new
96
+ widget.captcha_solution = '---'
97
+
98
+ assert widget.valid?
99
+ end
100
+
86
101
  with_question_provider do |provider|
87
102
  widget = Widget.new
88
103
  widget.captcha_solution = '---'
@@ -92,13 +107,20 @@ class ModelValidationTest < ValidatesCaptcha::TestCase
92
107
  end
93
108
 
94
109
  test "not within a #with_captcha_validation block, calling valid? should return true if a valid captcha_solution is set" do
95
- with_image_provider do |provider|
110
+ with_dynamic_image_provider do |provider|
96
111
  widget = Widget.new
97
112
  widget.captcha_solution = provider.send(:decrypt, widget.captcha_challenge)
98
113
 
99
114
  assert widget.valid?
100
115
  end
101
116
 
117
+ with_static_image_provider do |provider|
118
+ widget = Widget.new
119
+ widget.captcha_solution = 'hello'
120
+
121
+ assert widget.valid?
122
+ end
123
+
102
124
  with_question_provider do |provider|
103
125
  widget = Widget.new
104
126
  widget.captcha_solution = provider.send(:solve, widget.captcha_challenge)
@@ -130,7 +152,7 @@ class ModelValidationTest < ValidatesCaptcha::TestCase
130
152
  end
131
153
 
132
154
  test "within a #with_captcha_validation block, calling valid? should return false if an invalid captcha_solution is set" do
133
- with_image_provider do |provider|
155
+ with_dynamic_image_provider do |provider|
134
156
  Widget.with_captcha_validation do
135
157
  widget = Widget.new
136
158
  widget.captcha_solution = provider.send(:decrypt, widget.captcha_challenge).reverse
@@ -141,6 +163,17 @@ class ModelValidationTest < ValidatesCaptcha::TestCase
141
163
  end
142
164
  end
143
165
 
166
+ with_static_image_provider do |provider|
167
+ Widget.with_captcha_validation do
168
+ widget = Widget.new
169
+ widget.captcha_solution = '---'
170
+
171
+ assert !widget.valid?
172
+ assert_equal 1, Array.wrap(widget.errors[:captcha_solution]).size
173
+ assert Array.wrap(widget.errors[:captcha_solution]).first.include?('invalid')
174
+ end
175
+ end
176
+
144
177
  with_question_provider do |provider|
145
178
  Widget.with_captcha_validation do
146
179
  widget = Widget.new
@@ -154,7 +187,7 @@ class ModelValidationTest < ValidatesCaptcha::TestCase
154
187
  end
155
188
 
156
189
  test "within a #with_captcha_validation block, calling valid? should return true if a valid captcha_solution is set" do
157
- with_image_provider do |provider|
190
+ with_dynamic_image_provider do |provider|
158
191
  Widget.with_captcha_validation do
159
192
  widget = Widget.new
160
193
  widget.captcha_solution = provider.send(:decrypt, widget.captcha_challenge)
@@ -163,6 +196,15 @@ class ModelValidationTest < ValidatesCaptcha::TestCase
163
196
  end
164
197
  end
165
198
 
199
+ with_static_image_provider do |provider|
200
+ Widget.with_captcha_validation do
201
+ widget = Widget.new
202
+ widget.captcha_solution = 'hello'
203
+
204
+ assert widget.valid?
205
+ end
206
+ end
207
+
166
208
  with_question_provider do |provider|
167
209
  Widget.with_captcha_validation do
168
210
  widget = Widget.new
@@ -174,7 +216,7 @@ class ModelValidationTest < ValidatesCaptcha::TestCase
174
216
  end
175
217
 
176
218
  test "with #with_captcha_validation block, calling valid? before and after the block should return true if valid? returned false within block" do
177
- with_image_provider do |provider|
219
+ with_dynamic_image_provider do |provider|
178
220
  widget = Widget.new
179
221
  widget.captcha_solution = provider.send(:decrypt, widget.captcha_challenge).reverse
180
222
 
@@ -187,6 +229,19 @@ class ModelValidationTest < ValidatesCaptcha::TestCase
187
229
  assert widget.valid?
188
230
  end
189
231
 
232
+ with_static_image_provider do |provider|
233
+ widget = Widget.new
234
+ widget.captcha_solution = '---'
235
+
236
+ assert widget.valid?
237
+
238
+ Widget.with_captcha_validation do
239
+ assert !widget.valid?
240
+ end
241
+
242
+ assert widget.valid?
243
+ end
244
+
190
245
  with_question_provider do |provider|
191
246
  widget = Widget.new
192
247
  widget.captcha_solution = '---'
@@ -0,0 +1,148 @@
1
+ require 'test_helper'
2
+
3
+ STATIC_IMAGE = ValidatesCaptcha::Provider::StaticImage
4
+
5
+ unless defined?(::Rails)
6
+ module Rails
7
+ def self.public_path
8
+ 'public'
9
+ end
10
+ end
11
+ end
12
+
13
+ class StaticImageTest < ValidatesCaptcha::TestCase
14
+ test "defines a class level #string_generator method" do
15
+ assert_respond_to STATIC_IMAGE, :string_generator
16
+ end
17
+
18
+ test "defines a class level #string_generator= method" do
19
+ assert_respond_to STATIC_IMAGE, :string_generator=
20
+ end
21
+
22
+ test "#string_generator method's return value should equal the value set using the #string_generator= method" do
23
+ old_string_generator = STATIC_IMAGE.string_generator
24
+
25
+ STATIC_IMAGE.string_generator = 'abc'
26
+ assert_equal 'abc', STATIC_IMAGE.string_generator
27
+
28
+ STATIC_IMAGE.string_generator = old_string_generator
29
+ end
30
+
31
+ test "defines a class level #image_generator method" do
32
+ assert_respond_to STATIC_IMAGE, :image_generator
33
+ end
34
+
35
+ test "defines a class level #image_generator= method" do
36
+ assert_respond_to STATIC_IMAGE, :image_generator=
37
+ end
38
+
39
+ test "#image_generator method's return value should equal the value set using the #image_generator= method" do
40
+ old_image_generator = STATIC_IMAGE.image_generator
41
+
42
+ STATIC_IMAGE.image_generator = 'abc'
43
+ assert_equal 'abc', STATIC_IMAGE.image_generator
44
+
45
+ STATIC_IMAGE.image_generator = old_image_generator
46
+ end
47
+
48
+ test "defines a class level #salt method" do
49
+ assert_respond_to STATIC_IMAGE, :salt
50
+ end
51
+
52
+ test "defines a class level #salt= method" do
53
+ assert_respond_to STATIC_IMAGE, :salt=
54
+ end
55
+
56
+ test "#salt method's return value should equal the value set using the #salt= method" do
57
+ old_salt = STATIC_IMAGE.salt
58
+
59
+ STATIC_IMAGE.salt = 'abc'
60
+ assert_equal 'abc', STATIC_IMAGE.salt
61
+
62
+ STATIC_IMAGE.salt = old_salt
63
+ end
64
+
65
+ test "defines a class level #filesystem_dir method" do
66
+ assert_respond_to STATIC_IMAGE, :filesystem_dir
67
+ end
68
+
69
+ test "defines a class level #filesystem_dir= method" do
70
+ assert_respond_to STATIC_IMAGE, :filesystem_dir=
71
+ end
72
+
73
+ test "#filesystem_dir method's return value should equal the value set using the #filesystem_dir= method" do
74
+ old_filesystem_dir = STATIC_IMAGE.filesystem_dir
75
+
76
+ STATIC_IMAGE.filesystem_dir = 'abc'
77
+ assert_equal 'abc', STATIC_IMAGE.filesystem_dir
78
+
79
+ STATIC_IMAGE.filesystem_dir = old_filesystem_dir
80
+ end
81
+
82
+ test "defines a class level #web_dir method" do
83
+ assert_respond_to STATIC_IMAGE, :web_dir
84
+ end
85
+
86
+ test "defines a class level #web_dir= method" do
87
+ assert_respond_to STATIC_IMAGE, :web_dir=
88
+ end
89
+
90
+ test "#web_dir method's return value should equal the value set using the #web_dir= method" do
91
+ old_web_dir = STATIC_IMAGE.web_dir
92
+
93
+ STATIC_IMAGE.web_dir = 'abc'
94
+ assert_equal 'abc', STATIC_IMAGE.web_dir
95
+
96
+ STATIC_IMAGE.web_dir = old_web_dir
97
+ end
98
+
99
+ test "calling #call with unrecognized path should have response status 404" do
100
+ result = STATIC_IMAGE.new.call 'PATH_INFO' => '/unrecognized'
101
+
102
+ assert_equal 404, result.first
103
+ end
104
+
105
+ test "calling #call with regenerate path should have response status 200" do
106
+ si = STATIC_IMAGE.new
107
+ si.instance_variable_set "@challenges", ['abc']
108
+
109
+ result = si.call 'PATH_INFO' => si.send(:regenerate_path)
110
+
111
+ assert_equal 200, result.first
112
+ end
113
+
114
+ test "calling #call with regenerate path should have content type response header set to application/json" do
115
+ si = STATIC_IMAGE.new
116
+ si.instance_variable_set "@challenges", ['abc']
117
+
118
+ result = si.call 'PATH_INFO' => si.send(:regenerate_path)
119
+
120
+ assert result.second.key?('Content-Type')
121
+ assert_equal 'application/json', result.second['Content-Type']
122
+ end
123
+
124
+ test "calling #generate_challenge should raise runtime error if no images are available" do
125
+ si = STATIC_IMAGE.new
126
+ si.instance_variable_set "@images", []
127
+
128
+ assert_raises RuntimeError do
129
+ si.generate_challenge
130
+ end
131
+ end
132
+
133
+ test "calling #generate_challenge should not raise runtime error if an images is available" do
134
+ si = STATIC_IMAGE.new
135
+ si.instance_variable_set "@images", ['/path/to/an/image.gif']
136
+
137
+ assert_nothing_raised do
138
+ si.generate_challenge
139
+ end
140
+ end
141
+
142
+ test "calling #generate_challenge should return the image file basename without extension" do
143
+ si = STATIC_IMAGE.new
144
+ si.instance_variable_set "@images", ["/path/to/an/captcha-image#{si.send(:image_file_extension)}"]
145
+
146
+ assert_equal 'captcha-image', si.generate_challenge
147
+ end
148
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: validates_captcha
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.3
4
+ version: 0.9.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Andert
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-09-29 00:00:00 +02:00
12
+ date: 2009-10-07 00:00:00 +02:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -55,16 +55,19 @@ files:
55
55
  - lib/validates_captcha/model_validation.rb
56
56
  - lib/validates_captcha/provider/image.rb
57
57
  - lib/validates_captcha/provider/question.rb
58
+ - lib/validates_captcha/provider/static_image.rb
58
59
  - lib/validates_captcha/reversible_encrypter/simple.rb
59
60
  - lib/validates_captcha/string_generator/simple.rb
60
61
  - lib/validates_captcha/test_case.rb
61
62
  - lib/validates_captcha/version.rb
62
63
  - rails/init.rb
64
+ - tasks/static_image_tasks.rake
63
65
  - test/cases/controller_validation_test.rb
64
66
  - test/cases/image_generator/simple_test.rb
65
67
  - test/cases/model_validation_test.rb
66
68
  - test/cases/provider/image_test.rb
67
69
  - test/cases/provider/question_test.rb
70
+ - test/cases/provider/static_image_test.rb
68
71
  - test/cases/reversible_encrypter/simple_test.rb
69
72
  - test/cases/string_generator/simple_test.rb
70
73
  - test/cases/validates_captcha_test.rb
@@ -76,7 +79,7 @@ licenses: []
76
79
  post_install_message:
77
80
  rdoc_options:
78
81
  - --title
79
- - Validates Captcha 0.9.3
82
+ - Validates Captcha 0.9.4
80
83
  - --main
81
84
  - README.rdoc
82
85
  - --line-numbers
@@ -110,6 +113,7 @@ test_files:
110
113
  - test/cases/model_validation_test.rb
111
114
  - test/cases/provider/image_test.rb
112
115
  - test/cases/provider/question_test.rb
116
+ - test/cases/provider/static_image_test.rb
113
117
  - test/cases/reversible_encrypter/simple_test.rb
114
118
  - test/cases/string_generator/simple_test.rb
115
119
  - test/cases/validates_captcha_test.rb