camping 2.0 → 2.1

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/CHANGELOG CHANGED
@@ -1,3 +1,14 @@
1
+ = 2.1
2
+ === 19th Aug, 2010 (whyday)
3
+ * Helpers#R now calls to_param on any object it passes in
4
+ * Fix route generation issue with routes including "." (#22)
5
+ * Improved tests
6
+ * Improved 1.9 support
7
+ * Camping::Server is now built upon Rack::Server
8
+ * Add support for ERB, Haml etc through Tilt
9
+ * Introducing Camping.options and Camping#set
10
+ * Camping::Server only loads ActiveRecord when needed
11
+
1
12
  = 2.0
2
13
  === 9th Apr, 2010
3
14
  * Speed-up of Camping::Mab (thanks zimbatm!)
data/Rakefile CHANGED
@@ -10,7 +10,7 @@ task :default => :check
10
10
 
11
11
  ## Constants
12
12
  NAME = "camping"
13
- BRANCH = "2.0"
13
+ BRANCH = "2.1"
14
14
  GIT = ENV['GIT'] || "git"
15
15
  REV = `#{GIT} rev-list HEAD`.strip.split.length
16
16
  VERS = ENV['VERSION'] || (REV.zero? ? BRANCH : [BRANCH, REV] * '.')
@@ -60,6 +60,7 @@ omni =
60
60
  s.add_dependency('sqlite3-ruby', '>=1.1.0.1')
61
61
  s.add_dependency('mongrel')
62
62
  s.add_dependency('RedCloth')
63
+ s.add_dependency('markaby')
63
64
  end
64
65
 
65
66
  ## RDoc
@@ -123,7 +124,8 @@ end
123
124
 
124
125
  ## Tests
125
126
  Rake::TestTask.new(:test) do |t|
126
- t.test_files = FileList['test/test_*.rb']
127
+ t.libs << "test"
128
+ t.test_files = FileList['test/app_*.rb']
127
129
  # t.warning = true
128
130
  # t.verbose = true
129
131
  end
@@ -139,14 +141,19 @@ task :diff do
139
141
  u << Ruby2Ruby.new.process(RubyParser.new.parse(File.read("lib/camping.rb")))
140
142
  m << Ruby2Ruby.new.process(RubyParser.new.parse(File.read("lib/camping-unabridged.rb")))
141
143
 
144
+ u.flush
145
+ m.flush
146
+
142
147
  sh "diff -u #{u.path} #{m.path} | less"
143
148
 
144
149
  u.delete
145
150
  m.delete
146
151
  end
147
152
 
153
+ error = false
154
+
148
155
  ## Check
149
- task :check => ["check:valid", "check:size", "check:lines"]
156
+ task :check => ["test", "check:valid", "check:size", "check:lines", "check:exit"]
150
157
  namespace :check do
151
158
 
152
159
  desc "Check source code validity"
@@ -155,8 +162,13 @@ namespace :check do
155
162
  u = RubyParser.new.parse(File.read("lib/camping-unabridged.rb"))
156
163
  m = RubyParser.new.parse(File.read("lib/camping.rb"))
157
164
 
165
+ u.reject! do |sexp|
166
+ sexp.is_a?(Sexp) and sexp[1] == s(:gvar, :$LOADED_FEATURES)
167
+ end
168
+
158
169
  unless u == m
159
170
  STDERR.puts "camping.rb and camping-unabridged.rb are not synchronized."
171
+ error = true
160
172
  end
161
173
  end
162
174
 
@@ -169,6 +181,7 @@ namespace :check do
169
181
  end
170
182
  if File.size("lib/camping.rb") > SIZE_LIMIT
171
183
  STDERR.puts "lib/camping.rb: file is too big (> #{SIZE_LIMIT})"
184
+ error = true
172
185
  end
173
186
  end
174
187
 
@@ -177,10 +190,15 @@ namespace :check do
177
190
  i = 1
178
191
  File.open("lib/camping.rb").each_line do |line|
179
192
  if line.size > 81 # 1 added for \n
193
+ error = true
180
194
  STDERR.puts "lib/camping.rb:#{i}: line too long (#{line[-10..-1].inspect})"
181
195
  end
182
196
  i += 1
183
197
  end
184
198
  end
199
+
200
+ task :exit do
201
+ exit 1 if error
202
+ end
185
203
 
186
204
  end
@@ -1,97 +1,14 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- trap("INT") { exit }
4
- require 'optparse'
5
- require 'ostruct'
6
- require 'stringio'
7
- require 'yaml'
8
-
9
3
  $:.unshift File.dirname(__FILE__) + "/../lib"
4
+
10
5
  require 'camping'
11
6
  require 'camping/server'
12
7
 
13
- conf = OpenStruct.new(:host => '0.0.0.0', :port => 3301)
14
-
15
- # Setup paths
16
- if home = ENV['HOME'] # POSIX
17
- db_path = File.join(home, '.camping.db')
18
- rc_path = File.join(home, '.campingrc')
19
- elsif home = ENV['APPDATA'] # MSWIN
20
- db_path = File.join(home, 'Camping.db')
21
- rc_path = File.join(home, 'Campingrc')
22
- end
23
-
24
- # Parse options
25
- opts = OptionParser.new do |opts|
26
- opts.banner = "Usage: camping app1.rb, app2.rb..."
27
- opts.define_head "#{File.basename($0)}, the microframework ON-button for ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
28
- opts.separator ""
29
- opts.separator "Specific options:"
30
-
31
- opts.on("-h", "--host HOSTNAME", "Host for web server to bind to (default is all IPs)") { |val| conf.host = val }
32
- opts.on("-p", "--port NUM", "Port for web server (defaults to #{conf.port})") { |val| conf.port = val }
33
- opts.on("-d", "--database FILE", "SQLite3 database path (defaults to #{db_path ? db_path : '<none>'})") { |db_path| conf.database = {:adapter => 'sqlite3', :database => db_path} }
34
- opts.on("-C", "--console", "Run in console mode with IRB") { conf.server = "console" }
35
- server_list = ["mongrel", "webrick", "console"]
36
- opts.on("-s", "--server NAME", server_list, "Server to force (#{server_list.join(', ')})") { |val| conf.server = val }
37
-
38
- opts.separator ""
39
- opts.separator "Common options:"
40
-
41
- # No argument, shows at tail. This will print an options summary.
42
- # Try it and see!
43
- opts.on_tail("-?", "--help", "Show this message") do
44
- puts opts
45
- exit
46
- end
47
-
48
- # Another typical switch to print the version.
49
- opts.on_tail("-v", "--version", "Show version") do
50
- puts Gem.loaded_specs['camping'].version
51
- exit
52
- end
53
- end
54
-
55
8
  begin
56
- opts.parse! ARGV
9
+ Camping::Server.start
57
10
  rescue OptionParser::ParseError => ex
58
11
  STDERR.puts "!! #{ex.message}"
59
12
  puts "** use `#{File.basename($0)} --help` for more details..."
60
13
  exit 1
61
14
  end
62
-
63
- if ARGV.length < 1
64
- puts opts
65
- exit 1
66
- end
67
-
68
- # Load configuration if any
69
- if rc_path and File.exists?(rc_path)
70
- YAML.load_file(rc_path).each do |k,v|
71
- conf.send("#{k}=", v) unless conf.send(k)
72
- end
73
- puts "** conf file #{rc_path} loaded"
74
- end
75
-
76
- # Default db
77
- if conf.database.nil? and db_path
78
- conf.database = { :adapter => 'sqlite3', :database => db_path }
79
- end
80
-
81
-
82
- # get a copy of the paths to pass to the server
83
- paths = ARGV.dup
84
-
85
- # Check that mongrel exists
86
- if conf.server.nil? || conf.server == "mongrel"
87
- begin
88
- require 'mongrel'
89
- conf.server = "mongrel"
90
- rescue LoadError
91
- puts "!! Could not load mongrel. Falling back to webrick."
92
- conf.server = "webrick"
93
- end
94
- end
95
-
96
- server = Camping::Server.new(conf, paths)
97
- server.start
@@ -1,5 +1,22 @@
1
1
  = Appendix I: Upgrade Notes
2
2
 
3
+ This document includes everything needed in order to *upgrade* your
4
+ applications. If you're looking for all the new features in a version, please
5
+ have a look at the CHANGELOG in the Camping source.
6
+
7
+
8
+ == From 2.0 to 2.1
9
+ === Options
10
+
11
+ In Camping 2.1 there is now a built-in way to store options and settings. If
12
+ you use cookie session, it means that you'll now have to change to:
13
+
14
+ module Nuts
15
+ set :secret, "Very secret text, which no-one else should know!"
16
+ include Camping::Session
17
+ end
18
+
19
+
3
20
  == From 1.5 to 2.0
4
21
  === Rack
5
22
 
@@ -12,6 +12,8 @@
12
12
  require "uri"
13
13
  require "rack"
14
14
 
15
+ $LOADED_FEATURES << "camping.rb"
16
+
15
17
  class Object #:nodoc:
16
18
  def meta_def(m,&b) #:nodoc:
17
19
  (class<<self;self end).send(:define_method,m,&b)
@@ -46,6 +48,7 @@ module Camping
46
48
  S = IO.read(__FILE__) rescue nil
47
49
  P = "<h1>Cam\ping Problem!</h1><h2>%s</h2>"
48
50
  U = Rack::Utils
51
+ O = {}
49
52
  Apps = []
50
53
  # An object-like Hash.
51
54
  # All Camping query string and cookie variables are loaded as this.
@@ -183,7 +186,7 @@ module Camping
183
186
  raise "bad route" unless u = c.urls.find{|x|
184
187
  break x if x.scan(p).size == g.size &&
185
188
  /^#{x}\/?$/ =~ (x=g.inject(x){|x,a|
186
- x.sub p,U.escape((a[a.class.primary_key]rescue a))})
189
+ x.sub p,U.escape((a.to_param rescue a))}.gsub(/\\(.)/){$1})
187
190
  }
