Class: Tina4::SessionHandlers::FileHandler

Inherits:
Object
  • Object
show all
Defined in:
lib/tina4/session_handlers/file_handler.rb

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ FileHandler

TINA4_SESSION_PATH selects the session directory, with the SAME precedence the other three frameworks use: an explicit option wins, then the environment, then "data/sessions".

Ruby read NO env var here and hard-defaulted to /sessions, so it drifted on BOTH halves of ADR-0024: the documented variable did nothing (example/.env.example has advertised "TINA4_SESSION_PATH=data/sessions" the whole time), and the default location differed from Python (session/init.py FileSessionHandler), PHP (Session.php:128) and Node (session.ts:184) - all three of which resolve path || TINA4_SESSION_PATH || "data/sessions". Pointing the file backend at a mounted volume worked in three frameworks and was silently ignored in the fourth, which is the exact "one env var and nothing else" promise ADR-0024 makes.

The default stays RELATIVE, exactly as the other three leave it, so all four resolve the same path against the same working directory.



26
27
28
29
30
31
32
33
34
35
# File 'lib/tina4/session_handlers/file_handler.rb', line 26

def initialize(options = {})
  @dir = options[:dir] || ENV["TINA4_SESSION_PATH"] || File.join("data", "sessions")
  # TINA4_SESSION_TTL is the ONE session-lifetime variable, and it must
  # reach EVERY backend (ADR-0024). This used to be a hard-coded 86400,
  # so an operator setting a 15-minute session got 24 hours here and got
  # it right only on memcached - the one handler that read the variable.
  # 3600 is the default in Python (the master), PHP and Node.
  @ttl = (options[:ttl] || ENV["TINA4_SESSION_TTL"] || 3600).to_i
  FileUtils.mkdir_p(@dir)
end

Instance Method Details

#cleanupObject



82
83
84
# File 'lib/tina4/session_handlers/file_handler.rb', line 82

def cleanup
  sweep
end

#destroy(session_id) ⇒ Object



77
78
79
80
# File 'lib/tina4/session_handlers/file_handler.rb', line 77

def destroy(session_id)
  path = session_path(session_id)
  File.delete(path) if File.exist?(path)
end

#gc(max_age = nil) ⇒ Object

Garbage-collect expired sessions. Matches the Python interface.

Parameters:

  • max_age (Integer) (defaults to: nil)

    accepted for interface parity; expiry is absolute and already baked into the stored deadline, so it is used only for legacy records that carry no deadline of their own.



90
91
92
# File 'lib/tina4/session_handlers/file_handler.rb', line 90

def gc(max_age = nil)
  sweep(max_age)
end

#read(session_id) ⇒ Object



37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/tina4/session_handlers/file_handler.rb', line 37

def read(session_id)
  path = session_path(session_id)
  return nil unless File.exist?(path)

  stored = JSON.parse(File.read(path))
  if expired?(stored, path)
    File.delete(path)
    return nil
  end

  unwrap(stored)
rescue JSON::ParserError
  nil
end

#write(session_id, data, ttl = 0) ⇒ Object

Write session data.

The ttl is consumed HERE, at write time, and baked into an ABSOLUTE deadline stored beside the payload, so nothing at read time needs to know what the ttl was. That is what makes a per-call ttl durable: a reader with a different ttl can no longer judge someone else's record.

A ttl of 0 (or negative) means NEVER EXPIRES, matching the Python master, Node, and every mainstream implementation measured. It previously fell through to the mtime comparison, where mtime + 0 < Time.now made ttl: 0 mean "expire immediately" - the opposite meaning, and the only backend in any of the four frameworks that read it that way.

Parameters:

  • session_id (String)

    the session id

  • data (Hash)

    the payload to store

  • ttl (Integer) (defaults to: 0)

    per-call lifetime in seconds; 0 uses the handler default



68
69
70
71
72
73
74
75
# File 'lib/tina4/session_handlers/file_handler.rb', line 68

def write(session_id, data, ttl = 0)
  effective_ttl = ttl.to_i.positive? ? ttl.to_i : @ttl
  expires_at = effective_ttl.positive? ? Time.now.to_f + effective_ttl : 0.0
  File.write(
    session_path(session_id),
    JSON.generate({ "_data" => data, "_expires" => expires_at })
  )
end