attr_encrypted 3.0.0 → 4.0.0
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.
- checksums.yaml +5 -5
- data/.travis.yml +14 -16
- data/CHANGELOG.md +60 -14
- data/README.md +27 -9
- data/Rakefile +3 -0
- data/attr_encrypted.gemspec +6 -13
- data/checksum/attr_encrypted-3.0.0.gem.sha256 +1 -0
- data/checksum/attr_encrypted-3.0.0.gem.sha512 +1 -0
- data/checksum/attr_encrypted-3.0.1.gem.sha256 +1 -0
- data/checksum/attr_encrypted-3.0.1.gem.sha512 +1 -0
- data/checksum/attr_encrypted-3.0.2.gem.sha256 +1 -0
- data/checksum/attr_encrypted-3.0.2.gem.sha512 +1 -0
- data/checksum/attr_encrypted-3.0.3.gem.sha256 +1 -0
- data/checksum/attr_encrypted-3.0.3.gem.sha512 +1 -0
- data/checksum/attr_encrypted-3.1.0.gem.sha256 +1 -0
- data/checksum/attr_encrypted-3.1.0.gem.sha512 +1 -0
- data/lib/attr_encrypted/adapters/active_record.rb +58 -30
- data/lib/attr_encrypted/adapters/data_mapper.rb +3 -1
- data/lib/attr_encrypted/adapters/sequel.rb +3 -1
- data/lib/attr_encrypted/version.rb +3 -1
- data/lib/attr_encrypted.rb +160 -129
- data/test/active_record_test.rb +130 -104
- data/test/attr_encrypted_test.rb +113 -16
- data/test/compatibility_test.rb +21 -21
- data/test/data_mapper_test.rb +2 -0
- data/test/legacy_active_record_test.rb +9 -9
- data/test/legacy_attr_encrypted_test.rb +8 -6
- data/test/legacy_compatibility_test.rb +15 -15
- data/test/legacy_data_mapper_test.rb +2 -0
- data/test/legacy_sequel_test.rb +2 -0
- data/test/run.sh +15 -7
- data/test/sequel_test.rb +2 -0
- data/test/test_helper.rb +11 -5
- metadata +27 -50
- checksums.yaml.gz.sig +0 -0
- data/certs/saghaulor.pem +0 -21
- data.tar.gz.sig +0 -0
- metadata.gz.sig +0 -0
    
        data/lib/attr_encrypted.rb
    CHANGED
    
    | @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            require 'encryptor'
         | 
| 2 4 |  | 
| 3 5 | 
             
            # Adds attr_accessors that encrypt and decrypt an object's attributes
         | 
| @@ -8,7 +10,7 @@ module AttrEncrypted | |
| 8 10 | 
             
                base.class_eval do
         | 
| 9 11 | 
             
                  include InstanceMethods
         | 
| 10 12 | 
             
                  attr_writer :attr_encrypted_options
         | 
| 11 | 
            -
                  @attr_encrypted_options, @ | 
| 13 | 
            +
                  @attr_encrypted_options, @attr_encrypted_encrypted_attributes = {}, {}
         | 
| 12 14 | 
             
                end
         | 
| 13 15 | 
             
              end
         | 
| 14 16 |  | 
| @@ -16,90 +18,93 @@ module AttrEncrypted | |
| 16 18 | 
             
              #
         | 
| 17 19 | 
             
              # Options (any other options you specify are passed to the Encryptor's encrypt and decrypt methods)
         | 
| 18 20 | 
             
              #
         | 
| 19 | 
            -
              #   attribute: | 
| 20 | 
            -
              # | 
| 21 | 
            -
              # | 
| 22 | 
            -
              # | 
| 23 | 
            -
              # | 
| 24 | 
            -
              # | 
| 25 | 
            -
              #
         | 
| 26 | 
            -
              #   prefix: | 
| 27 | 
            -
              # | 
| 28 | 
            -
              # | 
| 29 | 
            -
              # | 
| 30 | 
            -
              # | 
| 31 | 
            -
              #
         | 