188
191
  h.any?? u+"?"+U.build_query(h[0]) : u
189
192
  end
@@ -195,7 +198,7 @@ module Camping
195
198
  # self / "styles.css" #=> "styles.css"
196
199
  # self / R(Edit, 1) #=> "/blog/edit/1"
197
200
  #
198
- def /(p); p[0]==?/?@root+p:p end
201
+ def /(p); p[0] == ?/ ? @root + p : p end
199
202
 
200
203
  # Builds a URL route to a controller or a path, returning a URI object.
201
204
  # This way you'll get the hostname and the port number, a complete URL.
@@ -235,7 +238,25 @@ module Camping
235
238
  module Base
236
239
  attr_accessor :env, :request, :root, :input, :cookies, :state,
237
240
  :status, :headers, :body
238
-
241
+
242
+ T = {}
243
+ L = :layout
244
+
245
+ # Finds a template, returning either:
246
+ #
247
+ # false # => Could not find template
248
+ # true # => Found template in Views
249
+ # instance of Tilt # => Found template in a file
250
+ def lookup(n)
251
+ T.fetch(n.to_sym) do |k|
252
+ t = Views.method_defined?(k) ||
253
+ (f = Dir[[O[:views] || "views", "#{n}.*"]*'/'][0]) &&
254
+ Template.new(f, O[f[/\.(\w+)$/, 1].to_sym] || {})
255
+
256
+ O[:dynamic_templates] ? t : T[k] = t
257
+ end
258
+ end
259
+
239
260
  # Display a view, calling it by its method name +v+. If a <tt>layout</tt>
