diff --git a/lib/camping-unabridged.rb b/lib/camping-unabridged.rb index 7cc6d59..e72bca7 100644 --- a/lib/camping-unabridged.rb +++ b/lib/camping-unabridged.rb @@ -507,6 +507,7 @@ def r501(m) # end # end def to_a + @env['rack.session'] = @state r = Rack::Response.new(@body, @status, @headers) @cookies.each do |k, v| v = {:value => v, :path => self / "/"} if String===v @@ -517,10 +518,10 @@ def to_a def initialize(env, m) #:nodoc: r = @request = Rack::Request.new(@env = env) - @root, p, @cookies, + @root, p, @cookies, @state, @headers, @status, @method = - (env.SCRIPT_NAME||'').sub(/\/$/,''), - H[r.params], H[r.cookies], + (env['SCRIPT_NAME']||'').sub(/\/$/,''), + H[r.params], H[r.cookies], H[r.session], {}, m =~ /r(\d+)/ ? $1.to_i : 200, m @input = p.inject(H[]) do |h, (k, v)| @@ -841,9 +842,8 @@ def goes(m) # And array with [statuc, headers, body] is expected at the output. def call(e) X.M - e = H[e] - p = e.PATH_INFO = U.unescape(e.PATH_INFO) - k,m,*a=X.D p,(e.REQUEST_METHOD||'get').downcase + p = e['PATH_INFO'] = U.unescape(e['PATH_INFO']) + k,m,*a=X.D p,(e['REQUEST_METHOD']||'get').downcase k.new(e,m).service(*a).to_a rescue r500(:I, k, m, $!, :env => e).to_a diff --git a/lib/camping.rb b/lib/camping.rb index bb2d074..d23b89a 100644 --- a/lib/camping.rb +++ b/lib/camping.rb @@ -19,14 +19,14 @@ def r s,b,h={};b,h=h,b if Hash===b;@status=s; @headers.merge!(h);@body=b;end;def redirect *a;r 302,'','Location'=>URL(*a). to_s;end;def r404 p;P%"#{p} not found"end;def r500 k,m,e;raise e;end def r501 m;P%"#{m.upcase} not implemented"end;def to_a -r=Rack::Response.new(@body,@status,@headers) +@env['rack.session']=@state;r=Rack::Response.new(@body,@status,@headers) @cookies.each{|k,v|v={:value=>v,:path=>self/"/"} if String===v r.set_cookie(k,v)} r.to_a;end;def initialize(env,m) r=@request=Rack::Request.new(@env=env) -@root,p,@cookies,@headers,@status,@method= -(env.SCRIPT_NAME||'').sub(/\/$/,''),H[r.params], -H[r.cookies],{},m=~/r(\d+)/?$1.to_i: 200,m +@root,p,@cookies,@state,@headers,@status,@method= +(env['SCRIPT_NAME']||'').sub(/\/$/,''),H[r.params], +H[r.cookies],H[r.session],{},m=~/r(\d+)/?$1.to_i: 200,m @input=p.inject(H[]){|h,(k,v)|h.merge k.split(/[\]\[]+/).reverse.inject(v){|x,i| H[i=>x]},&M};end;def service *a r=catch(:halt){send(@method,*a)};@body||=r @@ -42,8 +42,8 @@ def M;def M;end;constants.map{|c|k=const_get(c) }if !k.respond_to?:urls}end end;I=R() end;X=Controllers;class<e).to_a;end def method_missing m,c,*a;X.M;h=Hash===a[-1]?a.pop: {} e=H[Rack::MockRequest.env_for('',h[:env]||{})] diff --git a/lib/camping/ar/session.rb b/lib/camping/ar/session.rb deleted file mode 100644 index 20dc6df..0000000 --- a/lib/camping/ar/session.rb +++ /dev/null @@ -1,132 +0,0 @@ -# == About camping/ar/session.rb -# -# This file contains two modules which supply basic sessioning to your Camping app. -# Again, we're dealing with a pretty little bit of code: approx. 60 lines. -# -# * Camping::Models::Session is a module which adds a single sessions table -# to your database. -# * Camping::ARSession is a module which you will mix into your application (or into -# specific controllers which require sessions) to supply a @state variable -# you can use in controllers and views. -# -# For a basic tutorial, see the *Getting Started* section of the Camping::ARSession module. -require 'camping' -require 'camping/ar' - -module Camping::Models -# A database table for storing Camping sessions. Contains a unique 32-character hashid, a -# creation timestamp, and a column of serialized data called ivars. -class Session < Base - serialize :ivars - set_primary_key :hashid - - def []=(k, v) # :nodoc: - self.ivars[k] = v - end - def [](k) # :nodoc: - self.ivars[k] rescue nil - end - - protected - RAND_CHARS = [*'A'..'Z'] + [*'0'..'9'] + [*'a'..'z'] - def before_create - rand_max = RAND_CHARS.size - sid = (0...32).inject("") { |ret,_| ret << RAND_CHARS[rand(rand_max)] } - write_attribute('hashid', sid) - end - - # Generates a new session ID and creates a row for the new session in the database. - def self.generate cookies - sess = Session.create :ivars => Camping::H[] - cookies.camping_sid = sess.hashid - sess - end - - # Gets the existing session based on the camping_sid available in cookies. - # If none is found, generates a new session. - def self.persist cookies - session = nil - if cookies.camping_sid - session = Camping::Models::Session.find_by_hashid cookies.camping_sid - end - unless session - session = Camping::Models::Session.generate cookies - end - session - end - - # Builds the session table in the database. To be used in your application's - # create method. - # - # Like so: - # - # def Blog.create - # Camping::Models::Session.create_schema - # unless Blog::Models::Post.table_exists? - # ActiveRecord::Schema.define(&Blog::Models.schema) - # end - # end - # - def self.create_schema - unless table_exists? - ActiveRecord::Schema.define do - create_table :sessions, :force => true, :id => false do |t| - t.column :hashid, :string, :limit => 32, :null => false - t.column :created_at, :datetime - t.column :ivars, :text - end - add_index :sessions, [:hashid], :unique => true - end - reset_column_information - end - end -end -Session.partial_updates = false if Session.respond_to?(:partial_updates=) -end - -module Camping -# The Camping::ARSession module is designed to be mixed into your application or into specific -# controllers which require sessions. This module defines a service method which -# intercepts all requests handed to those controllers. -# -# == Getting Started -# -# To get sessions working for your application: -# -# 1. require 'camping/session' -# 2. Mixin the module: module YourApp; include Camping::ARSession end -# 3. In your application's create method, add a call to Camping::Models::Session.create_schema -# 4. Throughout your application, use the @state var like a hash to store your application's data. -# -# If you are unfamiliar with the create method, see -# http://code.whytheluckystiff.net/camping/wiki/GiveUsTheCreateMethod. -# -# == A Few Notes -# -# * The session ID is stored in a cookie. Look in @cookies.camping_sid. -# * The session data is stored in the sessions table in your database. -# * All mounted Camping apps using this class will use the same database table. -# * However, your application's data is stored in its own hash. -# * Session data is only saved if it has changed. -module ARSession - # This service method, when mixed into controllers, intercepts requests - # and wraps them with code to start and close the session. If a session isn't found - # in the database it is created. The @state variable is set and if it changes, - # it is saved back into the database. - def service(*a) - session = Camping::Models::Session.persist @cookies - app = self.class.name.gsub(/^(\w+)::.+$/, '\1') - @state = (session[app] ||= Camping::H[]) - hash_before = Marshal.dump(@state).hash - return super(*a) - ensure - if session - hash_after = Marshal.dump(@state).hash - unless hash_before == hash_after - session[app] = @state - session.save - end - end - end -end -end diff --git a/lib/camping/session.rb b/lib/camping/session.rb index ca3b262..2a68f4b 100644 --- a/lib/camping/session.rb +++ b/lib/camping/session.rb @@ -1,75 +1,26 @@ -# == About camping/session.rb -# TODO: Clean everything up. Lots of just plain wrong stuff in here. -# -# This file contains two modules which supply basic sessioning to your Camping app. -# Again, we're dealing with a pretty little bit of code: approx. 60 lines. -# -# * Camping::Models::Session is a module which adds a single sessions table -# to your database. -# * Camping::Session is a module which you will mix into your application (or into -# specific controllers which require sessions) to supply a @state variable -# you can use in controllers and views. -# -# For a basic tutorial, see the *Getting Started* section of the Camping::Session module. -#require 'camping' -require 'base64' -require 'openssl' - module Camping -# The Camping::Session module is designed to be mixed into your application or into specific -# controllers which require sessions. This module defines a service method which -# intercepts all requests handed to those controllers. -# -# == Getting Started -# -# To get sessions working for your application: -# -# 1. require 'camping/session' -# 2. Mixin the module: module YourApp; include Camping::Session end -# 3. Define a secret (and keep it secret): module YourApp; @@state_secret = "SECRET!"; end -# 4. Throughout your application, use the @state var like a hash to store your application's data. -# -# == A Few Notes -# -# * The session is stored in a cookie. Look in @cookies.identity. -# * Session data is only saved if it has changed. -module Session - DIGEST = OpenSSL::Digest::SHA1.new - # This service method, when mixed into controllers, intercepts requests - # and wraps them with code to start and close the session. If a session isn't found - # in the cookie it is created. The @state variable is set and if it changes, - # it is saved back into the cookie. - def service(*a) - @session_blob = @input.camping_blob || @cookies.camping_blob - @session_hash = @input.camping_hash || @cookies.camping_hash - decoded_blob, data = '', {} - begin - if @session_blob && @session_hash && secure_blob_hasher(@session_blob) == @session_hash - decoded_blob = Base64.decode64(@session_blob) - data = Marshal.restore(decoded_blob) - end - - app = C.name - @state = (data[app] ||= Camping::H[]) - hash_before = decoded_blob.hash - return super(*a) - ensure - data[app] = @state - decoded_blob = Marshal.dump(data) - unless hash_before == decoded_blob.hash - @session_blob = Base64.encode64(decoded_blob).gsub("\n", '').strip - @session_hash = secure_blob_hasher(@session_blob) - raise "The session contains to much data" if @session_blob.length > 4096 - @cookies.camping_blob = @session_blob - @cookies.camping_hash = @session_hash - end - end + # == Getting Started + # + # To get sessions working for your application: + # 1. require 'camping/session' + # 2. Mixin the module: include Camping::Session + # 3. Define a secret (and keep it secret): secret "SECRET!" + # 4. Throughout your application, use the @state var like a hash + # to store your application's data. + # + # require 'camping/session' # 1 + # + # module MyApp + # include Camping::Session # 2 + # secret "Oh yeah!" # 3 + # end + module Session + def self.included(app) + key = "#{app}.state".downcase + secret = [__FILE__, File.mtime(__FILE__)].join(":") + + app.meta_def(:secret) { |val| secret.replace(val) } + app.use Rack::Session::Cookie, :key => key, :secret => secret end - - def secure_blob_hasher(data) - OpenSSL::HMAC.hexdigest(DIGEST, state_secret, "#{@env.REMOTE_ADDR}#{data}") - end - - def state_secret; [__FILE__, File.mtime(__FILE__)].join(":") end -end -end + end +end \ No newline at end of file