tina4ruby 3.11.13 → 3.11.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +80 -80
  3. data/LICENSE.txt +21 -21
  4. data/README.md +137 -137
  5. data/exe/tina4ruby +5 -5
  6. data/lib/tina4/ai.rb +696 -696
  7. data/lib/tina4/api.rb +189 -189
  8. data/lib/tina4/auth.rb +305 -305
  9. data/lib/tina4/auto_crud.rb +244 -244
  10. data/lib/tina4/cache.rb +154 -154
  11. data/lib/tina4/cli.rb +1449 -1449
  12. data/lib/tina4/constants.rb +46 -46
  13. data/lib/tina4/container.rb +74 -74
  14. data/lib/tina4/cors.rb +74 -74
  15. data/lib/tina4/crud.rb +692 -692
  16. data/lib/tina4/database/sqlite3_adapter.rb +165 -165
  17. data/lib/tina4/database.rb +625 -625
  18. data/lib/tina4/database_result.rb +208 -208
  19. data/lib/tina4/debug.rb +8 -8
  20. data/lib/tina4/dev.rb +14 -14
  21. data/lib/tina4/dev_admin.rb +935 -935
  22. data/lib/tina4/dev_mailbox.rb +191 -191
  23. data/lib/tina4/drivers/firebird_driver.rb +124 -110
  24. data/lib/tina4/drivers/mongodb_driver.rb +561 -561
  25. data/lib/tina4/drivers/mssql_driver.rb +112 -112
  26. data/lib/tina4/drivers/mysql_driver.rb +90 -90
  27. data/lib/tina4/drivers/odbc_driver.rb +191 -191
  28. data/lib/tina4/drivers/postgres_driver.rb +116 -106
  29. data/lib/tina4/drivers/sqlite_driver.rb +122 -122
  30. data/lib/tina4/env.rb +95 -95
  31. data/lib/tina4/error_overlay.rb +252 -252
  32. data/lib/tina4/events.rb +109 -109
  33. data/lib/tina4/field_types.rb +154 -154
  34. data/lib/tina4/frond.rb +2025 -2025
  35. data/lib/tina4/gallery/auth/meta.json +1 -1
  36. data/lib/tina4/gallery/auth/src/routes/api/gallery_auth.rb +114 -114
  37. data/lib/tina4/gallery/database/meta.json +1 -1
  38. data/lib/tina4/gallery/database/src/routes/api/gallery_db.rb +43 -43
  39. data/lib/tina4/gallery/error-overlay/meta.json +1 -1
  40. data/lib/tina4/gallery/error-overlay/src/routes/api/gallery_crash.rb +17 -17
  41. data/lib/tina4/gallery/orm/meta.json +1 -1
  42. data/lib/tina4/gallery/orm/src/routes/api/gallery_products.rb +16 -16
  43. data/lib/tina4/gallery/queue/meta.json +1 -1
  44. data/lib/tina4/gallery/queue/src/routes/api/gallery_queue.rb +325 -325
  45. data/lib/tina4/gallery/rest-api/meta.json +1 -1
  46. data/lib/tina4/gallery/rest-api/src/routes/api/gallery_hello.rb +14 -14
  47. data/lib/tina4/gallery/templates/meta.json +1 -1
  48. data/lib/tina4/gallery/templates/src/routes/gallery_page.rb +12 -12
  49. data/lib/tina4/gallery/templates/src/templates/gallery_page.twig +257 -257
  50. data/lib/tina4/graphql.rb +966 -966
  51. data/lib/tina4/health.rb +39 -39
  52. data/lib/tina4/html_element.rb +170 -170
  53. data/lib/tina4/job.rb +80 -80
  54. data/lib/tina4/localization.rb +168 -168
  55. data/lib/tina4/log.rb +203 -203
  56. data/lib/tina4/mcp.rb +696 -696
  57. data/lib/tina4/messenger.rb +587 -587
  58. data/lib/tina4/metrics.rb +793 -793
  59. data/lib/tina4/middleware.rb +445 -445
  60. data/lib/tina4/migration.rb +451 -451
  61. data/lib/tina4/orm.rb +790 -790
  62. data/lib/tina4/public/css/tina4.css +2463 -2463
  63. data/lib/tina4/public/css/tina4.min.css +1 -1
  64. data/lib/tina4/public/images/logo.svg +5 -5
  65. data/lib/tina4/public/js/frond.min.js +2 -2
  66. data/lib/tina4/public/js/tina4-dev-admin.js +565 -565
  67. data/lib/tina4/public/js/tina4-dev-admin.min.js +480 -480
  68. data/lib/tina4/public/js/tina4.min.js +92 -92
  69. data/lib/tina4/public/js/tina4js.min.js +48 -48
  70. data/lib/tina4/public/swagger/index.html +90 -90
  71. data/lib/tina4/public/swagger/oauth2-redirect.html +63 -63
  72. data/lib/tina4/query_builder.rb +380 -380
  73. data/lib/tina4/queue.rb +366 -366
  74. data/lib/tina4/queue_backends/kafka_backend.rb +80 -80
  75. data/lib/tina4/queue_backends/lite_backend.rb +298 -298
  76. data/lib/tina4/queue_backends/mongo_backend.rb +126 -126
  77. data/lib/tina4/queue_backends/rabbitmq_backend.rb +73 -73
  78. data/lib/tina4/rack_app.rb +817 -817
  79. data/lib/tina4/rate_limiter.rb +130 -130
  80. data/lib/tina4/request.rb +268 -255
  81. data/lib/tina4/response.rb +346 -346
  82. data/lib/tina4/response_cache.rb +551 -551
  83. data/lib/tina4/router.rb +406 -406
  84. data/lib/tina4/scss/tina4css/_alerts.scss +34 -34
  85. data/lib/tina4/scss/tina4css/_badges.scss +22 -22
  86. data/lib/tina4/scss/tina4css/_buttons.scss +69 -69
  87. data/lib/tina4/scss/tina4css/_cards.scss +49 -49
  88. data/lib/tina4/scss/tina4css/_forms.scss +156 -156
  89. data/lib/tina4/scss/tina4css/_grid.scss +81 -81
  90. data/lib/tina4/scss/tina4css/_modals.scss +84 -84
  91. data/lib/tina4/scss/tina4css/_nav.scss +149 -149
  92. data/lib/tina4/scss/tina4css/_reset.scss +94 -94
  93. data/lib/tina4/scss/tina4css/_tables.scss +54 -54
  94. data/lib/tina4/scss/tina4css/_typography.scss +55 -55
  95. data/lib/tina4/scss/tina4css/_utilities.scss +197 -197
  96. data/lib/tina4/scss/tina4css/_variables.scss +117 -117
  97. data/lib/tina4/scss/tina4css/base.scss +1 -1
  98. data/lib/tina4/scss/tina4css/colors.scss +48 -48
  99. data/lib/tina4/scss/tina4css/tina4.scss +17 -17
  100. data/lib/tina4/scss_compiler.rb +178 -178
  101. data/lib/tina4/seeder.rb +567 -567
  102. data/lib/tina4/service_runner.rb +303 -303
  103. data/lib/tina4/session.rb +297 -297
  104. data/lib/tina4/session_handlers/database_handler.rb +72 -72
  105. data/lib/tina4/session_handlers/file_handler.rb +67 -67
  106. data/lib/tina4/session_handlers/mongo_handler.rb +49 -49
  107. data/lib/tina4/session_handlers/redis_handler.rb +43 -43
  108. data/lib/tina4/session_handlers/valkey_handler.rb +43 -43
  109. data/lib/tina4/shutdown.rb +84 -84
  110. data/lib/tina4/sql_translation.rb +158 -158
  111. data/lib/tina4/swagger.rb +124 -124
  112. data/lib/tina4/template.rb +894 -894
  113. data/lib/tina4/templates/base.twig +26 -26
  114. data/lib/tina4/templates/errors/302.twig +14 -14
  115. data/lib/tina4/templates/errors/401.twig +9 -9
  116. data/lib/tina4/templates/errors/403.twig +29 -29
  117. data/lib/tina4/templates/errors/404.twig +29 -29
  118. data/lib/tina4/templates/errors/500.twig +38 -38
  119. data/lib/tina4/templates/errors/502.twig +9 -9
  120. data/lib/tina4/templates/errors/503.twig +12 -12
  121. data/lib/tina4/templates/errors/base.twig +37 -37
  122. data/lib/tina4/test_client.rb +159 -159
  123. data/lib/tina4/testing.rb +340 -340
  124. data/lib/tina4/validator.rb +174 -174
  125. data/lib/tina4/version.rb +1 -1
  126. data/lib/tina4/webserver.rb +312 -312
  127. data/lib/tina4/websocket.rb +343 -343
  128. data/lib/tina4/websocket_backplane.rb +190 -190
  129. data/lib/tina4/wsdl.rb +564 -564
  130. data/lib/tina4.rb +458 -458
  131. data/lib/tina4ruby.rb +4 -4
  132. metadata +3 -3
