NAME
Concierge::Sessions::Session - Individual session objects for data access and persistence
VERSION
version 0.8.5
SYNOPSIS
Session objects are created by Concierge::Sessions factory methods:
use Concierge::Sessions;
my $sessions = Concierge::Sessions->new(
backend => 'database',
storage_dir => '/var/app/sessions',
);
# Create a new session
my $result = $sessions->new_session(
user_id => 'user123',
data => { cart => [], preferences => {} },
);
my $session = $result->{session};
# Read session data
my $data_result = $session->get_data();
my $data = $data_result->{value};
# Modify session data
$data->{cart} = ['item1', 'item2'];
$data->{preferences}{theme} = 'dark';
# Write modified data back (marks session as dirty)
$session->set_data($data);
# Check if session needs saving
if ($session->is_dirty()) {
$session->save(); # Persists to backend and extends timeout
}
# Check session status
if ($session->is_valid()) {
# Session is active and not expired
}
# Access session metadata
my $id = $session->session_id();
my $created = $session->created_at();
my $expires = $session->expires_at();
DESCRIPTION
Session objects represent individual user sessions and provide methods for data access, persistence, and status checking. They are created by the Concierge::Sessions factory and returned in the result hashref.
Session objects implement an explicit persistence model with dirty flag tracking. Changes made via set_data() are only in memory until save() is called.
METHODS
Data Access Methods
get_data
Retrieves the entire session data field.
my $result = $session->get_data();
my $data = $result->{value};
Parameters:
None.
Returns:
{
success => 1,
value => $data_hashref, # Entire session data structure
}
The data field can contain any data structure that can be represented as JSON: scalars, arrays, hashes, and nested combinations thereof.
set_data
Replaces the entire session data field with new data.
my $result = $session->set_data(\%new_data);
Parameters:
\%new_data- Hashref containing the new session data. This completely replaces the existing data field.
Returns:
{
success => 1,
}
This method:
Replaces the entire data field (not a merge)
Marks the session as dirty (unsaved changes exist)
Does NOT persist to backend (call save() for that)
Example:
my $data = $session->get_data()->{value};
$data->{username} = 'alice';
$data->{items} = [1, 2, 3];
$session->set_data($data); # Data replaced, session now dirty
$session->save(); # Persist to backend
save
Persists dirty session data to the backend storage.
my $result = $session->save();
Parameters:
None.
Returns:
{
success => 1,
}
Or on error:
{
success => 0,
message => "Error description",
}
This method:
Is a no-op if the session is not dirty (returns success immediately)
Writes the entire data field to backend storage
Updates the last_updated timestamp
Extends the session expiration (sliding window)
Clears the dirty flag on success
The save() method also implements sliding window expiration. Each save() extends the session timeout from the current time, keeping active sessions alive.
For indefinite sessions (session_timeout set to 'indefinite'), save() still persists data but does not modify expiration (session never expires).
Status Check Methods
is_valid
Returns true if the session is both active and not expired.
my $valid = $session->is_valid();
This is a convenience method that combines is_active() and is_expired():
return ($session->is_active() && !$session->is_expired()) ? 1 : 0;
Returns: 1 if valid, 0 if invalid.
Use this to check if a session can be used before accessing its data.
is_active
Returns true if the session state is 'active'.
my $active = $session->is_active();
Returns: 1 if active, 0 if inactive.
Currently, all sessions are created in the 'active' state and there is no API to change the state to inactive. This method is provided for future extensibility.
is_expired
Returns true if the session has passed its expiration time.
my $expired = $session->is_expired();
Returns: 1 if expired, 0 if not expired.
For indefinite sessions (session_timeout set to 'indefinite'), this method always returns 0 (never expires).
Example:
if ($session->is_expired()) {
# Session has expired, user must re-authenticate
} else {
# Session is still valid
}
is_dirty
Returns true if the session has unsaved changes.
my $dirty = $session->is_dirty();
Returns: 1 if dirty (unsaved changes), 0 if clean.
A session becomes dirty when set_data() is called. The dirty flag is cleared when save() successfully persists the changes.
Use this to optimize performance - only save when there are actual changes:
$session->set_data($new_data);
if ($session->is_dirty()) {
$session->save();
}
Accessor Methods
session_id
Returns the unique session identifier.
my $id = $session->session_id();
Returns: 40-character lowercase hex string (cryptographically random).
This ID is generated when the session is created and remains constant for the life of the session. Use it to retrieve the session later via Concierge::Sessions->get_session($id).
created_at
Returns the session creation timestamp.
my $created = $session->created_at();
Returns: Numeric timestamp (seconds since epoch, with fractional seconds).
This timestamp is set when the session is created and never changes.
expires_at
Returns the session expiration timestamp.
my $expires = $session->expires_at();
Returns: Numeric timestamp (seconds since epoch), or the string 'indefinite' for sessions that never expire.
The expiration time is extended each time save() is called (sliding window).
For indefinite sessions, returns the literal string 'indefinite'.
last_updated
Returns the timestamp of the last save() operation.
my $updated = $session->last_updated();
Returns: Numeric timestamp (seconds since epoch, with fractional seconds), or undef if the session has never been saved.
This timestamp is updated each time save() persists data to the backend.
storage_backend
Returns the backend class name.
my $backend = $session->storage_backend();
Returns: String such as 'Concierge::Sessions::SQLite' or 'Concierge::Sessions::File'.
Useful for debugging or when you need to know which backend is storing the session data.
status
Returns the session status hashref.
my $status = $session->status();
# Returns: { state => 'active', dirty => 0 }
Returns: Hashref with keys:
state- Session state ('active' or other values)dirty- Dirty flag (1 if unsaved changes, 0 if clean)
This provides direct access to the internal status structure. For most use cases, the is_active(), is_dirty(), and is_expired() methods are more convenient.
EXPLICIT PERSISTENCE
Session objects use an explicit persistence model. Changes are NOT automatically saved:
# 1. Get current data
my $data = $session->get_data()->{value};
# 2. Modify the data
$data->{cart} = ['item1', 'item2'];
# 3. Write back (changes are in-memory only)
$session->set_data($data);
# 4. Session is now dirty (unsaved changes)
$session->is_dirty(); # Returns 1
# 5. Persist to backend (also extends timeout)
$session->save();
# 6. Session is now clean
$session->is_dirty(); # Returns 0
If you don't call save(), the changes are lost when the session object goes out of scope. No automatic saving happens on scope exit.
The save() method is optimized - it's a no-op if the session is not dirty, so it's safe to call multiple times:
$session->save(); # Not dirty, returns immediately
$session->set_data($new_data);
$session->save(); # Actually saves
SLIDING WINDOW EXPIRATION
Each call to save() extends the session timeout, creating a "sliding window" expiration:
# Session created with 1 hour timeout
my $session = $sessions->new_session(
user_id => 'user123',
session_timeout => 3600,
)->{session};
# Session expires at: time + 3600
# User is active after 30 minutes
sleep 1800;
$session->save();
# Session now expires at: current_time + 3600
This keeps active users logged in indefinitely, while inactive sessions expire naturally after the timeout period.
For indefinite sessions (session_timeout set to 'indefinite'), save() still persists data but does not change expiration (the session never expires).
OPAQUE DATA STORAGE
The session data field is opaque - the application controls the structure and content. Concierge::Sessions simply stores and retrieves whatever data you provide, without interpretation.
# Store any data structure
$session->set_data({
user_id => 'user123',
cart => [\%items],
preferences => {
theme => 'dark',
language => 'en',
},
nested => {
deeply => {
data => [1, 2, 3],
},
},
});
There are no key-value operations or JSON path queries. Use get_data() to retrieve the entire data structure, modify it as needed, then set_data() to replace it.
This design gives applications complete control over their session data structure without any constraints from the session management system.
EXAMPLES
Basic Data Access
my $session = $sessions->new_session(
user_id => 'user123',
data => { cart => [] },
)->{session};
# Add item to cart
my $data = $session->get_data()->{value};
push @{$data->{cart}}, 'item1';
$session->set_data($data);
$session->save();
Conditional Save
# Only save if data changed
my $data = $session->get_data()->{value};
$data->{last_access} = time();
$session->set_data($data);
if ($session->is_dirty()) {
$session->save();
}
Session Validation
sub require_valid_session {
my ($session) = @_;
unless ($session && $session->is_valid()) {
die "Invalid or expired session";
}
return $session;
}
Tracking Session Age
my $session = $sessions->new_session(
user_id => 'user123',
)->{session};
my $created = $session->created_at();
my $age = time() - $created;
if ($age > 7200) { # 2 hours
# Force user to re-authenticate
}
SEE ALSO
Concierge::Sessions - Factory for creating session objects
Concierge::Sessions::SQLite - SQLite backend implementation
Concierge::Sessions::File - File backend implementation
AUTHOR
Bruce Van Allen <bva@cruzio.com>
LICENSE
Artistic License 2.0