| 32 | 
            -
              #   suffix: | 
| 33 | 
            -
              # | 
| 34 | 
            -
              # | 
| 35 | 
            -
              # | 
| 36 | 
            -
              # | 
| 37 | 
            -
              #
         | 
| 38 | 
            -
              #   key: | 
| 39 | 
            -
              # | 
| 40 | 
            -
              # | 
| 41 | 
            -
              # | 
| 42 | 
            -
              # | 
| 43 | 
            -
              # | 
| 44 | 
            -
              # | 
| 45 | 
            -
              # | 
| 46 | 
            -
              #
         | 
| 47 | 
            -
              #   encode: | 
| 48 | 
            -
              # | 
| 49 | 
            -
              # | 
| 50 | 
            -
              # | 
| 51 | 
            -
              # | 
| 52 | 
            -
              # | 
| 53 | 
            -
              # | 
| 54 | 
            -
              # | 
| 55 | 
            -
              # | 
| 56 | 
            -
              #
         | 
| 57 | 
            -
              #   encode_iv: | 
| 58 | 
            -
             | 
| 59 | 
            -
              #   encode_salt: | 
| 60 | 
            -
              #
         | 
| 61 | 
            -
              #   default_encoding: | 
| 62 | 
            -
              #
         | 
| 63 | 
            -
              #   marshal: | 
| 64 | 
            -
              # | 
| 65 | 
            -
              # | 
| 66 | 
            -
              # | 
| 67 | 
            -
              #
         | 
| 68 | 
            -
              #   marshaler: | 
| 69 | 
            -
              # | 
| 70 | 
            -
              #
         | 
| 71 | 
            -
              #   dump_method: | 
| 72 | 
            -
              # | 
| 73 | 
            -
              #
         | 
| 74 | 
            -
              #   load_method: | 
| 75 | 
            -
              # | 
| 76 | 
            -
              #
         | 
| 77 | 
            -
              #   encryptor: | 
| 78 | 
            -
              # | 
| 79 | 
            -
              #
         | 
| 80 | 
            -
              #   encrypt_method: | 
| 81 | 
            -
              # | 
| 82 | 
            -
              #
         | 
| 83 | 
            -
              #   decrypt_method: | 
| 84 | 
            -
              # | 
| 85 | 
            -
              #
         | 
| 86 | 
            -
              #   if: | 
| 87 | 
            -
              # | 
| 88 | 
            -
              # | 
| 89 | 
            -
              # | 
| 90 | 
            -
              # | 
| 91 | 
            -
              #
         | 
| 92 | 
            -
              #   unless: | 
| 93 | 
            -
              # | 
| 94 | 
            -
              # | 
| 95 | 
            -
              # | 
| 96 | 
            -
              # | 
| 97 | 
            -
              #
         | 
| 98 | 
            -
              #   mode: | 
| 99 | 
            -
              # | 
| 100 | 
            -
              # | 
| 101 | 
            -
              # | 
| 102 | 
            -
              # | 
| 21 | 
            +
              #   attribute:            The name of the referenced encrypted attribute. For example
         | 
| 22 | 
            +
              #                         <tt>attr_accessor :email, attribute: :ee</tt> would generate an
         | 
| 23 | 
            +
              #                         attribute named 'ee' to store the encrypted email. This is useful when defining
         | 
| 24 | 
            +
              #                         one attribute to encrypt at a time or when the :prefix and :suffix options
         | 
| 25 | 
            +
              #                         aren't enough.
         | 
| 26 | 
            +
              #                         Defaults to nil.
         | 
| 27 | 
            +
              #
         | 
| 28 | 
            +
              #   prefix:               A prefix used to generate the name of the referenced encrypted attributes.
         | 
| 29 | 
            +
              #                         For example <tt>attr_accessor :email, prefix: 'crypted_'</tt> would
         | 
| 30 | 
            +
              #                         generate attributes named 'crypted_email' to store the encrypted
         | 
| 31 | 
            +
              #                         email and password.
         | 
| 32 | 
            +
              #                         Defaults to 'encrypted_'.
         | 
