lib/lockbox/model.rb in lockbox-0.3.4 vs lib/lockbox/model.rb in lockbox-0.3.5
- old
+ new
@@ -9,20 +9,22 @@
# options[:type] = :datetime
# when JSON
# options[:type] = :json
# when Hash
# options[:type] = :hash
+ # when Array
+ # options[:type] = :array
# when String
# options[:type] = :string
# when Integer
# options[:type] = :integer
# when Float
# options[:type] = :float
# end
custom_type = options[:type].respond_to?(:serialize) && options[:type].respond_to?(:deserialize)
- raise ArgumentError, "Unknown type: #{options[:type]}" unless custom_type || [nil, :string, :boolean, :date, :datetime, :time, :integer, :float, :binary, :json, :hash].include?(options[:type])
+ raise ArgumentError, "Unknown type: #{options[:type]}" unless custom_type || [nil, :string, :boolean, :date, :datetime, :time, :integer, :float, :binary, :json, :hash, :array].include?(options[:type])
activerecord = defined?(ActiveRecord::Base) && self < ActiveRecord::Base
raise ArgumentError, "Type not supported yet with Mongoid" if options[:type] && !activerecord
attributes.each do |name|
@@ -110,11 +112,11 @@
# 2. existing virtual attribute
# 3. default to string (which can later be overridden)
if options[:type]
attribute_type =
case options[:type]
- when :json, :hash
+ when :json, :hash, :array
:string
when :integer
ActiveModel::Type::Integer.new(limit: 8)
else
options[:type]
@@ -122,10 +124,11 @@
attribute name, attribute_type
serialize name, JSON if options[:type] == :json
serialize name, Hash if options[:type] == :hash
+ serialize name, Array if options[:type] == :array
elsif !attributes_to_define_after_schema_loads.key?(name.to_s)
# when migrating it's best to specify the type directly
# however, we can try to use the original type if its already defined
if attributes_to_define_after_schema_loads.key?(original_name.to_s)
attribute name, attributes_to_define_after_schema_loads[original_name.to_s].first
@@ -221,22 +224,26 @@
private :"lockbox_direct_#{name}="
define_method(name) do
message = super()
- unless message
+ # possibly keep track of decrypted attributes directly in the future
+ # Hash serializer returns {} when nil, Array serializer returns [] when nil
+ # check for this explicitly as a layer of safety
+ if message.nil? || ((message == {} || message == []) && activerecord && @attributes[name.to_s].value_before_type_cast.nil?)
ciphertext = send(encrypted_attribute)
message = self.class.send(decrypt_method_name, ciphertext, context: self)
if activerecord
- # set previous attribute on first decrypt
- if @attributes[name.to_s]
- @attributes[name.to_s].instance_variable_set("@value_before_type_cast", message)
- end
+ # set previous attribute so changes populate correctly
+ # it's fine if this is set on future decryptions (as is the case when message is nil)
+ # as only the first value is loaded into changes
+ @attributes[name.to_s].instance_variable_set("@value_before_type_cast", message)
# cache
- if respond_to?(:_write_attribute, true)
- _write_attribute(name, message) if !@attributes.frozen?
+ # decrypt method does type casting
+ if respond_to?(:write_attribute_without_type_cast, true)
+ write_attribute_without_type_cast(name, message) if !@attributes.frozen?
else
raw_write_attribute(name, message) if !@attributes.frozen?
end
else
instance_variable_set("@#{name}", message)