240
261
  # method is found in Camping::Views, it will be used to wrap the HTML.
241
262
  #
@@ -248,8 +269,15 @@ module Camping
248
269
  # end
249
270
  # end
250
271
  #
251
- def render(v,*a,&b)
252
- mab(/^_/!~v.to_s){send(v,*a,&b)}
272
+ def render(v, *a, &b)
273
+ if t = lookup(v)
274
+ o = Hash === a[-1] ? a.pop : {}
275
+ s = (t == true) ? mab{ send(v, *a, &b) } : t.render(self, o[:locals] || {}, &b)
276
+ s = render(L, o.merge(L => false)) { s } if v.to_s[0] != ?_ && o[L] != false && lookup(L)
277
+ s
278
+ else
279
+ raise "Can't find template #{v}"
280
+ end
253
281
  end
254
282
 
255
283
  # You can directly return HTML form your controller for quick debugging
@@ -262,11 +290,8 @@ module Camping
262
290
  # end
263
291
  #
264
292
  # You can also pass true to use the :layout HTML wrapping method
265
- def mab(l=nil,&b)
266
- m=Mab.new({},self)
267
- s=m.capture(&b)
268
- s=m.capture{layout{s}} if l && m.respond_to?(:layout)
269
- s
293
+ def mab(&b)
294
+ (@mab ||= Mab.new({},self)).capture(&b)
270
295
  end
