picnic 0.5.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.
@@ -0,0 +1,55 @@
1
+ %w[active_support markaby tempfile uri].map{|l|require l}
2
+ module Camping;Apps=[];C=self;S=IO.read(__FILE__).sub(/S=I.+$/,'')
3
+ P="Cam\ping Problem!";module Helpers;def R(c,*g);p,h=/\(.+?\)/,g.grep(Hash)
4
+ (g-=h).inject(c.urls.find{|x|x.scan(p).size==g.size}.dup){|s,a|s.sub p,C.
5
+ escape((a[a.class.primary_key]rescue a))}+(h.any?? "?"+h[0].map{|x|x.map{|z|C.
6
+ escape z}*"="}*"&": "")end;def URL c='/',*a;c=R(c,*a)if c.
7
+ respond_to?:urls;c=self/c;c="//"+@env.HTTP_HOST+c if c[/^\//];URI(c)end;def/p
8
+ p[/^\//]?@root+p : p end;def errors_for o;ul.errors{o.errors.each_full{|x|li x}
9
+ }if o.errors.any?end end;module Base;include Helpers;attr_accessor:input,
10
+ :cookies,:env,:headers,:body,:status,:root;def method_missing*a,&b
11
+ a.shift if a[0]==:render;m=Mab.new({},self);s=m.capture{send(*a,&b)}
12
+ s=m.capture{send(:layout){s}} if /^_/!~a[0].to_s and m.respond_to?:layout
13
+ s end;def r s,b,h={};@status=s;@headers.merge!h;@body=b end
14
+ def redirect*a;r 302,'','Location'=>URL(*a)end;Z="\r\n"
15
+ def to_a;[@status,@body,@headers]end
16
+ def initialize r,e,m;e=H[e.to_hash];@status,@method,@env,@headers,@root=200,m.
17
+ downcase,e,{'Content-Type'=>"text/html"},e.SCRIPT_NAME.sub(/\/$/,'')
18
+ @k=C.kp e.HTTP_COOKIE;q=C.qsp e.QUERY_STRING;@in=r
19
+ if%r|\Amultipart/form-.*boundary=\"?([^\";,]+)|n.match e.CONTENT_TYPE
20
+ b=/(?:\r?\n|\A)#{Regexp::quote("--#$1")}(?:--)?\r$/;until@in.eof?;fh=H[];for l in@in
21
+ case l;when Z;break;when/^Content-D.+?: form-data;/;fh.u H[*$'.
22
+ scan(/(?:\s(\w+)="([^"]+)")/).flatten];when/^Content-Type: (.+?)(\r$|\Z)/m;fh[
23
+ :type]=$1;end;end;fn=fh[:name];o=if fh[:filename];o=fh[:tempfile]=Tempfile.new(:C)
24
+ o.binmode;else;fh=""end;while l=@in.read(16384);if l=~b;o<<$`.chomp;@in.seek(-$'.
25
+ size,IO::SEEK_CUR);break;end;o<<l;end;C.qsp(fn,'&;',fh,q) if fn;fh[:tempfile].rewind if
26
+ fh.is_a?H;end;elsif@method=="post";q.u C.qsp(@in.read)end;@cookies,@input=
27
+ @k.dup,q.dup end;def service*a;@body=send(@method,*a)if respond_to?@method
28
+ @headers["Set-Cookie"]=@cookies.map{|k,v|"#{k}=#{C.escape(v)}; path=#{self/'/'
29
+ }"if v!=@k[k]}-[nil];self end;def to_s;a=[];@headers.map{|k,v|[*v].map{|x|a<<
30
+ "#{k}: #{x}"}};"Status: #{@status}#{Z+a*Z+Z*2+@body}"end;end
31
+ X=module Controllers;@r=[];class<<self;def r;@r;end;def R*u;r=@r;Class.new{
32
+ meta_def(:urls){u};meta_def(:inherited){|x|r<<x}}end;def M;def M;end;constants.map{|c|
33
+ k=const_get(c);k.send:include,C,Base,Models;r[0,0]=k if !r.include?k;k.meta_def(
34
+ :urls){["/#{c.downcase}"]}if !k.respond_to?:urls}end;def D p;r.map{|k|k.urls.
35
+ map{|x|return k,$~[1..-1]if p=~/^#{x}\/?$/}};[NotFound,[p]]end end;class
36
+ NotFound<R();def get p;r(404,Mab.new{h1 P;h2 p+" not found"})end end;class
37
+ ServerError<R();def get k,m,e;r(500,Mab.new{h1 P;h2"#{k}.#{m}";h3"#{e.class
38
+ } #{e.message}:";ul{e.backtrace.each{|bt|li(bt)}}}.to_s)end end;self;end;class<<
39
+ self;def goes m;eval S.gsub(/Camping/,m.to_s).gsub("A\pps=[]","Cam\ping::Apps<<\
40
+ self"),TOPLEVEL_BINDING;end;def escape s;s.to_s.gsub(/[^ \w.-]+/n){'%'+($&.
41
+ unpack('H2'*$&.size)*'%').upcase}.tr(' ','+')end;def un s;s.tr('+',' ').gsub(
42
+ /%([\da-f]{2})/in){[$1].pack('H*')}end;def qsp q,d='&;',y=nil,z=H[];m=proc{|_,o,n|o.u(
43
+ n,&m)rescue([*o]<<n)};q.to_s.split(/[#{d}] */n).inject((b,z=z,H[])[0]){|h,p|k,v=un(p).
44
+ split('=',2);h.u k.split(/[\]\[]+/).reverse.inject(y||v){|x,i|H[i,x]},&m}end;def
45
+ kp s;c=qsp(s,';,')end;def run r=$stdin,e=ENV;X.M;k,a=X.D un("/#{e[
46
+ 'PATH_INFO']}".gsub(/\/+/,'/'));k.new(r,e,(m=e['REQUEST_METHOD']||"GET")).Y.
47
+ service *a;rescue Object=>x;X::ServerError.new(r,e,'get').service(k,m,x)end
48
+ def method_missing m,c,*a;X.M;k=X.const_get(c).new(StringIO.new,H['HTTP_HOST',
49
+ '','SCRIPT_NAME','','HTTP_COOKIE',''],m.to_s);H.new(a.pop).each{|e,f|k.send(
50
+ "#{e}=",f)}if Hash===a[-1];k.service *a;end;end;module Views;include X,Helpers
51
+ end;module Models;autoload:Base,'camping/db';def Y;self;end;end;class Mab<Markaby::Builder
52
+ include Views;def tag!*g,&b;h=g[-1];[:href,:action,:src].map{|a|(h[a]=self/h[a])rescue
53
+ 0};super end end;H=HashWithIndifferentAccess;class H;def method_missing m,*a
54
+ m.to_s=~/=$/?self[$`]=a[0]:a==[]?self[m]:raise(NoMethodError,"#{m}")end
55
+ alias_method:u,:regular_update;end end
@@ -0,0 +1,78 @@
1
+ class MissingLibrary < Exception #:nodoc: all
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 = %{
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
+
50
+ module Camping
51
+ module Models
52
+ A = ActiveRecord
53
+ # Base is an alias for ActiveRecord::Base. The big warning I'm going to give you
54
+ # about this: *Base overloads table_name_prefix.* This means that if you have a
55
+ # model class Blog::Models::Post, it's table name will be <tt>blog_posts</tt>.
56
+ #
57
+ # ActiveRecord is not loaded if you never reference this class. The minute you
58
+ # use the ActiveRecord or Camping::Models::Base class, then the ActiveRecord library
59
+ # is loaded.
60
+ Base = A::Base
61
+
62
+ # The default prefix for Camping model classes is the topmost module name lowercase
63
+ # and followed with an underscore.
64
+ #
65
+ # Tepee::Models::Page.table_name_prefix
66
+ # #=> "tepee_pages"
67
+ #
68
+ def Base.table_name_prefix
69
+ "#{name[/\w+/]}_".downcase.sub(/^(#{A}|camping)_/i,'')
70
+ end
71
+ module_eval $AR_EXTRAS
72
+ end
73
+ end
74
+ Camping::S.sub! "autoload:Base,'camping/db'", ""
75
+ Camping::S.sub! "def Y;self;end", $AR_EXTRAS
76
+ Camping::Apps.each do |app|
77
+ app::Models.module_eval $AR_EXTRAS
78
+ end
@@ -0,0 +1,244 @@
1
+ # == About camping/fastcgi.rb
2
+ #
3
+ # Camping works very well with FastCGI, since your application is only loaded
4
+ # once -- when FastCGI starts. In addition, this class lets you mount several
5
+ # Camping apps under a single FastCGI process, to help save memory costs.
6
+ #
7
+ # So where do you use the Camping::FastCGI class? Use it in your application's
8
+ # postamble and then you can point your web server directly at your application.
9
+ # See Camping::FastCGI docs for more.
10
+ require 'camping'
11
+ require 'fcgi'
12
+
13
+ module Camping
14
+ # Camping::FastCGI is a small class for hooking one or more Camping apps up to
15
+ # FastCGI. Generally, you'll use this class in your application's postamble.
16
+ #
17
+ # == The Smallest Example
18
+ #
19
+ # if __FILE__ == $0
20
+ # require 'camping/fastcgi'
21
+ # Camping::FastCGI.start(YourApp)
22
+ # end
23
+ #
24
+ # This example is stripped down to the basics. The postamble has no database
25
+ # connection. It just loads this class and calls Camping::FastCGI.start.
26
+ #
27
+ # Now, in Lighttpd or Apache, you can point to your app's file, which will
28
+ # be executed, only to discover that your app now speaks the FastCGI protocol.
29
+ #
30
+ # Here's a sample lighttpd.conf (tested with Lighttpd 1.4.11) to serve as example:
31
+ #
32
+ # server.port = 3044
33
+ # server.bind = "127.0.0.1"
34
+ # server.modules = ( "mod_fastcgi" )
35
+ # server.document-root = "/var/www/camping/blog/"
36
+ # server.errorlog = "/var/www/camping/blog/error.log"
37
+ #
38
+ # #### fastcgi module
39
+ # fastcgi.server = ( "/" => (
40
+ # "localhost" => (
41
+ # "socket" => "/tmp/camping-blog.socket",
42
+ # "bin-path" => "/var/www/camping/blog/blog.rb",
43
+ # "check-local" => "disable",
44
+ # "max-procs" => 1 ) ) )
45
+ #
46
+ # The file <tt>/var/www/camping/blog/blog.rb</tt> is the Camping app with
47
+ # the postamble.
48
+ #
49
+ # == Mounting Many Apps
50
+ #
51
+ # require 'camping/fastcgi'
52
+ # fast = Camping::FastCGI.new
53
+ # fast.mount("/blog", Blog)
54
+ # fast.mount("/tepee", Tepee)
55
+ # fast.mount("/", Index)
56
+ # fast.start
57
+ #
58
+ class FastCGI
59
+ CHUNK_SIZE=(4 * 1024)
60
+
61
+ attr_reader :mounts
62
+
63
+ # Creates a Camping::FastCGI class with empty mounts.
64
+ def initialize
65
+ @mounts = {}
66
+ end
67
+ # Mounts a Camping application. The +dir+ being the name of the directory
68
+ # to serve as the application's root. The +app+ is a Camping class.
69
+ def mount(dir, app)
70
+ dir.gsub!(/\/{2,}/, '/')
71
+ dir.gsub!(/\/+$/, '')
72
+ @mounts[dir] = app
73
+ end
74
+
75
+ #
76
+ # Starts the FastCGI main loop.
77
+ def start(&blk)
78
+ FCGI.each do |req|
79
+ camp_do(req, &blk)
80
+ end
81
+ end
82
+
83
+ # A simple single-app starter mechanism
84
+ #
85
+ # Camping::FastCGI.start(Blog)
86
+ #
87
+ def self.start(app)
88
+ cf = Camping::FastCGI.new
89
+ cf.mount("/", app)
90
+ cf.start
91
+ end
92
+
93
+ # Serve an entire directory of Camping apps. (See
94
+ # http://code.whytheluckystiff.net/camping/wiki/TheCampingServer.)
95
+ #
96
+ # Use this method inside your FastCGI dispatcher:
97
+ #
98
+ # #!/usr/local/bin/ruby
99
+ # require 'rubygems'
100
+ # require 'camping/fastcgi'
101
+ # Camping::Models::Base.establish_connection :adapter => 'sqlite3', :database => "/path/to/db"
102
+ # Camping::FastCGI.serve("/home/why/cvs/camping/examples")
103
+ #
104
+ def self.serve(path, index=nil)
105
+ require 'camping/reloader'
106
+ if File.directory? path
107
+ fast = Camping::FastCGI.new
108
+ script_load = proc do |script|
109
+ app = Camping::Reloader.new(script)
110
+ fast.mount("/#{app.mount}", app)
111
+ app
112
+ end
113
+ Dir[File.join(path, '*.rb')].each &script_load
114
+ fast.mount("/", index) if index
115
+
116
+ fast.start do |dir, app|
117
+ Dir[File.join(path, dir, '*.rb')].each do |script|
118
+ smount = "/" + File.basename(script, '.rb')
119
+ script_load[script] unless fast.mounts.has_key? smount
120
+ end
121
+ end
122
+ else
123
+ start(Camping::Reloader.new(path))
124
+ end
125
+ end
126
+
127
+ private
128
+
129
+ def camp_do(req)
130
+ root, path, dir, app = "/"
131
+ if ENV['FORCE_ROOT'] and ENV['FORCE_ROOT'].to_i == 1
132
+ path = req.env['SCRIPT_NAME']
133
+ else
134
+ root = req.env['SCRIPT_NAME']
135
+ path = req.env['PATH_INFO']
136
+ end
137
+
138
+ dir, app = @mounts.max { |a,b| match(path, a[0]) <=> match(path, b[0]) }
139
+ unless dir and app
140
+ dir, app = '/', Camping
141
+ end
142
+ yield dir, app if block_given?
143
+
144
+ req.env['SERVER_SCRIPT_NAME'] = req.env['SCRIPT_NAME']
145
+ req.env['SERVER_PATH_INFO'] = req.env['PATH_INFO']
146
+ req.env['SCRIPT_NAME'] = File.join(root, dir)
147
+ req.env['PATH_INFO'] = path.gsub(/^#{dir}/, '')
148
+
149
+ controller = app.run(SeekStream.new(req.in), req.env)
150
+ sendfile = nil
151
+ headers = {}
152
+ controller.headers.each do |k, v|
153
+ if k =~ /^X-SENDFILE$/i and !ENV['SERVER_X_SENDFILE']
154
+ sendfile = v
155
+ else
156
+ headers[k] = v
157
+ end
158
+ end
159
+
160
+ body = controller.body
161
+ controller.body = ""
162
+ controller.headers = headers
163
+
164
+ req.out << controller.to_s
165
+ if sendfile
166
+ File.open(sendfile, "rb") do |f|
167
+ while chunk = f.read(CHUNK_SIZE) and chunk.length > 0
168
+ req.out << chunk
169
+ end
170
+ end
171
+ elsif body.respond_to? :read
172
+ while chunk = body.read(CHUNK_SIZE) and chunk.length > 0
173
+ req.out << chunk
174
+ end
175
+ body.close if body.respond_to? :close
176
+ else
177
+ req.out << body.to_s
178
+ end
179
+ rescue Exception => e
180
+ req.out << server_error(root, path, exc, req)
181
+ ensure
182
+ req.finish
183
+ end
184
+
185
+ def server_error(root, path, exc, req)
186
+ "Content-Type: text/html\r\n\r\n" +
187
+ "<h1>Camping Problem!</h1>" +
188
+ "<h2><strong>#{root}</strong>#{path}</h2>" +
189
+ "<h3>#{exc.class} #{esc exc.message}</h3>" +
190
+ "<ul>" + exc.backtrace.map { |bt| "<li>#{esc bt}</li>" }.join + "</ul>" +
191
+ "<hr /><p>#{req.env.inspect}</p>"
192
+ end
193
+
194
+ def match(path, mount)
195
+ m = path.match(/^#{Regexp::quote mount}(\/|$)/)
196
+ if m; m.end(0)
197
+ else -1
198
+ end
199
+ end
200
+
201
+ def esc(str)
202
+ str.gsub(/&/n, '&amp;').gsub(/\"/n, '&quot;').gsub(/>/n, '&gt;').gsub(/</n, '&lt;')
203
+ end
204
+
205
+ class SeekStream
206
+ def initialize(stream)
207
+ @last_read = ""
208
+ @stream = stream
209
+ @buffer = ""
210
+ end
211
+ def eof?
212
+ @buffer.empty? && @stream.eof?
213
+ end
214
+ def each
215
+ while true
216
+ pull(1024) until eof? or @buffer.index("\n")
217
+ return nil if eof?
218
+ yield @buffer.slice!(0..(@buffer.index("\n") || -1))
219
+ end
220
+ end
221
+ def pull(len)
222
+ @buffer += @stream.read(len).to_s
223
+ end
224
+ def read(len = 16384)
225
+ pull(len)
226
+ @last_read =
227
+ if eof?
228
+ nil
229
+ else
230
+ @buffer.slice!(0...len)
231
+ end
232
+ end
233
+ def seek(len, typ)
234
+ raise NotImplementedError, "only IO::SEEK_CUR is supported with SeekStream" if typ != IO::SEEK_CUR
235
+ raise NotImplementedError, "only rewinding is supported with SeekStream" if len > 0
236
+ raise NotImplementedError, "rewinding #{-len} past the buffer #{@last_read.size} start not supported with SeekStream" if -len > @last_read.size
237
+ @buffer = @last_read[len..-1] + @buffer
238
+ @last_read = ""
239
+ self
240
+ end
241
+ end
242
+
243
+ end
244
+ end
@@ -0,0 +1,163 @@
1
+ module Camping
2
+ # == The Camping Reloader
3
+ #
4
+ # Camping apps are generally small and predictable. Many Camping apps are
5
+ # contained within a single file. Larger apps are split into a handful of
6
+ # other Ruby libraries within the same directory.
7
+ #
8
+ # Since Camping apps (and their dependencies) are loaded with Ruby's require
9
+ # method, there is a record of them in $LOADED_FEATURES. Which leaves a
10
+ # perfect space for this class to manage auto-reloading an app if any of its
11
+ # immediate dependencies changes.
12
+ #
13
+ # == Wrapping Your Apps
14
+ #
15
+ # Since bin/camping and the Camping::FastCGI class already use the Reloader,
16
+ # you probably don't need to hack it on your own. But, if you're rolling your
17
+ # own situation, here's how.
18
+ #
19
+ # Rather than this:
20
+ #
21
+ # require 'yourapp'
22
+ #
23
+ # Use this:
24
+ #
25
+ # require 'camping/reloader'
26
+ # Camping::Reloader.new('/path/to/yourapp.rb')
27
+ #
28
+ # The reloader will take care of requiring the app and monitoring all files
29
+ # for alterations.
30
+ class Reloader
31
+ attr_accessor :klass, :mtime, :mount, :requires
32
+
33
+ # Creates the reloader, assigns a +script+ to it and initially loads the
34
+ # application. Pass in the full path to the script, otherwise the script
35
+ # will be loaded relative to the current working directory.
36
+ def initialize(script)
37
+ @script = File.expand_path(script)
38
+ @mount = File.basename(script, '.rb')
39
+ @requires = nil
40
+ load_app
41
+ end
42
+
43
+ # Find the application, based on the script name.
44
+ def find_app(title)
45
+ @klass = Object.const_get(Object.constants.grep(/^#{title}$/i)[0]) rescue nil
46
+ end
47
+
48
+ # If the file isn't found, if we need to remove the app from the global
49
+ # namespace, this will be sure to do so and set @klass to nil.
50
+ def remove_app
51
+ Object.send :remove_const, @klass.name if @klass
52
+ @klass = nil
53
+ end
54
+
55
+ # Loads (or reloads) the application. The reloader will take care of calling
56
+ # this for you. You can certainly call it yourself if you feel it's warranted.
57
+ def load_app
58
+ title = File.basename(@script)[/^([\w_]+)/,1].gsub /_/,''
59
+ begin
60
+ all_requires = $LOADED_FEATURES.dup
61
+ load @script
62
+ @requires = ($LOADED_FEATURES - all_requires).select do |req|
63
+ req.index(File.basename(@script) + "/") == 0 || req.index(title + "/") == 0
64
+ end
65
+ rescue Exception => e
66
+ puts "!! trouble loading #{title}: [#{e.class}] #{e.message}"
67
+ puts e.backtrace.join("\n")
68
+ find_app title
69
+ remove_app
70
+ return
71
+ end
72
+
73
+ @mtime = mtime
74
+ find_app title
75
+ unless @klass and @klass.const_defined? :C
76
+ puts "!! trouble loading #{title}: not a Camping app, no #{title.capitalize} module found"
77
+ remove_app
78
+ return
79
+ end
80
+
81
+ Reloader.conditional_connect
82
+ @klass.create if @klass.respond_to? :create
83
+ @klass
84
+ end
85
+
86
+ # The timestamp of the most recently modified app dependency.
87
+ def mtime
88
+ ((@requires || []) + [@script]).map do |fname|
89
+ fname = fname.gsub(/^#{Regexp::quote File.dirname(@script)}\//, '')
90
+ begin
91
+ File.mtime(File.join(File.dirname(@script), fname))
92
+ rescue Errno::ENOENT
93
+ remove_app
94
+ @mtime
95
+ end
96
+ end.max
97
+ end
98
+
99
+ # Conditional reloading of the app. This gets called on each request and
100
+ # only reloads if the modification times on any of the files is updated.
101
+ def reload_app
102
+ return if @klass and @mtime and mtime <= @mtime
103
+
104
+ if @requires
105
+ @requires.each { |req| $LOADED_FEATURES.delete(req) }
106
+ end
107
+ k = @klass
108
+ Object.send :remove_const, k.name if k
109
+ load_app
110
+ end
111
+
112
+ # Conditionally reloads (using reload_app.) Then passes the request through
113
+ # to the wrapped Camping app.
114
+ def run(*a)
115
+ reload_app
116
+ if @klass
117
+ @klass.run(*a)
118
+ else
119
+ Camping.run(*a)
120
+ end
121
+ end
122
+
123
+ # Returns source code for the main script in the application.
124
+ def view_source
125
+ File.read(@script)
126
+ end
127
+
128
+ class << self
129
+ def database=(db)
130
+ @database = db
131
+ end
132
+ def log=(log)
133
+ @log = log
134
+ end
135
+ def conditional_connect
136
+ # If database models are present, `autoload?` will return nil.
137
+ unless Camping::Models.autoload? :Base
138
+ require 'logger'
139
+ require 'camping/session'
140
+ Camping::Models::Base.establish_connection @database if @database
141
+
142
+ case @log
143
+ when Logger
144
+ Camping::Models::Base.logger = @log
145
+ when String
146
+ Camping::Models::Base.logger = Logger.new(@log == "-" ? STDOUT : @log)
147
+ end
148
+
149
+ Camping::Models::Session.create_schema
150
+
151
+ if @database and @database[:adapter] == 'sqlite3'
152
+ begin
153
+ require 'sqlite3_api'
154
+ rescue LoadError
155
+ puts "!! Your SQLite3 adapter isn't a compiled extension."
156
+ abort "!! Please check out http://code.whytheluckystiff.net/camping/wiki/BeAlertWhenOnSqlite3 for tips."
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end