glamping 0.1.0
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.
- 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
|