Skip to content

Commit 05aa55a

Browse files
authored
Add flash functionality to session (#112)
1 parent f924c3d commit 05aa55a

File tree

3 files changed

+150
-0
lines changed

3 files changed

+150
-0
lines changed

spec/flash_spec.cr

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
require "./spec_helper"
2+
3+
describe "Flash" do
4+
before_each do
5+
Kemal::Session.config.engine = Kemal::Session::MemoryEngine.new
6+
Kemal::Session.config.secret = "kemal_rocks"
7+
end
8+
9+
describe "#[]=" do
10+
it "can set a flash value" do
11+
context = create_context(SESSION_ID)
12+
session = context.session
13+
flash = context.flash
14+
flash["notice"] = "welcome"
15+
session.string?("#{Kemal::Session::Flash::FLASH_PREFIX}notice").should eq("welcome")
16+
end
17+
end
18+
19+
describe "#[]?" do
20+
it "returns nil when flash key does not exist" do
21+
context = create_context(SESSION_ID)
22+
flash = context.flash
23+
flash["notice"]?.should be_nil
24+
end
25+
26+
it "returns the value and deletes it" do
27+
context = create_context(SESSION_ID)
28+
session = context.session
29+
flash = context.flash
30+
flash["notice"] = "welcome"
31+
flash["notice"]?.should eq("welcome")
32+
# Second access should return nil (deleted after first read)
33+
flash["notice"]?.should be_nil
34+
end
35+
36+
it "removes the flash from session storage after reading" do
37+
context = create_context(SESSION_ID)
38+
session = context.session
39+
flash = context.flash
40+
flash["notice"] = "welcome"
41+
flash["notice"]?
42+
session.string?("#{Kemal::Session::Flash::FLASH_PREFIX}notice").should be_nil
43+
end
44+
end
45+
46+
describe "#[]" do
47+
it "returns the value when it exists" do
48+
context = create_context(SESSION_ID)
49+
flash = context.flash
50+
flash["notice"] = "welcome"
51+
flash["notice"].should eq("welcome")
52+
end
53+
54+
it "raises KeyError when flash key does not exist" do
55+
context = create_context(SESSION_ID)
56+
flash = context.flash
57+
expect_raises(KeyError, /Flash key not found: notice/) do
58+
flash["notice"]
59+
end
60+
end
61+
62+
it "raises KeyError on second access (already consumed)" do
63+
context = create_context(SESSION_ID)
64+
flash = context.flash
65+
flash["notice"] = "welcome"
66+
flash["notice"].should eq("welcome")
67+
expect_raises(KeyError, /Flash key not found: notice/) do
68+
flash["notice"]
69+
end
70+
end
71+
end
72+
73+
describe "multiple flash values" do
74+
it "can handle multiple flash keys" do
75+
context = create_context(SESSION_ID)
76+
flash = context.flash
77+
flash["notice"] = "Success!"
78+
flash["error"] = "Something went wrong"
79+
flash["warning"] = "Be careful"
80+
81+
flash["notice"]?.should eq("Success!")
82+
flash["error"]?.should eq("Something went wrong")
83+
flash["warning"]?.should eq("Be careful")
84+
85+
# All should be consumed now
86+
flash["notice"]?.should be_nil
87+
flash["error"]?.should be_nil
88+
flash["warning"]?.should be_nil
89+
end
90+
end
91+
92+
describe "flash across requests" do
93+
it "persists flash to next request via session" do
94+
# First request: set flash
95+
context1 = create_context(SESSION_ID)
96+
flash1 = context1.flash
97+
flash1["notice"] = "Welcome!"
98+
99+
# Second request: read flash (same session - using Session.get to avoid re-creating)
100+
session2 = Kemal::Session.get(SESSION_ID)
101+
session2.should_not be_nil
102+
if session2
103+
flash2 = Kemal::Session::Flash.new(session2)
104+
flash2["notice"]?.should eq("Welcome!")
105+
106+
# Third request: flash should be gone (already consumed)
107+
session3 = Kemal::Session.get(SESSION_ID)
108+
session3.should_not be_nil
109+
if session3
110+
flash3 = Kemal::Session::Flash.new(session3)
111+
flash3["notice"]?.should be_nil
112+
end
113+
end
114+
end
115+
end
116+
end

src/kemal-session/ext/context.cr

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,15 @@
55
# end
66
class HTTP::Server::Context
77
property! session : Kemal::Session
8+
property! flash : Kemal::Session::Flash
89

910
def session
1011
@session ||= Kemal::Session.new(self)
1112
@session.not_nil!
1213
end
14+
15+
def flash
16+
@flash ||= Kemal::Session::Flash.new(session)
17+
@flash.not_nil!
18+
end
1319
end

src/kemal-session/flash.cr

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
module Kemal
2+
class Session
3+
class Flash
4+
FLASH_PREFIX = "_flash_"
5+
6+
def initialize(@session : Session)
7+
end
8+
9+
# env.flash["notice"] = "welcome"
10+
def []=(key : String, value : String)
11+
@session.string("#{FLASH_PREFIX}#{key}", value)
12+
end
13+
14+
# env.flash["notice"]? - returns value and marks for deletion
15+
def []?(key : String) : String?
16+
full_key = "#{FLASH_PREFIX}#{key}"
17+
value = @session.string?(full_key)
18+
@session.delete_string(full_key) if value
19+
value
20+
end
21+
22+
# env.flash["notice"] - raises if not found
23+
def [](key : String) : String
24+
self[key]? || raise KeyError.new("Flash key not found: #{key}")
25+
end
26+
end
27+
end
28+
end

0 commit comments

Comments
 (0)