glamping 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/History.txt +4 -0
- data/License.txt +18 -0
- data/Manifest.txt +9 -0
- data/README.txt +60 -0
- data/lib/glamping.rb +823 -0
- data/lib/glamping/boot.rb +90 -0
- data/lib/glamping/db.rb +333 -0
- data/lib/glamping/session.rb +132 -0
- data/lib/glamping/version.rb +9 -0
- data/test/test_glamping.rb +11 -0
- data/test/test_helper.rb +2 -0
- metadata +122 -0
- metadata.gz.sig +0 -0
@@ -0,0 +1,90 @@
|
|
1
|
+
# == About glamping/boot.rb (also known as <tt>boot.rb</tt>)
|
2
|
+
# If you don't like putting everthing into the same file and don't
|
3
|
+
# won't to cludder your file with many <tt>require</tt>'s, you should
|
4
|
+
# look at <tt>boot.rb</tt>.
|
5
|
+
#
|
6
|
+
# === Using
|
7
|
+
# require 'rubygems'
|
8
|
+
# require 'glamping/boot'
|
9
|
+
# Glamping.boot(binding)
|
10
|
+
#
|
11
|
+
# === Directory Structure
|
12
|
+
# app/
|
13
|
+
# controllers/
|
14
|
+
# helpers/
|
15
|
+
# models/
|
16
|
+
# views/
|
17
|
+
#
|
18
|
+
# public/
|
19
|
+
# cache/
|
20
|
+
# config/
|
21
|
+
# database.yml
|
22
|
+
# schema.rb
|
23
|
+
#
|
24
|
+
# sample_app.rb
|
25
|
+
#
|
26
|
+
# === Notes
|
27
|
+
# * <tt>boot.rb</tt> will automatically initialize a Glamping application
|
28
|
+
# based on the filename. So in this example, it will be named <tt>SampleApp</tt>
|
29
|
+
# * If you need databases, simply create the file <tt>config/database.yml</tt>:
|
30
|
+
# adapter: mysql
|
31
|
+
# host: localhost
|
32
|
+
# username: db_user
|
33
|
+
# password: db_pass
|
34
|
+
# database: sample_app
|
35
|
+
# * ActiveRecord won't be loaded at all unless <tt>config/database.yml</tt>
|
36
|
+
# is provided.
|
37
|
+
# * By adding <tt>sessions: true</tt> into <tt>config/database.yml</tt>,
|
38
|
+
# your application gets session support (See glamping/session.rb[link:files/lib/glamping/session_rb.html]).
|
39
|
+
# * The cache path will be set to <tt>public/cache/</tt> (See Glamping::Helpers::PageCaching)
|
40
|
+
# * The view path will be set to <tt>app/views/</tt>.
|
41
|
+
# * All files in <tt>public/</tt> will be served at /.
|
42
|
+
require 'glamping'
|
43
|
+
Glamping.class_eval do
|
44
|
+
BOOT = %q{
|
45
|
+
root = ENV["GLAMPING_ROOT"] || File.dirname(File.expand_path(__FILE__))
|
46
|
+
app_name = File.basename(__FILE__, ".rb").split('_').map{|word|word.capitalize}.join
|
47
|
+
Glamping.goes(app_name)
|
48
|
+
app = Object.const_get(app_name)
|
49
|
+
app.config.views = File.join(root,"app","views")
|
50
|
+
app.config.public = File.join(root, "public")
|
51
|
+
app.config.cache = File.join(app.config.public, "cache")
|
52
|
+
|
53
|
+
require 'glamping/db' if d=File.exists?(database_file = "config/database.yml")
|
54
|
+
|
55
|
+
(Dir["app/*/"] - ["app/views/"]).each do |mod_name|
|
56
|
+
mod = app.const_get(File.basename(mod_name).capitalize)
|
57
|
+
Dir[mod_name + "**/*.rb"].each do |file|
|
58
|
+
mod.module_eval File.read(file)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
if d
|
63
|
+
require 'yaml'
|
64
|
+
f = YAML.load(File.read(database_file))
|
65
|
+
s = f.delete("session") || f.delete("sessions")
|
66
|
+
app::Models::Base.establish_connection(f)
|
67
|
+
|
68
|
+
if s
|
69
|
+
require 'glamping/session'
|
70
|
+
app.send(:include, Glamping::Session)
|
71
|
+
Glamping::Models::Session.create_schema
|
72
|
+
end
|
73
|
+
if File.exists?(a="config/schema.rb")
|
74
|
+
app::Models::AutoMigration.auto_migrate(File.read(a))
|
75
|
+
else
|
76
|
+
app::Models.create_schema
|
77
|
+
end
|
78
|
+
end
|
79
|
+
app.create if app.respond_to? :create
|
80
|
+
}
|
81
|
+
|
82
|
+
# A magically method which enables directory structure, connects
|
83
|
+
# to the database, running migrations and adds session-support.
|
84
|
+
#
|
85
|
+
# Should always be called with <tt>binding</tt>.
|
86
|
+
# Glamping.boot(binding)
|
87
|
+
def self.boot(b)
|
88
|
+
eval(BOOT,b)
|
89
|
+
end
|
90
|
+
end
|
data/lib/glamping/db.rb
ADDED
@@ -0,0 +1,333 @@
|
|
1
|
+
class MissingLibrary < Exception # :nodoc:
|
2
|
+
end
|
3
|
+
begin
|
4
|
+
require 'active_record'
|
5
|
+
rescue LoadError => e
|
6
|
+
raise MissingLibrary, "ActiveRecord could not be loaded (is it installed?): #{e.message}"
|
7
|
+
end
|
8
|
+
|
9
|
+
$AR_EXTRAS = %q{
|
10
|
+
Base = ActiveRecord::Base unless const_defined? :Base
|
11
|
+
|
12
|
+
def Y; ActiveRecord::Base.verify_active_connections!; self; end
|
13
|
+
|
14
|
+
class SchemaInfo < Base
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.V(n)
|
18
|
+
@final = [n, @final.to_i].max
|
19
|
+
m = (@migrations ||= [])
|
20
|
+
Class.new(ActiveRecord::Migration) do
|
21
|
+
meta_def(:version) { n }
|
22
|
+
meta_def(:inherited) { |k| m << k }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.create_schema(opts = {})
|
27
|
+
opts[:assume] ||= 0
|
28
|
+
opts[:version] ||= @final
|
29
|
+
if @migrations
|
30
|
+
unless SchemaInfo.table_exists?
|
31
|
+
ActiveRecord::Schema.define do
|
32
|
+
create_table SchemaInfo.table_name do |t|
|
33
|
+
t.column :version, :float
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
si = SchemaInfo.find(:first) || SchemaInfo.new(:version => opts[:assume])
|
39
|
+
if si.version < opts[:version]
|
40
|
+
@migrations.each do |k|
|
41
|
+
k.migrate(:up) if si.version < k.version and k.version <= opts[:version]
|
42
|
+
k.migrate(:down) if si.version > k.version and k.version > opts[:version]
|
43
|
+
end
|
44
|
+
si.update_attributes(:version => opts[:version])
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
module AutoMigration
|
50
|
+
def self.migrate(s=nil, &blk)
|
51
|
+
s=blk if block_given?
|
52
|
+
case s
|
53
|
+
when String
|
54
|
+
eval(s)
|
55
|
+
when File
|
56
|
+
eval(s.read)
|
57
|
+
when Proc
|
58
|
+
module_eval(&s)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class << self
|
63
|
+
alias :<< :migrate
|
64
|
+
end
|
65
|
+
|
66
|
+
@@tables_in_schema, @@indexes_in_schema = [], []
|
67
|
+
|
68
|
+
def self.auto_migrate(s=nil, &blk)
|
69
|
+
self.migrate(s, &blk)
|
70
|
+
self.cleanup
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.cleanup
|
74
|
+
drop_unused_tables
|
75
|
+
drop_unused_indexes
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.method_missing(method, *args, &block)
|
79
|
+
case method
|
80
|
+
when :create_table
|
81
|
+
auto_create_table(method, *args, &block)
|
82
|
+
when :add_index
|
83
|
+
auto_add_index(method, *args, &block)
|
84
|
+
else
|
85
|
+
method_missing_without_auto_migration(method, *args, &block)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.method_missing_without_auto_migration(method, *args, &block)
|
90
|
+
ActiveRecord::Migration.send(method, *args, &block)
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.auto_create_table(method, *args, &block)
|
94
|
+
table_name = args.shift.to_s
|
95
|
+
options = args.pop || {}
|
96
|
+
(@@tables_in_schema ||= []) << table_name
|
97
|
+
|
98
|
+
# Table doesn't exist, create it
|
99
|
+
unless ActiveRecord::Base.connection.tables.include?(table_name)
|
100
|
+
return method_missing_without_auto_migration(method, *[table_name, options], &block)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Grab database columns
|
104
|
+
fields_in_db = ActiveRecord::Base.connection.columns(table_name).inject({}) do |hash, column|
|
105
|
+
hash[column.name] = column
|
106
|
+
hash
|
107
|
+
end
|
108
|
+
|
109
|
+
# Grab schema columns (lifted from active_record/connection_adapters/abstract/schema_statements.rb)
|
110
|
+
table_definition = ActiveRecord::ConnectionAdapters::TableDefinition.new(ActiveRecord::Base.connection)
|
111
|
+
table_definition.primary_key(options[:primary_key] || "id") unless options[:id] == false
|
112
|
+
yield table_definition
|
113
|
+
fields_in_schema = table_definition.columns.inject({}) do |hash, column|
|
114
|
+
hash[column.name.to_s] = column
|
115
|
+
hash
|
116
|
+
end
|
117
|
+
|
118
|
+
# Add fields to db new to schema
|
119
|
+
(fields_in_schema.keys - fields_in_db.keys).each do |field|
|
120
|
+
column = fields_in_schema[field]
|
121
|
+
add_column table_name, column.name, column.type.to_sym, :limit => column.limit, :precision => column.precision,
|
122
|
+
:scale => column.scale, :default => column.default, :null => column.null
|
123
|
+
end
|
124
|
+
|
125
|
+
# Remove fields from db no longer in schema
|
126
|
+
(fields_in_db.keys - fields_in_schema.keys & fields_in_db.keys).each do |field|
|
127
|
+
column = fields_in_db[field]
|
128
|
+
remove_column table_name, column.name
|
129
|
+
end
|
130
|
+
|
131
|
+
# Change field type if schema is different from db
|
132
|
+
(fields_in_schema.keys & fields_in_db.keys).each do |field|
|
133
|
+
if (field != 'id') && (fields_in_schema[field].type.to_sym != fields_in_db[field].type.to_sym)
|
134
|
+
change_column table_name, fields_in_schema[field].name.to_sym, fields_in_schema[field].type.to_sym
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def self.auto_add_index(method, *args, &block)
|
140
|
+
table_name = args.shift.to_s
|
141
|
+
fields = Array(args.shift).map(&:to_s)
|
142
|
+
options = args.shift
|
143
|
+
|
144
|
+
index_name = options[:name] if options
|
145
|
+
index_name ||= "index_#{table_name}_on_#{fields.join('_and_')}"
|
146
|
+
|
147
|
+
(@@indexes_in_schema ||= []) << index_name
|
148
|
+
|
149
|
+
unless ActiveRecord::Base.connection.indexes(table_name).detect { |i| i.name == index_name }
|
150
|
+
method_missing_without_auto_migration(method, *[table_name, fields, options], &block)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def self.drop_unused_tables
|
155
|
+
(ActiveRecord::Base.connection.tables - @@tables_in_schema - [SchemaInfo.table_name, "sessions"]).each do |table|
|
156
|
+
drop_table table
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def self.drop_unused_indexes
|
161
|
+
@@tables_in_schema.each do |table_name|
|
162
|
+
indexes_in_db = ActiveRecord::Base.connection.indexes(table_name).map(&:name)
|
163
|
+
(indexes_in_db - @@indexes_in_schema & indexes_in_db).each do |index_name|
|
164
|
+
remove_index table_name, :name => index_name
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
}
|
170
|
+
|
171
|
+
module Glamping
|
172
|
+
# Models is an empty Ruby module for housing model classes derived
|
173
|
+
# from ActiveRecord::Base. As a shortcut, you may derive from Base
|
174
|
+
# which is an alias for ActiveRecord::Base.
|
175
|
+
#
|
176
|
+
# module Glamping::Models
|
177
|
+
# class Post < Base; belongs_to :user end
|
178
|
+
# class User < Base; has_many :posts end
|
179
|
+
# end
|
180
|
+
#
|
181
|
+
# == Where Models are Used
|
182
|
+
#
|
183
|
+
# Models are used in your controller classes. However, if your model class
|
184
|
+
# name conflicts with a controller class name, you will need to refer to it
|
185
|
+
# using the Models module.
|
186
|
+
#
|
187
|
+
# module Glamping::Controllers
|
188
|
+
# class Post < R '/post/(\d+)'
|
189
|
+
# def get(post_id)
|
190
|
+
# @post = Models::Post.find post_id
|
191
|
+
# render :index
|
192
|
+
# end
|
193
|
+
# end
|
194
|
+
# end
|
195
|
+
#
|
196
|
+
# == Regular Migration
|
197
|
+
# It's very easy to use migration in Glamping (and Camping). Just add
|
198
|
+
# a class in your <tt>Models</tt>. Remember that all tables begins with
|
199
|
+
# the name of your app.
|
200
|
+
#
|
201
|
+
# class InitialSchema < V 0.1
|
202
|
+
# def self.up
|
203
|
+
# create_table :sample_app_posts do |t|
|
204
|
+
# t.string :title
|
205
|
+
# t.text :content
|
206
|
+
# t.timestamps
|
207
|
+
# end
|
208
|
+
# end
|
209
|
+
#
|
210
|
+
# def self.down
|
211
|
+
# drop_table :sample_app_posts
|
212
|
+
# end
|
213
|
+
# end
|
214
|
+
#
|
215
|
+
# When you later need to add some more tables you just write another
|
216
|
+
# migration, but now increase the number to <tt>0.2</tt>.
|
217
|
+
#
|
218
|
+
# class AddComments < V 0.2
|
219
|
+
# def self.up
|
220
|
+
# create_table :sample_app_comments do |t|
|
221
|
+
# t.integer :post_id, :null => false
|
222
|
+
# t.string :author
|
223
|
+
# t.text :content
|
224
|
+
# end
|
225
|
+
# end
|
226
|
+
#
|
227
|
+
# def self.down
|
228
|
+
# drop_table :sample_app_comments
|
229
|
+
# end
|
230
|
+
# end
|
231
|
+
#
|
232
|
+
# === Running the migrations
|
233
|
+
# (Please read glamping/boot.rb[link:files/lib/glamping/boot_rb.html]
|
234
|
+
# when using <tt>boot.rb</tt>)
|
235
|
+
#
|
236
|
+
# In order to run the migrates, you call the <tt>create_schema</tt>
|
237
|
+
# method. It's a good idea to put this in the <tt>create</tt> method.
|
238
|
+
#
|
239
|
+
# def SampleApp.create
|
240
|
+
# SampleApp::Models.create_schema
|
241
|
+
# end
|
242
|
+
#
|
243
|
+
# == Auto Migration
|
244
|
+
# Auto Migration is a fine alternative to regular migrations. It was
|
245
|
+
# originally a plugin by PJ Hyett (link[http://errtheblog.com/posts/65-automatically]),
|
246
|
+
# and the version included here is 99% equal. Please note that there
|
247
|
+
# is impossible to rename a column when using auto migration.
|
248
|
+
#
|
249
|
+
# === Running the migration
|
250
|
+
# (Please read glamping/boot.rb[link:files/lib/glamping/boot_rb.html]
|
251
|
+
# when using <tt>boot.rb</tt>)
|
252
|
+
#
|
253
|
+
# A good idea is to put all your migration into one file, and then
|
254
|
+
# run <tt>auto_migration(File.open("schema.rb"))</tt>.
|
255
|
+
#
|
256
|
+
# # in schema.rb
|
257
|
+
# create_table Post.table_name do |t|
|
258
|
+
# t.string :title
|
259
|
+
# t.text :content
|
260
|
+
# t.timestamps
|
261
|
+
# end
|
262
|
+
#
|
263
|
+
# # in your main file
|
264
|
+
# def SampleApp.create
|
265
|
+
# SampleApp::Models::AutoMigration.auto_migrate(File.open("schema.rb"))
|
266
|
+
# end
|
267
|
+
#
|
268
|
+
# Whenever this file changes, it magically migrate everything for you.
|
269
|
+
# In this example I'm using <tt>Post.table_name</tt> in stead of
|
270
|
+
# <tt>:sample_app_posts</tt>, this is because the schema is loaded
|
271
|
+
# _after_ all the models.
|
272
|
+
#
|
273
|
+
# <tt>auto_migration</tt> is essentially only a <tt>migration</tt> and
|
274
|
+
# <tt>cleanup</tt> in the same method, so if you have migrations spread
|
275
|
+
# over several files do something like this:
|
276
|
+
#
|
277
|
+
# def SampleApp.create
|
278
|
+
# files = Dir["migrations/*.rb"]
|
279
|
+
# files.each do |f|
|
280
|
+
# SampleApp::Models::AutoMigration.migrate(File.open(f))
|
281
|
+
# end
|
282
|
+
# SampleApp::Models::AutoMigration.cleanup
|
283
|
+
# end
|
284
|
+
#
|
285
|
+
# === The Auto Migration License
|
286
|
+
# Copyright (c) 2007 PJ Hyett
|
287
|
+
#
|
288
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
289
|
+
# a copy of this software and associated documentation files (the
|
290
|
+
# "Software"), to deal in the Software without restriction, including
|
291
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
292
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
293
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
294
|
+
# the following conditions:
|
295
|
+
#
|
296
|
+
# The above copyright notice and this permission notice shall be
|
297
|
+
# included in all copies or substantial portions of the Software.
|
298
|
+
#
|
299
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
300
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
301
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
302
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
303
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
304
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
305
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
306
|
+
|
307
|
+
module Models
|
308
|
+
A = ActiveRecord
|
309
|
+
# Base is an alias for ActiveRecord::Base. The big warning I'm going to give you
|
310
|
+
# about this: *Base overloads table_name_prefix.* This means that if you have a
|
311
|
+
# model class Blog::Models::Post, it's table name will be <tt>blog_posts</tt>.
|
312
|
+
#
|
313
|
+
# ActiveRecord is not loaded if you never reference this class. The minute you
|
314
|
+
# use the ActiveRecord or Glamping::Models::Base class, then the ActiveRecord library
|
315
|
+
# is loaded.
|
316
|
+
Base = A::Base
|
317
|
+
|
318
|
+
# The default prefix for Glamping model classes is the topmost module name lowercase
|
319
|
+
# and followed with an underscore.
|
320
|
+
#
|
321
|
+
# Tepee::Models::Page.table_name_prefix
|
322
|
+
# #=> "tepee_pages"
|
323
|
+
#
|
324
|
+
def Base.table_name_prefix
|
325
|
+
"#{name[/\w+/]}_".downcase.sub(/^(#{A}|glamping)_/i,'')
|
326
|
+
end
|
327
|
+
module_eval $AR_EXTRAS
|
328
|
+
end
|
329
|
+
end
|
330
|
+
Glamping::S.sub! /def\s*Y[;\s]*self[;\s]*end/, $AR_EXTRAS
|
331
|
+
Object.constants.map{|c|Object.const_get(c)}.each do |c|
|
332
|
+
c::Models.module_eval $AR_EXTRAS if c.respond_to?(:goes)
|
333
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# == About glamping/session.rb
|
2
|
+
#
|
3
|
+
# This file contains two modules which supply basic sessioning to your Glamping app.
|
4
|
+
# Again, we're dealing with a pretty little bit of code: approx. 60 lines.
|
5
|
+
#
|
6
|
+
# * Glamping::Models::Session is a module which adds a single <tt>sessions</tt> table
|
7
|
+
# to your database.
|
8
|
+
# * Glamping::Session is a module which you will mix into your application (or into
|
9
|
+
# specific controllers which require sessions) to supply a <tt>@state</tt> variable
|
10
|
+
# you can use in controllers and views.
|
11
|
+
#
|
12
|
+
# For a basic tutorial, see the *Getting Started* section of the Glamping::Session module.
|
13
|
+
0 # Just a dummy-code for stupid RDOC...
|
14
|
+
module Glamping::Models
|
15
|
+
# A database table for storing Glamping sessions. Contains a unique 32-character hashid, a
|
16
|
+
# creation timestamp, and a column of serialized data called <tt>ivars</tt>.
|
17
|
+
class Session < Base
|
18
|
+
serialize :ivars
|
19
|
+
set_primary_key :hashid
|
20
|
+
|
21
|
+
def []=(k, v) # :nodoc:
|
22
|
+
self.ivars[k] = v
|
23
|
+
end
|
24
|
+
def [](k) # :nodoc:
|
25
|
+
self.ivars[k] rescue nil
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
RAND_CHARS = [*'A'..'Z'] + [*'0'..'9'] + [*'a'..'z']
|
30
|
+
def before_create
|
31
|
+
rand_max = RAND_CHARS.size
|
32
|
+
sid = (0...32).inject("") { |ret,_| ret << RAND_CHARS[rand(rand_max)] }
|
33
|
+
write_attribute('hashid', sid)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Generates a new session ID and creates a row for the new session in the database.
|
37
|
+
def self.generate cookies
|
38
|
+
sess = Session.create :ivars => Glamping::H[]
|
39
|
+
cookies.camping_sid = sess.hashid
|
40
|
+
sess
|
41
|
+
end
|
42
|
+
|
43
|
+
# Gets the existing session based on the <tt>camping_sid</tt> available in cookies.
|
44
|
+
# If none is found, generates a new session.
|
45
|
+
def self.persist cookies
|
46
|
+
session = nil
|
47
|
+
if cookies.camping_sid
|
48
|
+
session = Glamping::Models::Session.find_by_hashid cookies.camping_sid
|
49
|
+
end
|
50
|
+
unless session
|
51
|
+
session = Glamping::Models::Session.generate cookies
|
52
|
+
end
|
53
|
+
session
|
54
|
+
end
|
55
|
+
|
56
|
+
# Builds the session table in the database. To be used in your application's
|
57
|
+
# <tt>create</tt> method.
|
58
|
+
#
|
59
|
+
# Like so:
|
60
|
+
#
|
61
|
+
# def Blog.create
|
62
|
+
# Glamping::Models::Session.create_schema
|
63
|
+
# unless Blog::Models::Post.table_exists?
|
64
|
+
# ActiveRecord::Schema.define(&Blog::Models.schema)
|
65
|
+
# end
|
66
|
+
# end
|
67
|
+
#
|
68
|
+
def self.create_schema
|
69
|
+
#unless table_exists?
|
70
|
+
ActiveRecord::Schema.define do
|
71
|
+
create_table :sessions, :force => true, :id => false do |t|
|
72
|
+
t.column :hashid, :string, :limit => 32, :null => false
|
73
|
+
t.column :created_at, :datetime
|
74
|
+
t.column :ivars, :text
|
75
|
+
end
|
76
|
+
add_index :sessions, [:hashid], :unique => true
|
77
|
+
end
|
78
|
+
reset_column_information
|
79
|
+
#end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
module Glamping
|
85
|
+
# The Glamping::Session module is designed to be mixed into your application or into specific
|
86
|
+
# controllers which require sessions. This module defines a <tt>service</tt> method which
|
87
|
+
# intercepts all requests handed to those controllers.
|
88
|
+
#
|
89
|
+
# == Getting Started
|
90
|
+
# (Please read glamping/boot.rb[link:files/lib/glamping/boot_rb.html]
|
91
|
+
# when using <tt>boot.rb</tt>)
|
92
|
+
#
|
93
|
+
# To get sessions working for your application:
|
94
|
+
#
|
95
|
+
# 1. <tt>require 'camping/session'</tt>
|
96
|
+
# 2. Mixin the module: <tt>module YourApp; include Glamping::Session end</tt>
|
97
|
+
# 3. In your application's <tt>create</tt> method, add a call to <tt>Glamping::Models::Session.create_schema</tt>
|
98
|
+
# 4. Throughout your application, use the <tt>@state</tt> var like a hash to store your application's data.
|
99
|
+
#
|
100
|
+
# If you are unfamiliar with the <tt>create</tt> method, see
|
101
|
+
# http://code.whytheluckystiff.net/camping/wiki/GiveUsTheCreateMethod.
|
102
|
+
#
|
103
|
+
# == A Few Notes
|
104
|
+
#
|
105
|
+
# * The session ID is stored in a cookie. Look in <tt>@cookies.camping_sid</tt>.
|
106
|
+
# * The session data is stored in the <tt>sessions</tt> table in your database.
|
107
|
+
# * All mounted Glamping apps using this class will use the same database table.
|
108
|
+
# * However, your application's data is stored in its own hash.
|
109
|
+
# * Session data is only saved if it has changed.
|
110
|
+
# * Sessions are loaded from DB at *every* requests
|
111
|
+
module Session
|
112
|
+
# This <tt>service</tt> method, when mixed into controllers, intercepts requests
|
113
|
+
# and wraps them with code to start and close the session. If a session isn't found
|
114
|
+
# in the database it is created. The <tt>@state</tt> variable is set and if it changes,
|
115
|
+
# it is saved back into the database.
|
116
|
+
def service(*a)
|
117
|
+
session = Glamping::Models::Session.persist @cookies
|
118
|
+
app = self.class.name.gsub(/^(\w+)::.+$/, '\1')
|
119
|
+
@state = (session[app] ||= Glamping::H[])
|
120
|
+
hash_before = Marshal.dump(@state).hash
|
121
|
+
return super(*a)
|
122
|
+
ensure
|
123
|
+
if session
|
124
|
+
hash_after = Marshal.dump(@state).hash
|
125
|
+
unless hash_before == hash_after
|
126
|
+
session[app] = @state
|
127
|
+
session.save
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|