Permalink
Browse files

initial import after porting code from datamapper4rails to rack

  • Loading branch information...
1 parent ef9bd7f commit e0cee5c4d0e6ecf35c406156c7e7c74de7572b35 @mkristian committed Jul 14, 2009
View
@@ -0,0 +1,2 @@
+/pkg
+*~
View
@@ -0,0 +1,6 @@
+=== 1.0.0 / 2009-02-09
+
+* 1 major enhancement
+
+ * Birthday!
+
View
@@ -0,0 +1,17 @@
+History.txt
+Manifest.txt
+README.txt
+Rakefile
+lib/rack_datamapper.rb
+lib/rack_datamapper/identity_maps.rb
+lib/rack_datamapper/restful_transactions.rb
+lib/rack_datamapper/session/abstract/store.rb
+lib/rack_datamapper/session/datamapper.rb
+lib/rack_datamapper/transaction_boundaries.rb
+lib/rack_datamapper/version.rb
+spec/datamapper_session_spec.rb
+spec/identity_maps_spec.rb
+spec/restful_transactions_spec.rb
+spec/spec.opts
+spec/spec_helper.rb
+spec/transaction_boundaries_spec.rb
View
No changes.
View
@@ -0,0 +1,32 @@
+= rack_datamapper
+
+* http://github.com/mkristian/rack_datamapper
+
+== DESCRIPTION:
+
+this collection of plugins helps to add datamapper functionality to Rack. there is a IdentityMaps plugin which wrappes the request and with it all database actions are using that identity map. the transaction related plugin TransactionBoundaries and RestfulTransactions wrappes the request into a transaction. for using datamapper to store session data there is the DatamapperStore.
+
+== LICENSE:
+
+(The MIT License)
+
+Copyright (c) 2009 Kristian Meier
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
View
@@ -0,0 +1,35 @@
+# -*- ruby -*-
+
+require 'rubygems'
+require 'hoe'
+require './lib/rack_datamapper/version.rb'
+
+require 'spec'
+require 'spec/rake/spectask'
+require 'pathname'
+require 'yard'
+
+Hoe.new('rack_datamapper', Rack::DataMapper::VERSION) do |p|
+ # p.rubyforge_name = 'dm-utf8x' # if different than lowercase project name
+ p.developer('mkristian', 'm.kristian@web.de')
+end
+
+desc 'Install the package as a gem.'
+task :install => [:clean, :package] do
+ gem = Dir['pkg/*.gem'].first
+ sh "gem install --local #{gem} --no-ri --no-rdoc"
+end
+
+desc 'Run specifications'
+Spec::Rake::SpecTask.new(:spec) do |t|
+ if File.exists?('spec/spec.opts')
+ t.spec_opts << '--options' << 'spec/spec.opts'
+ end
+ t.spec_files = Pathname.glob('./spec/**/*_spec.rb')
+end
+
+require 'yard'
+
+YARD::Rake::YardocTask.new
+
+# vim: syntax=Ruby
@@ -0,0 +1,9 @@
+require 'pathname'
+require 'extlib/pathname'
+require 'dm-core'
+
+require Pathname(__FILE__).dirname / 'rack_datamapper' / 'version'
+require Pathname(__FILE__).dirname / 'rack_datamapper' / 'identity_maps'
+require Pathname(__FILE__).dirname / 'rack_datamapper' / 'restful_transactions'
+require Pathname(__FILE__).dirname / 'rack_datamapper' / 'transaction_boundaries'
+require Pathname(__FILE__).dirname / 'rack_datamapper' / 'session' / 'datamapper'
@@ -0,0 +1,16 @@
+module DataMapper
+ class IdentityMaps
+ def initialize(app, name = :default)
+ @app = app
+ @name = name.to_sym
+ end
+
+ def call(env)
+ status, headers, response = nil, nil, nil
+ DataMapper.repository(@name) do
+ status, headers, response = @app.call(env)
+ end
+ [status, headers, response]
+ end
+ end
+end
@@ -0,0 +1,32 @@
+require 'rack'
+module DataMapper
+ class RestfulTransactions
+
+ class Rollback < StandardError; end
+
+ def initialize(app, name = :default)
+ @app = app
+ @name = name.to_sym
+ end
+
+ def call(env)
+ request = ::Rack::Request.new(env)
+ if ["POST", "PUT", "DELETE"].include? request.request_method
+ status, headers, response = nil, nil, nil
+ begin
+ transaction = DataMapper::Transaction.new(DataMapper.repository(@name))
+ transaction.commit do
+ status, headers, response = @app.call(env)
+ raise Rollback unless [301, 302, 303, 307].include?(status)
+ end
+ rescue Rollback
+ # ignore,
+ # this is just needed to trigger the rollback on the transaction
+ end
+ [status, headers, response]
+ else
+ @app.call(env)
+ end
+ end
+ end
+end
@@ -0,0 +1,97 @@
+require 'dm-core'
+
+module DataMapper
+ module Session
+ module Abstract
+ class Store
+
+ def initialize(app, options, id_generator)
+ @mutex = Mutex.new
+ if options.delete(:cache)
+ @@cache = {}
+ @@semaphore = Mutex.new
+ else
+ @@cache = nil unless self.class.class_variable_defined? :@@cache
+ end
+ @@session_class = options.delete(:session_class) || Session unless (self.class.class_variable_defined?(:@@session_class) and @@session_class)
+ @id_generator = id_generator
+ end
+
+ def get_session(env, sid)
+ @mutex.lock if env['rack.multithread']
+ if sid
+ session =
+ if @@cache
+ @@cache[sid] || @@session_class.get(sid)
+ else
+ @@session_class.get(sid)
+ end
+ end
+
+ unless sid and session
+ env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil?
+ sid = @id_generator.call
+ session = @@session_class.create(:session_id => sid, :updated_at => Time.now)
+ end
+ #session.instance_variable_set('@old', {}.merge(session))
+
+ return [sid, session.data]
+ ensure
+ @mutex.unlock if env['rack.multithread']
+ end
+
+ def set_session(env, sid, session_data, options)
+ @mutex.lock if env['rack.multithread']
+ session =
+ if @@cache
+ @@cache[sid] || @@session_class.get(sid)
+ else
+ @@session_class.get(sid)
+ end
+return false if session.nil?
+ if options[:renew] or options[:drop]
+ @@cache.delete(sid) if @@cache
+ session.destroy
+ return false if options[:drop]
+ sid = @id_generator.call
+ session = @@session_class.create(:session_id => sid, :updated_at => Time.now)
+ @@cache[sid] = session if @@cache
+ end
+# old_session = new_session.instance_variable_get('@old') || {}
+# session = merge_sessions session_id, old_session, new_session, session
+ session.data = session_data
+ if session.save
+ session.session_id
+ else
+ false
+ end
+ ensure
+ @mutex.unlock if env['rack.multithread']
+ end
+ end
+
+ class Session
+
+ include ::DataMapper::Resource
+
+ def self.name
+ "session"
+ end
+
+ property :session_id, String, :key => true
+
+ property :data, Text, :nullable => false, :default => ::Base64.encode64(Marshal.dump({}))
+
+ property :updated_at, DateTime, :nullable => true, :index => true
+
+ def data=(data)
+ attribute_set(:data, ::Base64.encode64(Marshal.dump(data)))
+ end
+
+ def data
+ Marshal.load(::Base64.decode64(attribute_get(:data)))
+ end
+ end
+ end
+ end
+end
@@ -0,0 +1,27 @@
+require 'rack/session/abstract/id'
+require 'rack_datamapper/session/abstract/store'
+
+module DataMapper
+ module Session
+ class Datamapper < ::Rack::Session::Abstract::ID
+
+ def initialize(app, options = {})
+ super
+ id_generator = Proc.new do
+ generate_sid
+ end
+ @store = ::DataMapper::Session::Abstract::Store.new(app, options, id_generator)
+ end
+
+ private
+
+ def get_session(env, sid)
+ @store.get_session(env, sid)
+ end
+
+ def set_session(env, sid, session_data, options)
+ @store.set_session(env, sid, session_data, options)
+ end
+ end
+ end
+end
@@ -0,0 +1,25 @@
+module DataMapper
+ class TransactionBoundaries
+
+ class Rollback < StandardError; end
+
+ def initialize(app, name = :default)
+ @app = app
+ @name = name.to_sym
+ end
+
+ def call(env)
+ status, headers, response = nil, nil, nil
+ begin
+ transaction = DataMapper::Transaction.new(DataMapper.repository(@name))
+ transaction.commit do
+ status, headers, response = @app.call(env)
+ raise Rollback if status >= 400 or status < 200
+ end
+ rescue Rollback
+ # ignore, needed to trigger the rollback on the transaction
+ end
+ [status, headers, response]
+ end
+ end
+end
Oops, something went wrong.

0 comments on commit e0cee5c

Please sign in to comment.