diff --git a/lib/utopia/session/lazy_hash.rb b/lib/utopia/session/lazy_hash.rb index e28d7497..f4c90887 100644 --- a/lib/utopia/session/lazy_hash.rb +++ b/lib/utopia/session/lazy_hash.rb @@ -16,10 +16,6 @@ def initialize(&block) @loader = block end - # The loaded session values, if already loaded. - # @returns [Hash | Nil] The loaded values. - attr :values - # Fetch a value by key, loading the hash if necessary. # @parameter key [Object] The key. # @returns [Object | Nil] The value. @@ -66,10 +62,20 @@ def changed? @changed end - # Load and return the underlying values. - # @returns [Hash] The loaded values. - def load! - @values ||= @loader.call + # Persist the session values if they have changed or require updating. + # @parameter timeout [Numeric | Nil] The maximum age before an update is required. + # @yields {|values, updated_at| ...} The loaded values and their update time. + # @returns [Object | Nil] The result of the block if persistence was required. + def persist(timeout = nil) + return unless needs_update?(timeout) + + values = load! + updated_at = values[:updated_at] = Time.now.utc + + result = yield(values, updated_at) + @changed = false + + return result end # Check whether the underlying values have been loaded. @@ -93,6 +99,13 @@ def needs_update?(timeout = nil) return false end + + private + + # Load and return the underlying values. + def load! + @values ||= @loader.call + end end end end diff --git a/lib/utopia/session/middleware.rb b/lib/utopia/session/middleware.rb index 90819fa6..d5be1110 100644 --- a/lib/utopia/session/middleware.rb +++ b/lib/utopia/session/middleware.rb @@ -149,14 +149,8 @@ def prepare_session(request) end def update_session(session_hash, headers) - if session_hash.needs_update?(@update_timeout) - values = session_hash.values - - values[:updated_at] = Time.now.utc - - data = encrypt(session_hash.values) - - commit(data, values[:updated_at], headers) + session_hash.persist(@update_timeout) do |values, updated_at| + commit(encrypt(values), updated_at, headers) end end diff --git a/test/utopia/session.rb b/test/utopia/session.rb index b353cd6b..449f9c65 100755 --- a/test/utopia/session.rb +++ b/test/utopia/session.rb @@ -399,4 +399,42 @@ def before expect(hash).to be(:needs_update?) end + + it "should persist changed values" do + hash = Utopia::Session::LazyHash.new do + {a: 10} + end + + hash[:a] = 20 + persisted_at = Time.now.utc + + result = hash.persist do |values, updated_at| + expect(values[:a]).to be == 20 + expect(updated_at).to be >= persisted_at + expect(values[:updated_at]).to be_equal(updated_at) + + :complete + end + + expect(result).to be == :complete + expect(hash).not.to be(:changed?) + expect(hash).not.to be(:respond_to?, :values) + expect(hash).not.to be(:respond_to?, :load!) + end + + it "should retain changes if persistence fails" do + hash = Utopia::Session::LazyHash.new do + {a: 10} + end + + hash[:a] = 20 + + expect do + hash.persist do + raise "Persistence failed!" + end + end.to raise_exception(RuntimeError, message: be == "Persistence failed!") + + expect(hash).to be(:changed?) + end end