data/lib/tina4/session.rb CHANGED
@@ -1,297 +1,297 @@
1
- # frozen_string_literal: true
2
- require "securerandom"
3
- require "json"
4
-
5
- module Tina4
6
- class Session
7
- DEFAULT_OPTIONS = {
8
- cookie_name: "tina4_session",
9
- secret: nil,
10
- max_age: 86400,
11
- handler: :file,
12
- handler_options: {}
13
- }.freeze
14
-
15
- attr_reader :id, :data
16
-
17
- def initialize(env, options = {})
18
- @options = DEFAULT_OPTIONS.merge(options)
19
- @options[:secret] ||= ENV["SECRET"] || "tina4-default-secret"
20
- @handler = create_handler
21
- @id = extract_session_id(env) || SecureRandom.hex(32)
22
- @data = load_session
23
- @modified = false
24
- end
25
-
26
- def [](key)
27
- @data[key.to_s]
28
- end
29
-
30
- def []=(key, value)
31
- @data[key.to_s] = value
32
- @modified = true
33
- end
34
-
35
- def delete(key)
36
- @data.delete(key.to_s)
37
- @modified = true
38
- end
39
-
40
- def clear
41
- @data = {}
42
- @modified = true
43
- end
44
-
45
- def to_hash
46
- @data.dup
47
- end
48
-
49
- def save
50
- return unless @modified
51
- @handler.write(@id, @data)
52
- @modified = false
53
- end
54
-
55
- def destroy
56
- @handler.destroy(@id)
57
- @data = {}
58
- end
59
-
60
- # Get a session value with optional default
61
- def get(key, default = nil)
62
- @data[key.to_s] || default
63
- end
64
-
65
- # Set a session value
66
- def set(key, value)
67
- @data[key.to_s] = value
68
- @modified = true
69
- end
70
-
71
- # Check if a key exists in the session
72
- def has?(key)
73
- @data.key?(key.to_s)
74
- end
75
-
76
- # Return all session data
77
- def all
78
- @data.dup
79
- end
80
-
81
- # Flash data: set a value that is removed after next read.
82
- # Call with value to set, call without value to get (and remove).
83
- def flash(key, value = nil)
84
- flash_key = "_flash_#{key}"
85
- if value.nil?
86
- val = @data.delete(flash_key.to_s)
87
- @modified = true if val
88
- val
89
- else
90
- @data[flash_key.to_s] = value
91
- @modified = true
92
- value
93
- end
94
- end
95
-
96
- # Get flash data by key (alias for flash(key) without value)
97
- def get_flash(key, default = nil)
98
- result = flash(key)
99
- result.nil? ? default : result
100
- end
101
-
102
- # Regenerate the session ID while preserving data — returns new ID
103
- def regenerate
104
- old_id = @id
105
- @id = SecureRandom.hex(32)
106
- @handler.destroy(old_id)
107
- @modified = true
108
- @id
109
- end
110
-
111
- # Start or resume a session. If session_id is given, load that session;
112
- # otherwise generate a new ID. Returns the session ID string.
113
- def start(session_id = nil)
114
- if session_id
115
- @id = session_id
116
- @data = load_session
117
- else
118
- @id = SecureRandom.hex(32)
119
- @data = {}
120
- end
121
- @modified = false
122
- @id
123
- end
124
-
125
- # Returns the current session ID string.
126
- def get_session_id
127
- @id
128
- end
129
-
130
- # Reads raw session data for a given session ID from backend storage.
131
- # Returns the data hash or nil.
132
- def read(session_id)
133
- @handler.read(session_id)
134
- end
135
-
136
- # Writes raw session data for a given session ID to backend storage.
137
- def write(session_id, data, ttl = nil)
138
- if ttl
139
- @handler.write(session_id, data, ttl)
140
- else
141
- @handler.write(session_id, data)
142
- end
143
- end
144
-
145
- # Garbage collection: remove expired sessions from the handler
146
- def gc(max_lifetime = nil)
147
- max_lifetime ||= @options[:max_age]
148
- @handler.gc(max_lifetime) if @handler.respond_to?(:gc)
149
- end
150
-
151
- def cookie_header(cookie_name = nil)
152
- name = cookie_name || @options[:cookie_name]
153
- samesite = ENV["TINA4_SESSION_SAMESITE"] || "Lax"
154
- "#{name}=#{@id}; Path=/; HttpOnly; SameSite=#{samesite}; Max-Age=#{@options[:max_age]}"
155
- end
156
-
157
- private
158
-
159
- def extract_session_id(env)
160
- cookie_str = env["HTTP_COOKIE"] || ""
161
- cookie_str.split(";").each do |pair|
162
- key, value = pair.strip.split("=", 2)
163
- return value if key == @options[:cookie_name]
164
- end
165
- nil
166
- end
167
-
168
- def load_session
169
- existing = @handler.read(@id)
170
- existing || {}
171
- end
172
-
173
- def create_handler
174
- case @options[:handler].to_sym
175
- when :file
176
- Tina4::SessionHandlers::FileHandler.new(@options[:handler_options])
177
- when :redis
178
- Tina4::SessionHandlers::RedisHandler.new(@options[:handler_options])
179
- when :mongo, :mongodb
180
- Tina4::SessionHandlers::MongoHandler.new(@options[:handler_options])
181
- when :valkey
182
- Tina4::SessionHandlers::ValkeyHandler.new(@options[:handler_options])
183
- when :database, :db
184
- Tina4::SessionHandlers::DatabaseHandler.new(@options[:handler_options])
185
- else
186
- Tina4::SessionHandlers::FileHandler.new(@options[:handler_options])
187
- end
188
- end
189
- end
190
-
191
- class LazySession
192
- def initialize(env, options = {})
193
- @env = env
194
- @options = options
195
- @session = nil
196
- end
197
-
198
- def [](key)
199
- ensure_loaded
200
- @session[key]
201
- end
202
-
203
- def []=(key, value)
204
- ensure_loaded
205
- @session[key] = value
206
- end
207
-
208
- def delete(key)
209
- ensure_loaded
210
- @session.delete(key)
211
- end
212
-
213
- def clear
214
- ensure_loaded
215
- @session.clear
216
- end
217
-
218
- def save
219
- @session&.save
220
- end
221
-
222
- def destroy
223
- @session&.destroy
224
- end
225
-
226
- def get(key, default = nil)
227
- ensure_loaded
228
- @session.get(key, default)
229
- end
230
-
231
- def set(key, value)
232
- ensure_loaded
233
- @session.set(key, value)
234
- end
235
-
236
- def has?(key)
237
- ensure_loaded
238
- @session.has?(key)
239
- end
240
-
241
- def all
242
- ensure_loaded
243
- @session.all
244
- end
245
-
246
- def flash(key, value = nil)
247
- ensure_loaded
248
- @session.flash(key, value)
249
- end
250
-
251
- def regenerate
252
- ensure_loaded
253
- @session.regenerate
254
- end
255
-
256
- def gc(max_lifetime = nil)
257
- ensure_loaded
258
- @session.gc(max_lifetime)
259
- end
260
-
261
- def start(session_id = nil)
262
- ensure_loaded
263
- @session.start(session_id)
264
- end
265
-
266
- def get_session_id
267
- ensure_loaded
268
- @session.get_session_id
269
- end
270
-
271
- def read(session_id)
272
- ensure_loaded
273
- @session.read(session_id)
274
- end
275
-
276
- def write(session_id, data, ttl = nil)
277
- ensure_loaded
278
- @session.write(session_id, data, ttl)
279
- end
280
-
281
- def cookie_header(cookie_name = nil)
282
- ensure_loaded
283
- @session.cookie_header(cookie_name)
284
- end
285
-
286
- def to_hash
287
- ensure_loaded
288
- @session.to_hash
289
- end
290
-
291
- private
292
-
293
- def ensure_loaded
294
- @session ||= Session.new(@env, @options)
295
- end
296
- end
297
- end
1
+ # frozen_string_literal: true
2
+ require "securerandom"
3
+ require "json"
4
+
5
+ module Tina4
6
+ class Session
7
+ DEFAULT_OPTIONS = {
8
+ cookie_name: "tina4_session",
9
+ secret: nil,
10
+ max_age: 86400,
11
+ handler: :file,
12
+ handler_options: {}
13
+ }.freeze
14
+
15
+ attr_reader :id, :data
16
+
17
+ def initialize(env, options = {})
18
+ @options = DEFAULT_OPTIONS.merge(options)
19
+ @options[:secret] ||= ENV["SECRET"] || "tina4-default-secret"
20
+ @handler = create_handler
21
+ @id = extract_session_id(env) || SecureRandom.hex(32)
22
+ @data = load_session
23
+ @modified = false
24
+ end
25
+
26
+ def [](key)
27
+ @data[key.to_s]
28
+ end
29
+
30
+ def []=(key, value)
31
+ @data[key.to_s] = value
32
+ @modified = true
33
+ end
34
+
35
+ def delete(key)
36
+ @data.delete(key.to_s)
37
+ @modified = true
38
+ end
39
+
40
+ def clear
41
+ @data = {}
42
+ @modified = true
43
+ end
44
+
45
+ def to_hash
46
+ @data.dup
47
+ end
48
+
49
+ def save
50
+ return unless @modified
51
+ @handler.write(@id, @data)
52
+ @modified = false
53
+ end
54
+
55
+ def destroy
56
+ @handler.destroy(@id)
57
+ @data = {}
58
+ end
59
+
60
+ # Get a session value with optional default
61
+ def get(key, default = nil)
62
+ @data[key.to_s] || default
63
+ end
64
+
65
+ # Set a session value
66
+ def set(key, value)
67
+ @data[key.to_s] = value
68
+ @modified = true
69
+ end
70
+
71
+ # Check if a key exists in the session
72
+ def has?(key)
73
+ @data.key?(key.to_s)
74
+ end
75
+
76
+ # Return all session data
77
+ def all
78
+ @data.dup
79
+ end
80
+
81
+ # Flash data: set a value that is removed after next read.
82
+ # Call with value to set, call without value to get (and remove).
83
+ def flash(key, value = nil)
84
+ flash_key = "_flash_#{key}"
85
+ if value.nil?
86
+ val = @data.delete(flash_key.to_s)
87
+ @modified = true if val
88
+ val
89
+ else
90
+ @data[flash_key.to_s] = value
91
+ @modified = true
92
+ value
93
+ end
94
+ end
95
+
96
+ # Get flash data by key (alias for flash(key) without value)
97
+ def get_flash(key, default = nil)
98
+ result = flash(key)
99
+ result.nil? ? default : result
100
+ end
101
+
102
+ # Regenerate the session ID while preserving data — returns new ID
103
+ def regenerate
104
+ old_id = @id
105
+ @id = SecureRandom.hex(32)
106
+ @handler.destroy(old_id)
107
+ @modified = true
108
+ @id
109
+ end
110
+
111
+ # Start or resume a session. If session_id is given, load that session;
112
+ # otherwise generate a new ID. Returns the session ID string.
113
+ def start(session_id = nil)
114
+ if session_id
115
+ @id = session_id
116
+ @data = load_session
117
+ else
118
+ @id = SecureRandom.hex(32)
119
+ @data = {}
120
+ end
121
+ @modified = false
122
+ @id
123
+ end
124
+
125
+ # Returns the current session ID string.
126
+ def get_session_id
127
+ @id
128
+ end
129
+
130
+ # Reads raw session data for a given session ID from backend storage.
131
+ # Returns the data hash or nil.
132
+ def read(session_id)
133
+ @handler.read(session_id)
134
+ end
135
+
136
+ # Writes raw session data for a given session ID to backend storage.
137
+ def write(session_id, data, ttl = nil)
138
+ if ttl
139
+ @handler.write(session_id, data, ttl)
140
+ else
141
+ @handler.write(session_id, data)
142
+ end
143
+ end
144
+
145
+ # Garbage collection: remove expired sessions from the handler
146
+ def gc(max_lifetime = nil)
147
+ max_lifetime ||= @options[:max_age]
148
+ @handler.gc(max_lifetime) if @handler.respond_to?(:gc)
149
+ end
150
+
151
+ def cookie_header(cookie_name = nil)
152
+ name = cookie_name || @options[:cookie_name]
153
+ samesite = ENV["TINA4_SESSION_SAMESITE"] || "Lax"
154
+ "#{name}=#{@id}; Path=/; HttpOnly; SameSite=#{samesite}; Max-Age=#{@options[:max_age]}"
155
+ end
156
+
157
+ private
158
+
159
+ def extract_session_id(env)
160
+ cookie_str = env["HTTP_COOKIE"] || ""
161
+ cookie_str.split(";").each do |pair|
162
+ key, value = pair.strip.split("=", 2)
163
+ return value if key == @options[:cookie_name]
164
+ end
165
+ nil
166
+ end
167
+
168
+ def load_session
169
+ existing = @handler.read(@id)
170
+ existing || {}
171
+ end
172
+
173
+ def create_handler
174
+ case @options[:handler].to_sym
175
+ when :file
176
+ Tina4::SessionHandlers::FileHandler.new(@options[:handler_options])
177
+ when :redis
178
+ Tina4::SessionHandlers::RedisHandler.new(@options[:handler_options])
179
+ when :mongo, :mongodb
180
+ Tina4::SessionHandlers::MongoHandler.new(@options[:handler_options])
181
+ when :valkey
182
+ Tina4::SessionHandlers::ValkeyHandler.new(@options[:handler_options])
183
+ when :database, :db
184
+ Tina4::SessionHandlers::DatabaseHandler.new(@options[:handler_options])
185
+ else
186
+ Tina4::SessionHandlers::FileHandler.new(@options[:handler_options])
187
+ end
188
+ end
189
+ end
190
+
191
+ class LazySession
192
+ def initialize(env, options = {})
193
+ @env = env
194
+ @options = options
195
+ @session = nil
196
+ end
197
+
198
+ def [](key)
199
+ ensure_loaded
200
+ @session[key]
201
+ end
202
+
203
+ def []=(key, value)
204
+ ensure_loaded
205
+ @session[key] = value
206
+ end
207
+
208
+ def delete(key)
209
+ ensure_loaded
210
+ @session.delete(key)
211
+ end
212
+
213
+ def clear
214
+ ensure_loaded
215
+ @session.clear
216
+ end
217
+
218
+ def save
219
+ @session&.save
220
+ end
221
+
222
+ def destroy
223
+ @session&.destroy
224
+ end
225
+
226
+ def get(key, default = nil)
227
+ ensure_loaded
228
+ @session.get(key, default)
229
+ end
230
+
231
+ def set(key, value)
232
+ ensure_loaded
233
+ @session.set(key, value)
234
+ end
235
+
236
+ def has?(key)
237
+ ensure_loaded
238
+ @session.has?(key)
239
+ end
240
+
241
+ def all
242
+ ensure_loaded
243
+ @session.all
244
+ end
245
+
246
+ def flash(key, value = nil)
247
+ ensure_loaded
248
+ @session.flash(key, value)
249
+ end
250
+
251
+ def regenerate
252
+ ensure_loaded
253
+ @session.regenerate
254
+ end
255
+
256
+ def gc(max_lifetime = nil)
257
+ ensure_loaded
258
+ @session.gc(max_lifetime)
259
+ end
260
+
261
+ def start(session_id = nil)
262
+ ensure_loaded
263
+ @session.start(session_id)
264
+ end
265
+
266
+ def get_session_id
267
+ ensure_loaded
268
+ @session.get_session_id
269
+ end
270
+
271
+ def read(session_id)
272
+ ensure_loaded
273
+ @session.read(session_id)
274
+ end
275
+
276
+ def write(session_id, data, ttl = nil)
277
+ ensure_loaded
278
+ @session.write(session_id, data, ttl)
279
+ end
280
+
281
+ def cookie_header(cookie_name = nil)
282
+ ensure_loaded
283
+ @session.cookie_header(cookie_name)
284
+ end
285
+
286
+ def to_hash
287
+ ensure_loaded
288
+ @session.to_hash
289
+ end
290
+
291
+ private
292
+
293
+ def ensure_loaded
294
+ @session ||= Session.new(@env, @options)
295
+ end
296
+ end
297
+ end