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 +5 -0
 - data/README.rdoc +35 -9
 - data/lib/validates_captcha/provider/image.rb +0 -4
 - data/lib/validates_captcha/provider/static_image.rb +224 -0
 - data/lib/validates_captcha/version.rb +1 -1
 - data/lib/validates_captcha.rb +1 -0
 - data/tasks/static_image_tasks.rake +33 -0
 - data/test/cases/controller_validation_test.rb +45 -5
 - data/test/cases/model_validation_test.rb +61 -6
 - data/test/cases/provider/static_image_test.rb +148 -0
 - metadata +7 -3
 
    
        data/CHANGELOG.rdoc
    CHANGED
    
    
    
        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 
     | 
    
         
            -
             
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
      
 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  
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
      
 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  
     | 
| 
      
 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 
     | 
    
         
            -
            ==  
     | 
| 
      
 175 
     | 
    
         
            +
            == Dynamic image challenge captchas
         
     | 
| 
       174 
176 
     | 
    
         | 
| 
       175 
     | 
    
         
            -
            You can set the captcha provider to use image challenges  
     | 
| 
       176 
     | 
    
         
            -
            the code below.  
     | 
| 
      
 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 
     | 
    
         
            +
                  
         
     | 
    
        data/lib/validates_captcha.rb
    CHANGED
    
    | 
         @@ -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  
     | 
| 
      
 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 
     | 
    
         
            -
                 
     | 
| 
      
 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 
     | 
    
         
            -
                 
     | 
| 
      
 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 
     | 
    
         
            -
                 
     | 
| 
      
 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 
     | 
    
         
            -
                 
     | 
| 
      
 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  
     | 
| 
      
 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 
     | 
    
         
            -
                 
     | 
| 
      
 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 
     | 
    
         
            -
                 
     | 
| 
      
 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 
     | 
    
         
            -
                 
     | 
| 
      
 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 
     | 
    
         
            -
                 
     | 
| 
      
 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 
     | 
    
         
            -
                 
     | 
| 
      
 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. 
     | 
| 
      
 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- 
     | 
| 
      
 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. 
     | 
| 
      
 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
         
     |