271
296
 
272
297
  # A quick means of setting this controller's status, body and headers
@@ -355,7 +380,7 @@ module Camping
355
380
  # end
356
381
  # end
357
382
  def to_a
358
- @env['rack.session'] = @state
383
+ @env['rack.session'] = Hash[@state]
359
384
  r = Rack::Response.new(@body, @status, @headers)
360
385
  @cookies.each do |k, v|
361
386
  next if @old_cookies[k] == v
@@ -517,11 +542,11 @@ module Camping
517
542
  # * Classes with routes are searched in order of their creation.
518
543
  #
519
544
  # So, define your catch-all controllers last.
520
- def D(p, m)
545
+ def D(p, m, e)
521
546
  p = '/' if !p || !p[0]
522
547
  r.map { |k|
523
548
  k.urls.map { |x|
524
- return (k.instance_method(m) rescue nil) ?
549
+ return (k.method_defined?(m)) ?
525
550
  [k, m, *$~[1..-1]] : [I, 'r501', m] if p =~ /^#{x}\/?$/
526
551
  }
527
552
  }
@@ -547,9 +572,9 @@ module Camping
547
572
  end
548
573
  constants.map { |c|
549
574
  k = const_get(c)
550
- k.send :include,C,Base,Helpers,Models
575
+ k.send :include,C,X,Base,Helpers,Models
551
576
  @r=[k]+r if r-[k]==r
552
- k.meta_def(:urls){["/#{c.scan(/.[^A-Z]*/).map(&N.method(:[]))*'/'}"]}if !k.respond_to?:urls
577
+ k.meta_def(:urls){["/#{c.to_s.scan(/.[^A-Z]*/).map(&N.method(:[]))*'/'}"]}if !k.respond_to?:urls
553
578
  }
554
579
  end
555
580
  end
@@ -584,7 +609,7 @@ module Camping
584
609
  def call(e)
585
610
  X.M
586
611
  p = e['PATH_INFO'] = U.unescape(e['PATH_INFO'])
587
- k,m,*a=X.D p,e['REQUEST_METHOD'].downcase
612
+ k,m,*a=X.D p,e['REQUEST_METHOD'].downcase,e
588
613
  k.new(e,m).service(*a).to_a
589
614
  rescue
590
615
  r500(:I, k, m, $!, :env => e).to_a
@@ -627,6 +652,20 @@ module Camping
627
652
  m = a.shift.new(method(:call), *a, &b)
628
653
  meta_def(:call) { |e| m.call(e) }
629
654
  end
655
+
656
+ # A hash where you can set different settings.
657
+ def options
658
+ O
659
+ end
660
+
661
+ # Shortcut for setting options:
662
+ #
663
+ # module Blog
664
+ # set :secret, "Hello!"
665
+ # end
666
+ def set(k, v)
667
+ O[k] = v
668
+ end
630
669
  end
631
670
 
632
671
  # Views is an empty module for storing methods which create HTML. The HTML
@@ -696,6 +735,7 @@ module Camping
696
735
  end
697
736
 
698
737
  autoload :Mab, 'camping/mab'
738
+ autoload :Template, 'camping/template'
699
739
  C
700
740
  end
701
741
 