| 33 | 
            +
              #
         | 
| 34 | 
            +
              #   suffix:               A suffix used to generate the name of the referenced encrypted attributes.
         | 
| 35 | 
            +
              #                         For example <tt>attr_accessor :email, prefix: '', suffix: '_encrypted'</tt>
         | 
| 36 | 
            +
              #                         would generate attributes named 'email_encrypted' to store the
         | 
| 37 | 
            +
              #                         encrypted email.
         | 
| 38 | 
            +
              #                         Defaults to ''.
         | 
| 39 | 
            +
              #
         | 
| 40 | 
            +
              #   key:                  The encryption key. This option may not be required if
         | 
| 41 | 
            +
              #                         you're using a custom encryptor. If you pass a symbol
         | 
| 42 | 
            +
              #                         representing an instance method then the :key option
         | 
| 43 | 
            +
              #                         will be replaced with the result of the method before
         | 
| 44 | 
            +
              #                         being passed to the encryptor. Objects that respond
         | 
| 45 | 
            +
              #                         to :call are evaluated as well (including procs).
         | 
| 46 | 
            +
              #                         Any other key types will be passed directly to the encryptor.
         | 
| 47 | 
            +
              #                         Defaults to nil.
         | 
| 48 | 
            +
              #
         | 
| 49 | 
            +
              #   encode:               If set to true, attributes will be encoded as well as
         | 
| 50 | 
            +
              #                         encrypted. This is useful if you're planning on storing
         | 
| 51 | 
            +
              #                         the encrypted attributes in a database. The default
         | 
| 52 | 
            +
              #                         encoding is 'm' (base64), however this can be overwritten
         | 
| 53 | 
            +
              #                         by setting the :encode option to some other encoding
         | 
| 54 | 
            +
              #                         string instead of just 'true'. See
         | 
| 55 | 
            +
              #                         http://www.ruby-doc.org/core/classes/Array.html#M002245
         | 
| 56 | 
            +
              #                         for more encoding directives.
         | 
| 57 | 
            +
              #                         Defaults to false unless you're using it with ActiveRecord, DataMapper, or Sequel.
         | 
| 58 | 
            +
              #
         | 
| 59 | 
            +
              #   encode_iv:            Defaults to true.
         | 
| 60 | 
            +
             | 
| 61 | 
            +
              #   encode_salt:          Defaults to true.
         | 
| 62 | 
            +
              #
         | 
| 63 | 
            +
              #   default_encoding:     Defaults to 'm' (base64).
         | 
| 64 | 
            +
              #
         | 
| 65 | 
            +
              #   marshal:              If set to true, attributes will be marshaled as well
         | 
| 66 | 
            +
              #                         as encrypted. This is useful if you're planning on
         | 
| 67 | 
            +
              #                         encrypting something other than a string.
         | 
| 68 | 
            +
              #                         Defaults to false.
         | 
| 69 | 
            +
              #
         | 
| 70 | 
            +
              #   marshaler:            The object to use for marshaling.
         | 
| 71 | 
            +
              #                         Defaults to Marshal.
         | 
| 72 | 
            +
              #
         | 
| 73 | 
            +
              #   dump_method:          The dump method name to call on the <tt>:marshaler</tt> object to.
         | 
| 74 | 
            +
              #                         Defaults to 'dump'.
         | 
| 75 | 
            +
              #
         | 
| 76 | 
            +
              #   load_method:          The load method name to call on the <tt>:marshaler</tt> object.
         | 
| 77 | 
            +
              #                         Defaults to 'load'.
         | 
| 78 | 
            +
              #
         | 
| 79 | 
            +
              #   encryptor:            The object to use for encrypting.
         | 
| 80 | 
            +
              #                         Defaults to Encryptor.
         | 
| 81 | 
            +
              #
         | 
| 82 | 
            +
              #   encrypt_method:       The encrypt method name to call on the <tt>:encryptor</tt> object.
         | 
| 83 | 
            +
              #                         Defaults to 'encrypt'.
         | 
| 84 | 
            +
              #
         | 
