camping 2.0 → 2.1

Sign up to get free protection for your applications and to get access to all the features.
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