Created
September 27, 2017 15:04
-
-
Save blimmer/20b4e376bce851df1c06af0f529a071a to your computer and use it in GitHub Desktop.
Revisions
-
Ben Limmer created this gist
Sep 27, 2017 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,80 @@ # This class exists to make the caching of Foo objects more efficient. # Marshaling full activerecord objects is expensive, especially in Rails 5 # because of this bug: https://github.com/rails/rails/issues/30680 # This simple ruby class selects a very small subset of parameters needed # for common operations on these objects. It also acts like Foo, by # passing attributes through to an in-memory Foo model. class FooForCaching attr_accessor :attributes # This is a list of attributes we want to save in cache ATTRIBUTES_CACHED = %i( id bar baz ).freeze # These are methods that we've ensured we have the cached attributes to properly # calculate. SAFE_PASSTHROUGH_METHODS = %i( my_method_1 my_method_2 ).concat(ATTRIBUTES_CACHED).freeze # Constructor # @param [Hash] attributes the attributes to cache. attributes not provided will be interpreted to be `nil` def initialize(attributes) @attributes = attributes.symbolize_keys.slice(*ATTRIBUTES_CACHED) end # Don't create the in-memory activerecord model to access the simple id # attribute. def id attributes[:id] end # Pass undefined attribute methods to a memoized, in-memory Foo object # to allow running methods on an ActiveRecord Foo object. def method_missing(method, *args) unless SAFE_PASSTHROUGH_METHODS.include?(method) log_unsafelisted_method_passthrough(method) end if respond_to_missing?(method) ar_model_with_attrs.public_send(method, *args) else super end end # See https://rubocop.readthedocs.io/en/latest/cops_style/#stylemethodmissing def respond_to_missing?(method, *args) ar_model_with_attrs.public_methods.include?(method) || super(method, args) end # Do not accidentally cache the in-memory ActiveRecord object if it was created # before the Marshal dump was called. Caching that object would re-introduce # the problem that this class intends to solve. def marshal_dump @attributes end # Set the attributes instance variable when this object is unmarshalled from # cache. def marshal_load(attributes) @attributes = attributes end private def ar_model_with_attrs @ar_model = Foo.new(@attributes).tap(&:readonly!) end # exposed for testing def log_unsafelisted_method_passthrough(method) Rails.logger.warn( "[FooForCaching] Method #{method} called. This method will be delegated, but it's not explicitly marked as safe." ) end end