| 85 | 
            +
              #   decrypt_method:       The decrypt method name to call on the <tt>:encryptor</tt> object.
         | 
| 86 | 
            +
              #                         Defaults to 'decrypt'.
         | 
| 87 | 
            +
              #
         | 
| 88 | 
            +
              #   if:                   Attributes are only encrypted if this option evaluates
         | 
| 89 | 
            +
              #                         to true. If you pass a symbol representing an instance
         | 
| 90 | 
            +
              #                         method then the result of the method will be evaluated.
         | 
| 91 | 
            +
              #                         Any objects that respond to <tt>:call</tt> are evaluated as well.
         | 
| 92 | 
            +
              #                         Defaults to true.
         | 
| 93 | 
            +
              #
         | 
| 94 | 
            +
              #   unless:               Attributes are only encrypted if this option evaluates
         | 
| 95 | 
            +
              #                         to false. If you pass a symbol representing an instance
         | 
| 96 | 
            +
              #                         method then the result of the method will be evaluated.
         | 
| 97 | 
            +
              #                         Any objects that respond to <tt>:call</tt> are evaluated as well.
         | 
| 98 | 
            +
              #                         Defaults to false.
         | 
| 99 | 
            +
              #
         | 
| 100 | 
            +
              #   mode:                 Selects encryption mode for attribute: choose <tt>:single_iv_and_salt</tt> for compatibility
         | 
| 101 | 
            +
              #                         with the old attr_encrypted API: the IV is derived from the encryption key by the underlying Encryptor class; salt is not used.
         | 
| 102 | 
            +
              #                         The <tt>:per_attribute_iv_and_salt</tt> mode uses a per-attribute IV and salt. The salt is used to derive a unique key per attribute.
         | 
| 103 | 
            +
              #                         A <tt>:per_attribute_iv</default> mode derives a unique IV per attribute; salt is not used.
         | 
| 104 | 
            +
              #                         Defaults to <tt>:per_attribute_iv</tt>.
         | 
| 105 | 
            +
              #
         | 
| 106 | 
            +
              #   allow_empty_value:    Attributes which have nil or empty string values will not be encrypted unless this option
         | 
| 107 | 
            +
              #                         has a truthy value.
         | 
| 103 108 | 
             
              #
         | 
| 104 109 | 
             
              # You can specify your own default options
         | 
| 105 110 | 
             
              #
         | 
| @@ -140,23 +145,26 @@ module AttrEncrypted | |
| 140 145 | 
             
                  encrypted_attribute_name = (options[:attribute] ? options[:attribute] : [options[:prefix], attribute, options[:suffix]].join).to_sym
         | 
| 141 146 |  | 
| 142 147 | 
             
                  instance_methods_as_symbols = attribute_instance_methods_as_symbols
         | 
| 143 | 
            -
                  attr_reader encrypted_attribute_name unless instance_methods_as_symbols.include?(encrypted_attribute_name)
         | 
| 144 | 
            -
                  attr_writer encrypted_attribute_name unless instance_methods_as_symbols.include?(:"#{encrypted_attribute_name}=")
         | 
| 145 148 |  | 
| 146 | 
            -
                   | 
| 147 | 
            -
             | 
| 148 | 
            -
             | 
| 149 | 
            +
                  if attribute_instance_methods_as_symbols_available?
         | 
| 150 | 
            +
                    attr_reader encrypted_attribute_name unless instance_methods_as_symbols.include?(encrypted_attribute_name)
         | 
| 151 | 
            +
                    attr_writer encrypted_attribute_name unless instance_methods_as_symbols.include?(:"#{encrypted_attribute_name}=")
         | 
| 152 | 
            +
             | 
| 153 | 
            +
                    iv_name = "#{encrypted_attribute_name}_iv".to_sym
         | 
| 154 | 
            +
                    attr_reader iv_name unless instance_methods_as_symbols.include?(iv_name)
         | 
| 155 | 
            +
                    attr_writer iv_name unless instance_methods_as_symbols.include?(:"#{iv_name}=")
         | 
