aboutsummaryrefslogtreecommitdiff
path: root/lib/rack-flash.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rack-flash.rb')
-rw-r--r--lib/rack-flash.rb171
1 files changed, 171 insertions, 0 deletions
diff --git a/lib/rack-flash.rb b/lib/rack-flash.rb
new file mode 100644
index 0000000..bc95b21
--- /dev/null
+++ b/lib/rack-flash.rb
@@ -0,0 +1,171 @@
+#
+# borrowed from http://github.com/nakajima/rack-flash - thanks!
+#
+
+require 'rack/builder'
+
+module Rack
+ class Builder
+ attr :ins
+ def use(middleware, *args, &block)
+ middleware.instance_variable_set "@rack_builder", self
+ def middleware.rack_builder
+ @rack_builder
+ end
+ @ins << lambda { |app|
+ middleware.new(app, *args, &block)
+ }
+ end
+
+ def run(app)
+ klass = app.class
+ klass.instance_variable_set "@rack_builder", self
+ def klass.rack_builder
+ @rack_builder
+ end
+ @ins << app #lambda { |nothing| app }
+ end
+
+ def leaf_app
+ ins.last
+ end
+ end
+end
+
+module Rack
+ class Flash
+ # Raised when the session passed to FlashHash initialize is nil. This
+ # is usually an indicator that session middleware is not in use.
+ class SessionUnavailable < StandardError; end
+
+ # Implements bracket accessors for storing and retrieving flash entries.
+ class FlashHash
+ attr_reader :flagged
+
+ def initialize(store, opts={})
+ raise Rack::Flash::SessionUnavailable \
+ .new('Rack::Flash depends on session middleware.') unless store
+
+ @opts = opts
+ @store = store
+
+ if accessors = @opts[:accessorize]
+ accessors.each { |opt| def_accessor(opt) }
+ end
+ end
+
+ # Remove an entry from the session and return its value. Cache result in
+ # the instance cache.
+ def [](key)
+ key = key.to_sym
+ cache[key] ||= values.delete(key)
+ end
+
+ # Store the entry in the session, updating the instance cache as well.
+ def []=(key,val)
+ key = key.to_sym
+ cache[key] = values[key] = val
+ end
+
+ # Store a flash entry for only the current request, swept regardless of
+ # whether or not it was actually accessed. Useful for AJAX requests, where
+ # you want a flash message, even though you're response isn't redirecting.
+ def now
+ cache
+ end
+
+ # Checks for the presence of a flash entry without retrieving or removing
+ # it from the cache or store.
+ def has?(key)
+ [cache, values].any? { |store| store.keys.include?(key.to_sym) }
+ end
+ alias_method :include?, :has?
+
+ # Mark existing entries to allow for sweeping.
+ def flag!
+ @flagged = values.keys
+ end
+
+ # Remove flagged entries from flash session, clear flagged list.
+ def sweep!
+ Array(flagged).each { |key| values.delete(key) }
+ flagged.clear
+ end
+
+ # Hide the underlying :__FLASH__ session key and only expose values stored
+ # in the flash.
+ def inspect
+ '#<FlashHash @values=%s @cache=%s>' % [values.inspect, cache.inspect]
+ end
+
+ # Human readable for logging.
+ def to_s
+ values.inspect
+ end
+
+ private
+
+ # Maintain an instance-level cache of retrieved flash entries. These
+ # entries will have been removed from the session, but are still available
+ # through the cache.
+ def cache
+ @cache ||= {}
+ end
+
+ # Helper to access flash entries from :__FLASH__ session value. This key
+ # is used to prevent collisions with other user-defined session values.
+ def values
+ @store[:__FLASH__] ||= {}
+ end
+
+ # Generate accessor methods for the given entry key if :accessorize is true.
+ def def_accessor(key)
+ raise ArgumentError.new('Invalid entry type: %s' % key) if respond_to?(key)
+
+ class << self; self end.class_eval do
+ define_method(key) { |*args| val = args.first; val ? (self[key]=val) : self[key] }
+ define_method("#{key}=") { |val| self[key] = val }
+ define_method("#{key}!") { |val| cache[key] = val }
+ end
+ end
+ end
+
+ # -------------------------------------------------------------------------
+ # - Rack Middleware implementation
+
+ def initialize(app, opts={})
+ if klass = app_class(app, opts)
+ klass.class_eval do
+ def flash; env['x-rack.flash'] end
+ end
+ end
+
+ @app, @opts = app, opts
+ end
+
+ def call(env)
+ env['x-rack.flash'] ||= Rack::Flash::FlashHash.new(env['rack.session'], @opts)
+
+ if @opts[:sweep]
+ env['x-rack.flash'].flag!
+ end
+
+ res = @app.call(env)
+
+ if @opts[:sweep]
+ env['x-rack.flash'].sweep!
+ end
+
+ res
+ end
+
+ private
+
+ def app_class(app, opts)
+ return nil if opts.has_key?(:helper) and not opts[:helper]
+ opts[:flash_app_class] ||
+ defined?(Sinatra::Base) && Sinatra::Base ||
+ self.class.rack_builder.leaf_app.class
+ end
+ end
+end