Skip to content

Commit

Permalink
Rails 5.2 support (and dropping 4.1 support).
Browse files Browse the repository at this point in the history
All the instance variable wrangling has been kept for compatibility’s sake (given they’re in the specs).

Instead of hand-crafting dirty support, this commit makes use of Rails’ own approach, though it does differ slightly between versions.
  • Loading branch information
pat committed Nov 5, 2018
1 parent 9edcc81 commit 169f021
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 34 deletions.
8 changes: 4 additions & 4 deletions Appraisals
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
appraise "rails-4.1" do
gem "rails", "~> 4.1.0"
end if RUBY_VERSION.to_f < 2.4

appraise "rails-4.2" do
gem "rails", "~> 4.2.0"
end
Expand All @@ -13,3 +9,7 @@ end
appraise "rails-5.1" do
gem "rails", "~> 5.1.0"
end

appraise "rails-5.2" do
gem "rails", "~> 5.2.0"
end
42 changes: 16 additions & 26 deletions lib/vault/encrypted_model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,13 @@ def vault_attribute(attribute, options = {})
serializer.define_singleton_method(:decode, &options[:decode])
end

self.attribute attribute.to_s, ActiveRecord::Type::Value.new,
:default => nil

# Getter
define_method("#{attribute}") do
self.__vault_load_attributes! unless @__vault_loaded
instance_variable_get("@#{attribute}")
super()
end

# Setter
Expand All @@ -75,34 +78,15 @@ def vault_attribute(attribute, options = {})

attribute_will_change!("#{attribute}")
instance_variable_set("@#{attribute}", value)
super value

# Return the value to be consistent with other AR methods.
value
end

# Checker
define_method("#{attribute}?") do
self.__vault_load_attributes! unless @__vault_loaded
instance_variable_get("@#{attribute}").present?
end

# Dirty method
define_method("#{attribute}_change") do
changes["#{attribute}"]
end

# Dirty method
define_method("#{attribute}_changed?") do
changed.include?("#{attribute}")
end

# Dirty method
define_method("#{attribute}_was") do
if changes["#{attribute}"]
changes["#{attribute}"][0]
else
public_send("#{attribute}")
end
public_send(attribute).present?
end

# Make a note of this attribute so we can use it in the future (maybe).
Expand Down Expand Up @@ -199,7 +183,7 @@ def __vault_load_attribute!(attribute, options)

# If the user provided a value for the attribute, do not try to load
# it from Vault
if instance_variable_get("@#{attribute}")
if attributes[attribute.to_s]
return
end

Expand All @@ -213,6 +197,7 @@ def __vault_load_attribute!(attribute, options)

# Write the virtual attribute with the plaintext value
instance_variable_set("@#{attribute}", plaintext)
@attributes.write_from_database attribute.to_s, plaintext
end

# Encrypt all the attributes using Vault and set the encrypted values back
Expand Down Expand Up @@ -247,12 +232,16 @@ def __vault_persist_attribute!(attribute, options)

# Only persist changed attributes to minimize requests - this helps
# minimize the number of requests to Vault.
if !changed.include?("#{attribute}")
return
if ActiveRecord::VERSION::STRING.to_f >= 5.2
return unless previous_changes_include?(attribute)
elsif ActiveRecord::VERSION::STRING.to_f >= 5.1
return unless saved_change_to_attribute?(attribute.to_s)
else
return unless attribute_changed?(attribute)
end

# Get the current value of the plaintext attribute
plaintext = instance_variable_get("@#{attribute}")
plaintext = attributes[attribute.to_s]

# Apply the serialize to the plaintext value, if one exists
if serializer
Expand All @@ -279,6 +268,7 @@ def reload(*)
# from Vault
self.class.__vault_attributes.each do |attribute, _|
self.instance_variable_set("@#{attribute}", nil)
@attributes.write_from_database attribute.to_s, nil
end

self.__vault_initialize_attributes!
Expand Down
7 changes: 3 additions & 4 deletions spec/unit/encrypted_model_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,9 @@
end

it "defines dirty attribute methods" do
klass.vault_attribute(:foo)
expect(klass.instance_methods).to include(:foo_change)
expect(klass.instance_methods).to include(:foo_changed?)
expect(klass.instance_methods).to include(:foo_was)
expect(Person.new).to respond_to(:ssn_change)
expect(Person.new).to respond_to(:ssn_changed?)
expect(Person.new).to respond_to(:ssn_was)
end
end
end

0 comments on commit 169f021

Please sign in to comment.