lib/huberry/class.rb in shuber-attr_encrypted-1.0.1 vs lib/huberry/class.rb in shuber-attr_encrypted-1.0.2

- old
+ new

@@ -21,12 +21,12 @@ # would generate attributes named 'email_encrypted' and 'password_encrypted' to store the # encrypted email. Defaults to ''. # # :key => The encryption key. This option may not be required if you're using a custom encryptor. If you pass # a symbol representing an instance method then the :key option will be replaced with the result of the - # method before being passed to the encryptor. Proc objects are evaluated as well. Any other key types - # will be passed directly to the encryptor. + # method before being passed to the encryptor. Objects that respond to :call are evaluated as well (including procs). + # Any other key types will be passed directly to the encryptor. # # :encode => If set to true, attributes will be encoded as well as encrypted. This is useful if you're # planning on storing the encrypted attributes in a database. The default encoding is 'm*' (base64), # however this can be overwritten by setting the :encode option to some other encoding string instead of # just 'true'. See http://www.ruby-doc.org/core/classes/Array.html#M002245 for more encoding directives. @@ -40,11 +40,18 @@ # # :encrypt_method => The encrypt method name to call on the <tt>:encryptor</tt> object. Defaults to :encrypt. # # :decrypt_method => The decrypt method name to call on the <tt>:encryptor</tt> object. Defaults to :decrypt. # + # :if => Attributes are only encrypted if this option evaluates to true. If you pass a symbol representing an instance + # method then the result of the method will be evaluated. Any objects that respond to :call are evaluated as well. + # Defaults to true. # + # :unless => Attributes are only encrypted if this option evaluates to false. If you pass a symbol representing an instance + # method then the result of the method will be evaluated. Any objects that respond to :call are evaluated as well. + # Defaults to false. + # # You can specify your own default options # # class User # # now all attributes will be encoded and marshaled by default # attr_encrypted_options.merge!(:encode => true, :marshal => true, :some_other_option => true) @@ -74,11 +81,13 @@ :suffix => '', :encryptor => Huberry::Encryptor, :encrypt_method => :encrypt, :decrypt_method => :decrypt, :encode => false, - :marshal => false + :marshal => false, + :if => true, + :unless => false }.merge(attr_encrypted_options).merge(attrs.last.is_a?(Hash) ? attrs.pop : {}) options[:encode] = 'm*' if options[:encode] == true attrs.each do |attribute| encrypted_attribute_name = options[:attribute].nil? ? options[:prefix].to_s + attribute.to_s + options[:suffix].to_s : options[:attribute].to_s @@ -86,60 +95,73 @@ encrypted_attributes[attribute.to_s] = encrypted_attribute_name attr_accessor encrypted_attribute_name.to_sym unless instance_methods.include?(encrypted_attribute_name) define_class_method "encrypt_#{attribute}" do |value| - if value.nil? - encrypted_value = nil + if options[:if] && !options[:unless] + if value.nil? + encrypted_value = nil + else + value = Marshal.dump(value) if options[:marshal] + encrypted_value = options[:encryptor].send options[:encrypt_method], options.merge(:value => value) + encrypted_value = [encrypted_value].pack(options[:encode]) if options[:encode] + end + encrypted_value else - value = Marshal.dump(value) if options[:marshal] - encrypted_value = options[:encryptor].send options[:encrypt_method], options.merge(:value => value) - encrypted_value = [encrypted_value].pack(options[:encode]) if options[:encode] + value end - encrypted_value end define_class_method "decrypt_#{attribute}" do |encrypted_value| - if encrypted_value.nil? - decrypted_value = nil + if options[:if] && !options[:unless] + if encrypted_value.nil? + decrypted_value = nil + else + encrypted_value = encrypted_value.unpack(options[:encode]).to_s if options[:encode] + decrypted_value = options[:encryptor].send(options[:decrypt_method], options.merge(:value => encrypted_value)) + decrypted_value = Marshal.load(decrypted_value) if options[:marshal] + end + decrypted_value else - encrypted_value = encrypted_value.unpack(options[:encode]).to_s if options[:encode] - decrypted_value = options[:encryptor].send(options[:decrypt_method], options.merge(:value => encrypted_value)) - decrypted_value = Marshal.load(decrypted_value) if options[:marshal] + encrypted_value end - decrypted_value end define_method "#{attribute}" do value = instance_variable_get("@#{attribute}") encrypted_value = read_attribute(encrypted_attribute_name) - original_key = options[:key] - options[:key] = self.class.send :evaluate_attr_encrypted_key, options[:key], self + original_options = [:key, :if, :unless].inject({}) do |hash, option| + hash[option] = options[option] + options[option] = self.class.send :evaluate_attr_encrypted_option, options[option], self + hash + end value = instance_variable_set("@#{attribute}", self.class.send("decrypt_#{attribute}".to_sym, encrypted_value)) if value.nil? && !encrypted_value.nil? - options[:key] = original_key + options.merge!(original_options) value end define_method "#{attribute}=" do |value| - original_key = options[:key] - options[:key] = self.class.send :evaluate_attr_encrypted_key, options[:key], self + original_options = [:key, :if, :unless].inject({}) do |hash, option| + hash[option] = options[option] + options[option] = self.class.send :evaluate_attr_encrypted_option, options[option], self + hash + end write_attribute(encrypted_attribute_name, self.class.send("encrypt_#{attribute}".to_sym, value)) - options[:key] = original_key + options.merge!(original_options) instance_variable_set("@#{attribute}", value) end end end - # Evaluates encryption keys specified as symbols (representing instance methods) or procs - # If the key is not a symbol or proc then the original key is returned - def evaluate_attr_encrypted_key(key, object) - case key - when Symbol - object.send(key) - when Proc - key.call(object) + # Evaluates an option specified as a symbol representing an instance method or a proc + # If the option is not a symbol or proc then the original option is returned + def evaluate_attr_encrypted_option(option, object) + if option.is_a?(Symbol) && object.respond_to?(option) + object.send(option) + elsif option.respond_to?(:call) + option.call(object) else - key + option end end end end \ No newline at end of file