@@ -1,40 +1,45 @@
1
1
  require "uri";require "rack";class Object;def meta_def m,&b;(class<<self;self
2
2
  end).send:define_method,m,&b end end;module Camping;C=self;S=IO.read(__FILE__
3
- )rescue nil;P="<h1>Cam\ping Problem!</h1><h2>%s</h2>";U=Rack::Utils;Apps=[]
3
+ )rescue nil;P="<h1>Cam\ping Problem!</h1><h2>%s</h2>";U=Rack::Utils;O={};Apps=[]
4
4
  class H<Hash;def method_missing m,*a;m.to_s=~/=$/?self[$`]=a[0]:a==[]?self[m.
5
5
  to_s]:super end;undef id,type if ??==63;end;module Helpers;def R c,*g;p,h=
6
6
  /\(.+?\)/,g.grep(Hash);g-=h;raise"bad route"unless u=c.urls.find{|x|break x if
7
- x.scan(p).size==g.size&&/^#{x}\/?$/=~(x=g.inject(x){|x,a|x.sub p,U.escape((a[
8
- a.class.primary_key]rescue a))})};h.any?? u+"?"+U.build_query(h[0]):u end;def
7
+ x.scan(p).size==g.size&&/^#{x}\/?$/=~(x=g.inject(x){|x,a|x.sub p,U.escape((a.
8
+ to_param rescue a))}.gsub(/\\(.)/){$1})};h.any?? u+"?"+U.build_query(h[0]):u end;def
9
9
  / p;p[0]==?/?@root + p : p end;def URL c='/',*a;c=R(c, *a) if c.respond_to?(
10
10
  :urls);c=self/c;c=@request.url[/.{8,}?(?=\/)/]+c if c[0]==?/;URI c end end
11
11
  module Base;attr_accessor:env,:request,:root,:input,:cookies,:state,:status,
12
- :headers,:body;def render v,*a,&b;mab(/^_/!~v.to_s){send(v,*a,&b)} end;def
13
- mab l=nil,&b;m=Mab.new({},self);s=m.capture(&b);s=m.capture{layout{s}} if l &&
14
- m.respond_to?(:layout);s end;def r s,b,h={};b,h=h,b if Hash===b;@status=s;
15
- @headers.merge!(h);@body=b;end;def redirect *a;r 302,'','Location'=>URL(*a).
16
- to_s;end;def r404 p;P%"#{p} not found"end;def r500 k,m,e;raise e;end;def r501 m
17
- P%"#{m.upcase} not implemented"end;def to_a;@env['rack.session']=@state;r=Rack::
18
- Response.new(@body,@status,@headers);@cookies.each{|k,v|next if @old_cookies[
19
- k]==v;v={:value=>v,:path=>self/"/"} if String===v;r.set_cookie(k,v)};r.to_a;end
20
- def initialize(env,m) r=@request=Rack::Request.new(@env=env);@root,@input,
21
- @cookies,@state,@headers,@status,@method=r.script_name.sub(/\/$/,''),n(r.params
22
- ),H[@old_cookies = r.cookies],H[r.session],{},m=~/r(\d+)/?$1.to_i: 200,m end;def
23
- n h;Hash===h ?h.inject(H[]){|m,(k,v)|m[k]=n(v);m}: h end;def service *a;r=catch(
24
- :halt){send(@method,*a)};@body||=r;self;end;end;module Controllers;@r=[];class<<
25
- self;def r;@r end;def R *u;r=@r;Class.new{meta_def(:urls){u};meta_def(:inherited
26
- ){|x|r<<x}}end;def D p,m;p='/'if !p||!p[0];r.map{|k|k.urls.map{|x|return(k.
27
- instance_method(m)rescue nil)?[k,m,*$~[1..-1]]:[I,'r501',m]if p=~/^#{x}\/?$/}}
28
- [I,'r404',p] end;N=H.new{|_,x|x.downcase}.merge! "N"=>'(\d+)',"X"=>'([^/]+)',
29
- "Index"=>'';def M;def M;end;constants.map{|c|k=const_get(c);k.send:include,C,
30
- Base,Helpers,Models;@r=[k]+r if r-[k]==r;k.meta_def(:urls){ [ "/#{c.scan(
31
- /.[^A-Z]*/).map(&N.method(:[]))*'/'}"]}if !k.respond_to?:urls}end end;I=R()
32
- end;X=Controllers;class<<self;def goes m;Apps<<eval(S.gsub(/Camping/,m.to_s),
33
- TOPLEVEL_BINDING) end;def call e;X.M;p=e['PATH_INFO']=U.unescape(e['PATH_INFO'])
34
- k,m,*a=X.D p,e['REQUEST_METHOD'].downcase;k.new(e,m).service(*a).to_a;rescue
35
- r500(:I,k,m,$!,:env=>e).to_a;end;def method_missing m,c,*a;X.M;h=Hash===a[-1]?
36
- a.pop: {};e=H[Rack::MockRequest.env_for('',h.delete(:env)||{})];k=X.const_get(c
37
- ).new(e,m.to_s);h.each{|i,v|k.send"#{i}=",v};k.service(*a);end;def use*a,&b;m=a.
38
- shift.new(method(:call),*a,&b);meta_def(:call){|e|m.call(e)}end end;module Views
39
- include X,Helpers end;module Models;autoload:Base,
40
- 'camping/ar';end;autoload:Mab,'camping/mab';C end
12
+ :headers,:body;T={};L=:layout;def lookup n;T.fetch(n.to_sym){|k|t=Views.
13
+ method_defined?(k)||(f=Dir[[O[:views]||"views","#{n}.*"]*'/'][0])&&Template.
14
+ new(f,O[f[/\.(\w+)$/,1].to_sym]||{});O[:dynamic_templates]?t:T[k]=t} end
15
+ def render(v,*a,&b)if t=lookup(v);o=Hash===a[-1]?a.pop: {};s=(t==true)?mab{
16
+ send v,*a,&b}: t.render(self,o[:locals]||{},&b);s=render(L,o.merge(L=>false)){s
17
+ }if v.to_s[0]!=?_&&o[L]!=false&&lookup(L);s;else;raise"Can't find template #{v}"end
18
+ end;def mab &b;(@mab||=Mab.new({},self)).capture(&b) end;def r s,b,h={};b,h=h,
19
+ b if Hash===b;@status=s;@headers.merge!(h);@body=b;end;def redirect *a;r 302,'',
20
+ 'Location'=>URL(*a).to_s;end;def r404 p;P%"#{p} not found"end;def r500 k,m,e
21
+ raise e;end;def r501 m;P%"#{m.upcase} not implemented"end;def to_a;@env[
22
+ 'rack.session']=Hash[@state];r=Rack::Response.new(@body,@status,@headers)
23
+ @cookies.each{|k,v|next if @old_cookies[k]==v;v={:value=>v,:path=>self/"/"} if
24
+ String===v;r.set_cookie(k,v)};r.to_a;end;def initialize(env,m) r=@request=Rack::
25
+ Request.new(@env=env);@root,@input,@cookies,@state,@headers,@status,@method=r.
26
+ script_name.sub(/\/$/,''),n(r.params),H[@old_cookies = r.cookies],H[r.session],
27
+ {},m=~/r(\d+)/?$1.to_i: 200,m end;def n h;Hash===h ?h.inject(H[]){|m,(k,v)|m[k]=
28
+ n(v);m}: h end;def service *a;r=catch(:halt){send(@method,*a)};@body||=r;self
29
+ end;end;module Controllers;@r=[];class<<self;def r;@r end;def R *u;r=@r;Class.
30
+ new{meta_def(:urls){u};meta_def(:inherited){|x|r<<x}}end;def D p,m,e;p='/'if !p||
31
+ !p[0];r.map{|k|k.urls.map{|x|return(k.method_defined? m)?[k,m,*$~[1..-1]]:[I,
32
+ 'r501',m]if p=~/^#{x}\/?$/}};[I,'r404',p] end;N=H.new{|_,x|x.downcase}.merge!(
33
+ "N"=>'(\d+)',"X"=>'([^/]+)',"Index"=>'');def M;def M;end;constants.map{|c|k=
34
+ const_get(c);k.send:include,C,X,Base,Helpers,Models;@r=[k]+r if r-[k]==r
35
+ k.meta_def(:urls){ [ "/#{c.to_s.scan(/.[^A-Z]*/).map(&N.method(:[]))*'/'}"]}if !k.
36
+ respond_to?:urls}end end;I=R();end;X=Controllers;class<<self;def goes m;Apps<<
37
+ eval(S.gsub(/Camping/,m.to_s),TOPLEVEL_BINDING) end;def call e;X.M
38
+ p=e['PATH_INFO']=U.unescape(e['PATH_INFO']);k,m,*a=X.D p,e['REQUEST_METHOD'].
39
+ downcase,e;k.new(e,m).service(*a).to_a;rescue;r500(:I,k,m,$!,:env=>e).to_a;end
40
+ def method_missing m,c,*a;X.M;h=Hash===a[-1]?a.pop: {};e=H[Rack::MockRequest.
41
+ env_for('',h.delete(:env)||{})];k=X.const_get(c).new(e,m.to_s);h.each{|i,v|k.
42
+ send"#{i}=",v};k.service(*a);end;def use*a,&b;m=a.shift.new(method(:call),*a,&b)
43
+ meta_def(:call){|e|m.call(e)}end;def options;O end;def set k,v;O[k]=v end end
44
+ module Views;include X,Helpers end;module Models;autoload:Base,'camping/ar';end
45
+ autoload:Mab,'camping/mab';autoload:Template,'camping/template';C end