| 149 156 |  | 
| 150 | 
            -
             | 
| 151 | 
            -
             | 
| 152 | 
            -
             | 
| 157 | 
            +
                    salt_name = "#{encrypted_attribute_name}_salt".to_sym
         | 
| 158 | 
            +
                    attr_reader salt_name unless instance_methods_as_symbols.include?(salt_name)
         | 
| 159 | 
            +
                    attr_writer salt_name unless instance_methods_as_symbols.include?(:"#{salt_name}=")
         | 
| 160 | 
            +
                  end
         | 
| 153 161 |  | 
| 154 162 | 
             
                  define_method(attribute) do
         | 
| 155 | 
            -
                    instance_variable_get("@#{attribute}") || instance_variable_set("@#{attribute}",  | 
| 163 | 
            +
                    instance_variable_get("@#{attribute}") || instance_variable_set("@#{attribute}", attr_encrypted_decrypt(attribute, send(encrypted_attribute_name)))
         | 
| 156 164 | 
             
                  end
         | 
| 157 165 |  | 
| 158 166 | 
             
                  define_method("#{attribute}=") do |value|
         | 
| 159 | 
            -
                    send("#{encrypted_attribute_name}=",  | 
| 167 | 
            +
                    send("#{encrypted_attribute_name}=", attr_encrypted_encrypt(attribute, value))
         | 
| 160 168 | 
             
                    instance_variable_set("@#{attribute}", value)
         | 
| 161 169 | 
             
                  end
         | 
| 162 170 |  | 
| @@ -165,7 +173,7 @@ module AttrEncrypted | |
| 165 173 | 
             
                    value.respond_to?(:empty?) ? !value.empty? : !!value
         | 
| 166 174 | 
             
                  end
         | 
| 167 175 |  | 
| 168 | 
            -
                   | 
| 176 | 
            +
                  self.attr_encrypted_encrypted_attributes[attribute.to_sym] = options.merge(attribute: encrypted_attribute_name)
         | 
| 169 177 | 
             
                end
         | 
| 170 178 | 
             
              end
         | 
| 171 179 |  | 
| @@ -197,6 +205,7 @@ module AttrEncrypted | |
| 197 205 | 
             
                  decrypt_method:    'decrypt',
         | 
| 198 206 | 
             
                  mode:              :per_attribute_iv,
         | 
| 199 207 | 
             
                  algorithm:         'aes-256-gcm',
         | 
| 208 | 
            +
                  allow_empty_value: false,
         | 
| 200 209 | 
             
                }
         | 
| 201 210 | 
             
              end
         | 
| 202 211 |  | 
| @@ -214,7 +223,7 @@ module AttrEncrypted | |
| 214 223 | 
             
              #   User.attr_encrypted?(:name)  # false
         | 
| 215 224 | 
             
              #   User.attr_encrypted?(:email) # true
         | 
| 216 225 | 
             
              def attr_encrypted?(attribute)
         | 
| 217 | 
            -
                 | 
| 226 | 
            +
                attr_encrypted_encrypted_attributes.has_key?(attribute.to_sym)
         | 
| 218 227 | 
             
              end
         | 
| 219 228 |  | 
| 220 229 | 
             
              # Decrypts a value for the attribute specified
         | 
| @@ -225,10 +234,10 @@ module AttrEncrypted | |
| 225 234 | 
             
              #     attr_encrypted :email
         | 
| 226 235 | 
             
              #   end
         | 
| 227 236 | 
             
              #
         | 
| 228 | 
            -
              #   email = User. | 
| 229 | 
            -
              def  | 
| 230 | 
            -
                options =  | 
| 231 | 
            -
                if options[:if] && !options[:unless] &&  | 
| 237 | 
            +
              #   email = User.attr_encrypted_decrypt(:email, 'SOME_ENCRYPTED_EMAIL_STRING')
         | 
| 238 | 
            +
              def attr_encrypted_decrypt(attribute, encrypted_value, options = {})
         | 
| 239 | 
            +
                options = attr_encrypted_encrypted_attributes[attribute.to_sym].merge(options)
         | 
| 240 | 
            +
                if options[:if] && !options[:unless] && not_empty?(encrypted_value)
         | 
| 232 241 | 
             
                  encrypted_value = encrypted_value.unpack(options[:encode]).first if options[:encode]
         | 
| 233 242 | 
             
                  value = options[:encryptor].send(options[:decrypt_method], options.merge!(value: encrypted_value))
         | 
| 234 243 | 
             
                  if options[:marshal]
         | 
| @@ -251,10 +260,10 @@ module AttrEncrypted | |
| 251 260 | 
             
              #     attr_encrypted :email
         | 
| 252 261 | 
             
              #   end
         | 
| 253 262 | 
             
              #
         | 
| 254 | 
            -
              #   encrypted_email = User. | 
| 255 | 
            -
              def  | 
| 256 | 
            -
                options =  | 
| 257 | 
            -
                if options[:if] && !options[:unless] &&  | 
| 263 | 
            +
              #   encrypted_email = User.attr_encrypted_encrypt(:email, 'test@example.com')
         | 
| 264 | 
            +
              def attr_encrypted_encrypt(attribute, value, options = {})
         | 
| 265 | 
            +
                options = attr_encrypted_encrypted_attributes[attribute.to_sym].merge(options)
         | 
| 266 | 
            +
                if options[:if] && !options[:unless] && (options[:allow_empty_value] || not_empty?(value))
         | 
| 258 267 | 
             
                  value = options[:marshal] ? options[:marshaler].send(options[:dump_method], value) : value.to_s
         | 
| 259 268 | 
             
                  encrypted_value = options[:encryptor].send(options[:encrypt_method], options.merge!(value: value))
         | 
| 260 269 | 
             
                  encrypted_value = [encrypted_value].pack(options[:encode]) if options[:encode]
         | 
| @@ -264,6 +273,10 @@ module AttrEncrypted | |
| 264 273 | 
             
                end
         | 
| 265 274 | 
             
              end
         | 
| 266 275 |  | 
| 276 | 
            +
              def not_empty?(value)
         | 
| 277 | 
            +
                !value.nil? && !(value.is_a?(String) && value.empty?)
         | 
| 278 | 
            +
              end
         | 
| 279 | 
            +
             | 
| 267 280 | 
             
              # Contains a hash of encrypted attributes with virtual attribute names as keys
         | 
| 268 281 | 
             
              # and their corresponding options as values
         | 
| 269 282 | 
             
              #
         | 
| @@ -273,9 +286,9 @@ module AttrEncrypted | |
| 273 286 | 
             
              #     attr_encrypted :email, key: 'my secret key'
         | 
| 274 287 | 
             
              #   end
         | 
| 275 288 | 
             
              #
         | 
| 276 | 
            -
              #   User. | 
| 277 | 
            -
              def  | 
| 278 | 
            -
                @ | 
| 289 | 
            +
              #   User.attr_encrypted_encrypted_attributes # { email: { attribute: 'encrypted_email', key: 'my secret key' } }
         | 
| 290 | 
            +
              def attr_encrypted_encrypted_attributes
         | 
| 291 | 
            +
                @attr_encrypted_encrypted_attributes ||= superclass.attr_encrypted_encrypted_attributes.dup
         | 
| 279 292 | 
             
              end
         | 
| 280 293 |  | 
| 281 294 | 
             
              # Forwards calls to :encrypt_#{attribute} or :decrypt_#{attribute} to the corresponding encrypt or decrypt method
         | 
| @@ -290,7 +303,7 @@ module AttrEncrypted | |
| 290 303 | 
             
              #   User.encrypt_email('SOME_ENCRYPTED_EMAIL_STRING')
         | 
| 291 304 | 
             
              def method_missing(method, *arguments, &block)
         | 
| 292 305 | 
             
                if method.to_s =~ /^((en|de)crypt)_(.+)$/ && attr_encrypted?($3)
         | 
| 293 | 
            -
                  send($1, $3, *arguments)
         | 
| 306 | 
            +
                  send("attr_encrypted_#{$1}", $3, *arguments)
         | 
| 294 307 | 
             
                else
         | 
| 295 308 | 
             
                  super
         | 
| 296 309 | 
             
                end
         | 
| @@ -312,9 +325,10 @@ module AttrEncrypted | |
| 312 325 | 
             
                #
         | 
| 313 326 | 
             
                #  @user = User.new('some-secret-key')
         | 
| 314 327 | 
             
                #  @user.decrypt(:email, 'SOME_ENCRYPTED_EMAIL_STRING')
         | 
| 315 | 
            -
                def  | 
| 316 | 
            -
                   | 
| 317 | 
            -
                  self.class. | 
| 328 | 
            +
                def attr_encrypted_decrypt(attribute, encrypted_value)
         | 
| 329 | 
            +
                  attr_encrypted_encrypted_attributes[attribute.to_sym][:operation] = :decrypting
         | 
| 330 | 
            +
                  attr_encrypted_encrypted_attributes[attribute.to_sym][:value_present] = self.class.not_empty?(encrypted_value)
         | 
| 331 | 
            +
                  self.class.attr_encrypted_decrypt(attribute, encrypted_value, evaluated_attr_encrypted_options_for(attribute))
         | 
| 318 332 | 
             
                end
         | 
| 319 333 |  | 
| 320 334 | 
             
                # Encrypts a value for the attribute specified using options evaluated in the current object's scope
         | 
| @@ -331,17 +345,22 @@ module AttrEncrypted | |
| 331 345 | 
             
                #  end
         | 
| 332 346 | 
             
                #
         | 
| 333 347 | 
             
                #  @user = User.new('some-secret-key')
         | 
| 334 | 
            -
                #  @user. | 
| 335 | 
            -
                def  | 
| 336 | 
            -
                   | 
| 337 | 
            -
                  self.class. | 
| 348 | 
            +
                #  @user.attr_encrypted_encrypt(:email, 'test@example.com')
         | 
| 349 | 
            +
                def attr_encrypted_encrypt(attribute, value)
         | 
| 350 | 
            +
                  attr_encrypted_encrypted_attributes[attribute.to_sym][:operation] = :encrypting
         | 
| 351 | 
            +
                  attr_encrypted_encrypted_attributes[attribute.to_sym][:value_present] = self.class.not_empty?(value)
         | 
| 352 | 
            +
                  self.class.attr_encrypted_encrypt(attribute, value, evaluated_attr_encrypted_options_for(attribute))
         | 
| 338 353 | 
             
                end
         | 
| 339 354 |  | 
| 340 355 | 
             
                # Copies the class level hash of encrypted attributes with virtual attribute names as keys
         | 
| 341 356 | 
             
                # and their corresponding options as values to the instance
         | 
| 342 357 | 
             
                #
         | 
| 343 | 
            -
                def  | 
| 344 | 
            -
                  @ | 
| 358 | 
            +
                def attr_encrypted_encrypted_attributes
         | 
| 359 | 
            +
                  @attr_encrypted_encrypted_attributes ||= begin
         | 
| 360 | 
            +
                    duplicated= {}
         | 
| 361 | 
            +
                    self.class.attr_encrypted_encrypted_attributes.map { |key, value| duplicated[key] = value.dup }
         | 
| 362 | 
            +
                    duplicated
         | 
| 363 | 
            +
                  end
         | 
| 345 364 | 
             
                end
         | 
| 346 365 |  | 
| 347 366 | 
             
                protected
         | 
| @@ -349,20 +368,28 @@ module AttrEncrypted | |
| 349 368 | 
             
                  # Returns attr_encrypted options evaluated in the current object's scope for the attribute specified
         | 
| 350 369 | 
             
                  def evaluated_attr_encrypted_options_for(attribute)
         | 
| 351 370 | 
             
                    evaluated_options = Hash.new
         | 
| 352 | 
            -
                     | 
| 353 | 
            -
                     | 
| 354 | 
            -
             | 
| 371 | 
            +
                    attributes = attr_encrypted_encrypted_attributes[attribute.to_sym]
         | 
| 372 | 
            +
                    attribute_option_value = attributes[:attribute]
         | 
| 373 | 
            +
             | 
| 374 | 
            +
                    [:if, :unless, :value_present, :allow_empty_value].each do |option|
         | 
| 375 | 
            +
                      evaluated_options[option] = evaluate_attr_encrypted_option(attributes[option])
         | 
| 355 376 | 
             
                    end
         | 
| 356 377 |  | 
| 357 378 | 
             
                    evaluated_options[:attribute] = attribute_option_value
         | 
| 358 379 |  | 
| 359 380 | 
             
                    evaluated_options.tap do |options|
         | 
| 360 | 
            -
                      unless options[: | 
| 361 | 
            -
                         | 
| 362 | 
            -
             | 
| 363 | 
            -
             | 
| 364 | 
            -
             | 
| 365 | 
            -
                         | 
| 381 | 
            +
                      if options[:if] && !options[:unless] && options[:value_present] || options[:allow_empty_value]
         | 
| 382 | 
            +
                        (attributes.keys - evaluated_options.keys).each do |option|
         | 
| 383 | 
            +
                          options[option] = evaluate_attr_encrypted_option(attributes[option])
         | 
| 384 | 
            +
                        end
         | 
| 385 | 
            +
             | 
| 386 | 
            +
                        unless options[:mode] == :single_iv_and_salt
         | 
| 387 | 
            +
                          load_iv_for_attribute(attribute, options)
         | 
| 388 | 
            +
                        end
         | 
| 389 | 
            +
             | 
| 390 | 
            +
                        if options[:mode] == :per_attribute_iv_and_salt
         | 
| 391 | 
            +
                          load_salt_for_attribute(attribute, options)
         | 
| 392 | 
            +
                        end
         | 
| 366 393 | 
             
                      end
         | 
| 367 394 | 
             
                    end
         | 
| 368 395 | 
             
                  end
         | 
| @@ -371,7 +398,7 @@ module AttrEncrypted | |
| 371 398 | 
             
                  #
         | 
| 372 399 | 
             
                  # If the option is not a symbol or proc then the original option is returned
         | 
| 373 400 | 
             
                  def evaluate_attr_encrypted_option(option)
         | 
| 374 | 
            -
                    if option.is_a?(Symbol) && respond_to?(option)
         | 
| 401 | 
            +
                    if option.is_a?(Symbol) && respond_to?(option, true)
         | 
| 375 402 | 
             
                      send(option)
         | 
| 376 403 | 
             
                    elsif option.respond_to?(:call)
         | 
| 377 404 | 
             
                      option.call(self)
         | 
| @@ -408,7 +435,7 @@ module AttrEncrypted | |
| 408 435 | 
             
                    encrypted_attribute_name = options[:attribute]
         | 
| 409 436 | 
             
                    encode_salt = options[:encode_salt]
         | 
| 410 437 | 
             
                    salt = options[:salt] || send("#{encrypted_attribute_name}_salt")
         | 
| 411 | 
            -
                    if  | 
| 438 | 
            +
                    if options[:operation] == :encrypting
         | 
| 412 439 | 
             
                      salt = SecureRandom.random_bytes
         | 
| 413 440 | 
             
                      salt = prefix_and_encode_salt(salt, encode_salt) if encode_salt
         | 
| 414 441 | 
             
                      send("#{encrypted_attribute_name}_salt=", salt)
         | 
| @@ -436,6 +463,10 @@ module AttrEncrypted | |
| 436 463 | 
             
                instance_methods.collect { |method| method.to_sym }
         | 
| 437 464 | 
             
              end
         | 
| 438 465 |  | 
| 466 | 
            +
              def attribute_instance_methods_as_symbols_available?
         | 
| 467 | 
            +
                true
         | 
| 468 | 
            +
              end
         | 
| 469 | 
            +
             | 
| 439 470 | 
             
            end
         | 
| 440 471 |  | 
| 441 472 |  |