From b0db5ff208c65202e00388455d7058d6cdbfa2c2 Mon Sep 17 00:00:00 2001 From: Pat Allan Date: Mon, 5 Nov 2018 17:37:06 +1100 Subject: [PATCH] Rails 5.2 support (and dropping 4.1 support). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- Appraisals | 8 +++--- lib/vault/encrypted_model.rb | 42 ++++++++++++------------------- spec/unit/encrypted_model_spec.rb | 7 +++--- 3 files changed, 23 insertions(+), 34 deletions(-) diff --git a/Appraisals b/Appraisals index 40267be3..609041a8 100644 --- a/Appraisals +++ b/Appraisals @@ -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 @@ -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 diff --git a/lib/vault/encrypted_model.rb b/lib/vault/encrypted_model.rb index f57cc548..f212c0ae 100644 --- a/lib/vault/encrypted_model.rb +++ b/lib/vault/encrypted_model.rb @@ -63,10 +63,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 @@ -79,6 +82,7 @@ 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 @@ -86,27 +90,7 @@ def vault_attribute(attribute, options = {}) # 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). @@ -212,7 +196,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 @@ -232,6 +216,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 @@ -267,12 +252,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.gem_version >= Gem::Version.new("5.2") + return unless previous_changes_include?(attribute) + elsif ActiveRecord.gem_version >= Gem::Version.new("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 @@ -319,6 +308,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! diff --git a/spec/unit/encrypted_model_spec.rb b/spec/unit/encrypted_model_spec.rb index f3f1f3d8..ff326ff2 100644 --- a/spec/unit/encrypted_model_spec.rb +++ b/spec/unit/encrypted_model_spec.rb @@ -42,10 +42,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