Skip to content

Commit

Permalink
Cleaned up dynamic accessors (thanks foca), added flash.now
Browse files Browse the repository at this point in the history
  • Loading branch information
nakajima committed Feb 27, 2009
1 parent aed54d7 commit c7f7519
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 44 deletions.
58 changes: 40 additions & 18 deletions README.md
@@ -1,8 +1,6 @@
# Rack Flash

Simple flash hash implementation for Raçk apps. My implementation has slightly
different behavior than Rails in that it doesn't delete entries until they used.
I think it's pretty rad, but I'm happy to hear thoughts to the contrary.
Simple flash hash implementation for Rack apps.

Try it out here: [flash.patnakajima.net](http://flash.patnakajima.net).

Expand All @@ -12,10 +10,24 @@ Here's how to use it.

### Vanilla Rack apps

You can access flash entries via `env['rack-flash']`. You can treat it either like a regular
flash hash (via the `env['rack-flash'][:notice]` style), or you can pass the `:accessorize`
option when you call `use Rack::Flash`, and you'll be able to use the accessor style, which
generates accessors on the fly (`env['rack-flash'].notice` and the like).
You can access flash entries via `env['rack-flash']`. You can treat it either
like a regular flash hash:

env['rack-flash'][:notice] = 'You have logged out.'

Or you can pass the `:accessorize` option to declare your flash types. Each of
these will have accessors defined on the flash object:

use Rack::Flash, :accessorize => [:notice, :error]

# Set a flash entry
env['rack-flash'].notice = 'You have logged out.'

# Get a flash entry
env['rack-flash'].notice # => 'You have logged out.'

# Set a a flash entry for only the current request
env['rack-flash'].notice! 'You have logged out.'

Sample rack app:

Expand All @@ -26,7 +38,7 @@ Sample rack app:
}

set = proc { |env|
env['rack-flash'].notice = 'Hey, the flash was set! Now refresh and watch it go away.'
env['rack-flash'].notice = 'Hey, the flash was set!'
[302, {'Location' => '/'},
'You are being redirected.'
]
Expand All @@ -52,17 +64,27 @@ If you're using Sinatra, you can use the flash hash just like in Rails:
class MyApp < Sinatra::Base
use Rack::Flash

get '/' do
flash[:greeting] || "No greeting..."
post '/set-flash' do
# Set a flash entry
flash[:notice] = "Thanks for signing up!"
# Get a flash entry
flash[:notice] # => "Thanks for signing up!"
# Set a flash entry for only the current request
flash.now[:notice] = "Thanks for signing up!"
end
end

get '/greeting' do
flash[:greeting] = params[:q]
redirect '/'
end
If you've got any ideas on how to simplify access to the flash hash for vanilla
Rack apps, let me know. It still feels a bit off to me.

run!
end
## Sweeping stale entries

By default Rack::Flash has slightly different behavior than Rails in that it
doesn't delete entries until they are used. If you want entries to be cleared
even if they are not ever accessed, you can use the `:sweep` option:

use Rack::Flash, :sweep => true

If you've got any ideas on how to simplify access to the flash hash for non-Sinatra
apps, let me know. It still feels a bit off to me.
This will sweep stale flash entries, whether your not you actually use them.
42 changes: 17 additions & 25 deletions lib/rack/flash.rb
@@ -1,5 +1,3 @@
require 'metaid'

module Rack
class Flash
# Raised when the session passed to FlashHash initialize is nil. This
Expand All @@ -17,6 +15,10 @@ def initialize(store, opts={})
@opts = opts
@store = store
@store[:__FLASH__] ||= {}

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
Expand All @@ -32,6 +34,13 @@ def []=(key,val)
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)
Expand Down Expand Up @@ -60,17 +69,6 @@ def to_s
values.inspect
end

# Allow more convenient style for accessing flash entries (This isn't really
# necessary for Sinatra, since it provides the flash[:foo] hash that we're all
# used to. This is for vanilla Rack apps where it can be difficult to define
# such helpers as middleware).
def method_missing(sym, *args)
super unless @opts[:accessorize]
key = sym.to_s =~ /\w=$/ ? sym.to_s[0..-2] : sym
def_accessors(key)
send(sym, *args)
end

private

# Maintain an instance-level cache of retrieved flash entries. These
Expand All @@ -87,19 +85,13 @@ def values
end

# Generate accessor methods for the given entry key if :accessorize is true.
def def_accessors(key)
return if respond_to?(key)

meta_def(key) do |*args|
if val = args.first
self[key] = val
else
self[key]
end
end
def def_accessor(key)
raise ArgumentError.new('Invalid entry type: %s' % key) if respond_to?(key)

meta_def("#{key}=") do |val|
self[key] = val
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
Expand Down
25 changes: 24 additions & 1 deletion test/test_flash.rb
Expand Up @@ -57,9 +57,16 @@ def new_flash(entries={})
end
end

it 'allows setters with Flash.now semantics' do
flash = new_flash
flash.now[:foo] = 'bar'
flash[:foo].should.equal('bar')
new_flash[:foo].should.be.nil
end

describe 'accessorize option' do
def new_flash(entries={})
flash = Rack::Flash::FlashHash.new(@fake_session, :accessorize => true)
flash = Rack::Flash::FlashHash.new(@fake_session, :accessorize => [:foo, :fizz])
entries.each { |key,val| flash[key] = val }
flash
end
Expand All @@ -80,6 +87,22 @@ def new_flash(entries={})
flash.fizz 'buzz'
flash.fizz.should.equal('buzz')
end

it 'allows setters with Flash.now semantics' do
flash = new_flash
flash.foo! 'bar'
flash.foo.should.equal('bar')
new_flash[:foo].should.be.nil
end

it 'only defines accessors for passed entry types' do
err_explain do
flash = new_flash
proc {
flash.bliggety = 'blam'
}.should.raise(NoMethodError)
end
end
end

it 'does not provide getters by default' do
Expand Down

0 comments on commit c7f7519

Please sign in to comment.