camping 2.1.467 → 2.1.523

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,36 +1,36 @@
1
- = Appendix I: Upgrade Notes
1
+ # Appendix I: Upgrade Notes
2
2
 
3
3
  This document includes everything needed in order to *upgrade* your
4
4
  applications. If you're looking for all the new features in a version, please
5
5
  have a look at the CHANGELOG in the Camping source.
6
6
 
7
7
 
8
- == From 2.0 to 2.1
9
- === Options
8
+ ##From 2.0 to 2.1
9
+ ###Options
10
10
 
11
11
  In Camping 2.1 there is now a built-in way to store options and settings. If
12
12
  you use cookie session, it means that you'll now have to change to:
13
13
 
14
- module Nuts
15
- set :secret, "Very secret text, which no-one else should know!"
16
- include Camping::Session
17
- end
14
+ module Nuts
15
+ set :secret, "Very secret text, which no-one else should know!"
16
+ include Camping::Session
17
+ end
18
18
 
19
19
 
20
- == From 1.5 to 2.0
21
- === Rack
20
+ ##From 1.5 to 2.0
21
+ ###Rack
22
22
 
23
- The biggest change in 2.0 is that it now uses Rack[http://rack.rubyforge.org/]
23
+ The biggest change in 2.0 is that it now uses [Rack](http://rack.rubyforge.org/)
24
24
  internally. This means that you'll now have to deploy Camping differently, but
25
25
  hopefully more easily too. Now every Camping application is also a Rack
26
26
  application, so simply check out the documentation to the server of your
27
27
  choice.
28
28
 
29
- === require 'camping/db'
29
+ ###`require 'camping/db'`
30
30
 
31
31
  In earlier versions of Camping, you loaded database support by:
32
32
 
33
- require 'camping/db'
33
+ require 'camping/db'
34
34
 
35
35
  Actually, this loaded a very thin layer on top of ActiveRecord, and in the
36
36
  future we want to experiment with other libraries. Therefore you should now
@@ -40,48 +40,48 @@ also means you'll have to place your migrations *after* the models.
40
40
  We also encourage you to use <tt>Model.table_name</tt> instead of
41
41
  <tt>:appname_model</tt>, just to make sure it's named correctly.
42
42
 
43
- ## Don't require anything:
44
- # require 'camping/db'
45
-
46
- module Nuts::Models
47
- ## Just inherit Base:
48
- class Page < Base; end
43
+ ## Don't require anything:
44
+ # require 'camping/db'
49
45
 
50
- ## Migrations have to come *after* the models:
51
- class CreateTheBasics < V 0.1
52
- def self.up
53
- create_table Page.table_name do |t|
54
- ...
55
- end
56
- end
46
+ module Nuts::Models
47
+ ## Just inherit Base:
48
+ class Page < Base; end
57
49
 
58
- def self.down
59
- drop_table Page.table_name
50
+ ## Migrations have to come *after* the models:
51
+ class CreateTheBasics < V 0.1
52
+ def self.up
53
+ create_table Page.table_name do |t|
54
+ ...
55
+ end
56
+ end
57
+
58
+ def self.down
59
+ drop_table Page.table_name
60
+ end
60
61
  end
61
62
  end
62
- end
63
63
 
64
- === Cookie Sessions
64
+ ###Cookie Sessions
65
65
 
66
66
  Camping 2.0 now uses a cookie-based session system, which means you now longer
67
67
  need a database in order to use sessions. The disadvantage of this is that
68
68
  you are restricted to only around 4k of data. See below for the changes
69
69
  required, and see Camping::Session more details.
70
70
 
71
- module Nuts
72
- ## Include Camping::Session as before:
73
- include Camping::Session
74
-
75
- ## But also define a secret:
76
- secret "Very secret text, which no-one else should know!"
77
- end
78
-
79
- def Nuts.create
80
- ## And remove the following line:
81
- # Camping::Models::Session.create_schema
82
- end
71
+ module Nuts
72
+ ## Include Camping::Session as before:
73
+ include Camping::Session
74
+
75
+ ## But also define a secret:
76
+ secret "Very secret text, which no-one else should know!"
77
+ end
78
+
79
+ def Nuts.create
80
+ ## And remove the following line:
81
+ # Camping::Models::Session.create_schema
82
+ end
83
83
 
84
- === Error handling
84
+ ###Error handling
85
85
 
86
86
  Camping now uses three methods in order to handle errors. These replaces the
87
87
  old classes NotFound and ServerError.
@@ -93,18 +93,18 @@ old classes NotFound and ServerError.
93
93
 
94
94
  You can override these in your application:
95
95
 
96
- module Nuts
97
- def r404(path)
98
- "Sorry, but I can't find #{path}."
99
- end
100
-
101
- def r501(method)
102
- "Sorry, but I can't respond to #{method}."
103
- end
104
-
105
- def r500(klass, method, ex)
106
- "Sorry, but #{klass}##{method} failed with #{ex}."
96
+ module Nuts
97
+ def r404(path)
98
+ "Sorry, but I can't find #{path}."
99
+ end
100
+
101
+ def r501(method)
102
+ "Sorry, but I can't respond to #{method}."
103
+ end
104
+
105
+ def r500(klass, method, ex)
106
+ "Sorry, but #{klass}##{method} failed with #{ex}."
107
+ end
107
108
  end
108
- end
109
109
 
110
110
  It should be noted that this might change in the future.
@@ -201,7 +201,7 @@ module Camping
201
201
  def R(c,*g)
202
202
  p,h=/\(.+?\)/,g.grep(Hash)
203
203
  g-=h
204
- raise "bad route" unless u = c.urls.find{|x|
204
+ raise "bad route" if !u = c.urls.find{|x|
205
205
  break x if x.scan(p).size == g.size &&
206
206
  /^#{x}\/?$/ =~ (x=g.inject(x){|x,a|
207
207
  x.sub p,U.escape((a.to_param rescue a))}.gsub(/\\(.)/){$1})
@@ -242,7 +242,7 @@ module Camping
242
242
  def URL c='/',*a
243
243
  c = R(c, *a) if c.respond_to? :urls
244
244
  c = self/c
245
- c = @request.url[/.{8,}?(?=\/)/]+c if c[0]==?/
245
+ c = @request.url[/.{8,}?(?=\/|$)/]+c if c[0]==?/
246
246
  URI(c)
247
247
  end
248
248
  end
@@ -291,7 +291,7 @@ module Camping
291
291
  def render(v, *a, &b)
292
292
  if t = lookup(v)
293
293
  r, @_r = @_r, o = Hash === a[-1] ? a.pop : {}
294
- s = (t == true) ? mab{ send(v, *a, &b) } : t.render(self, o[:locals] || {}, &b)
294
+ s = (t == true) ? mab { send(v, *a, &b) } : t.render(self, o[:locals] || {}, &b)
295
295
  s = render(L, o.merge(L => false)) { s } if o[L] or o[L].nil? && lookup(L) && (!r && v.to_s[0] != ?_)
296
296
  s
297
297
  else
@@ -310,7 +310,8 @@ module Camping
310
310
  #
311
311
  # You can also pass true to use the :layout HTML wrapping method
312
312
  def mab(&b)
313
- (@mab ||= Mab.new({},self)).capture(&b)
313
+ extend Mab
314
+ mab(&b)
314
315
  end
315
316
 
316
317
  # A quick means of setting this controller's status, body and headers
@@ -567,7 +568,7 @@ module Camping
567
568
  @r.map { |k|
568
569
  k.urls.map { |x|
569
570
  return (k.method_defined?(m)) ?
570
- [k, m, *$~[1..-1]] : [I, 'r501', m] if p =~ /^#{x}\/?$/
571
+ [k, m, *$~[1..-1].map{|x|U.unescape(x)}] : [I, 'r501', m] if p =~ /^#{x}\/?$/
571
572
  }
572
573
  }
573
574
  [I, 'r404', p]
@@ -632,8 +633,7 @@ module Camping
632
633
  # See: http://rack.rubyforge.org/doc/SPEC.html
633
634
  def call(e)
634
635
  X.M
635
- p = e['PATH_INFO'] = U.unescape(e['PATH_INFO'])
636
- k,m,*a=X.D p,e['REQUEST_METHOD'].downcase,e
636
+ k,m,*a=X.D e['PATH_INFO'],e['REQUEST_METHOD'].downcase,e
637
637
  k.new(e,m).service(*a).to_a
638
638
  rescue
639
639
  r500(:I, k, m, $!, :env => e).to_a
@@ -10,7 +10,7 @@ end;module Helpers;def R c,*g;p,h=
10
10
  x.scan(p).size==g.size&&/^#{x}\/?$/=~(x=g.inject(x){|x,a|x.sub p,U.escape((a.
11
11
  to_param rescue a))}.gsub(/\\(.)/){$1})};h.any?? u+"?"+U.build_query(h[0]):u
12
12
  end;def / p;p[0]==?/?@root+p :p end;def URL c='/',*a;c=R(c,*a) if c.respond_to?(
13
- :urls);c=self/c;c=@request.url[/.{8,}?(?=\/)/]+c if c[0]==?/;URI c end end
13
+ :urls);c=self/c;c=@request.url[/.{8,}?(?=\/|$)/]+c if c[0]==?/;URI c end end
14
14
  module Base;attr_accessor:env,:request,:root,:input,:cookies,:state,:status,
15
15
  :headers,:body;T={};L=:layout;def lookup n;T.fetch(n.to_sym){|k|t=Views.
16
16
  method_defined?(k)||(t=O[:_t].keys.grep(/^#{n}\./)[0]and Template[t].new{
@@ -19,7 +19,7 @@ new(f,O[f[/\.(\w+)$/,1].to_sym]||{});O[:dynamic_templates]?t:T[k]=t} end
19
19
  def render v,*a,&b;if t=lookup(v);r,@_r=@_r,o=Hash===a[-1]?a.pop: {};s=(t==true)?mab{
20
20
  send v,*a,&b}: t.render(self,o[:locals]||{},&b);s=render(L,o.merge(L=>false)){s
21
21
  }if o[L]or o[L].nil?&&lookup(L)&&!r&&v.to_s[0]!=?_;s;else;raise"no template: #{v}"
22
- end;end;def mab &b;(@mab||=Mab.new({},self)).capture(&b) end;def r s,b,h={};b,h=
22
+ end;end;def mab &b;extend(Mab);mab(&b) end;def r s,b,h={};b,h=
23
23
  h,b if Hash===b;@status=s;@headers.merge!(h);@body=b end;def redirect *a;r 302,
24
24
  '','Location'=>URL(*a).to_s end;def r404 p;P%"#{p} not found"end;def r500 k,m,e
25
25
  raise e end;def r501 m;P%"#{m.upcase} not implemented"end;def serve(p,c)
@@ -34,7 +34,7 @@ n(v);m}: h end;def service *a;r=catch(:halt){send(@method,*a)};@body||=r;self
34
34
  end end;module Controllers;@r=[];class<<self;def R *u;r=@r;Class.
35
35
  new{meta_def(:urls){u};meta_def(:inherited){|x|r<<x}}end;def D p,m,e;p='/'if
36
36
  !p||!p[0];(a=O[:_t].find{|n,_|n==p}) and return [I,:serve,*a]
37
- @r.map{|k|k.urls.map{|x|return(k.method_defined? m)?[k,m,*$~[1..-1]]:
37
+ @r.map{|k|k.urls.map{|x|return(k.method_defined? m)?[k,m,*$~[1..-1].map{|x|U.unescape x}]:
38
38
  [I, 'r501',m]if p=~/^#{x}\/?$/}};[I,'r404',p] end;N=H.new{|_,x|x.downcase}.
39
39
  merge!("N"=>'(\d+)',"X"=>'([^/]+)',"Index"=>'');def M;def M;end;constants.
40
40
  map{|c|k=const_get(c);k.send:include,C,X,Base,Helpers,Models
@@ -44,7 +44,7 @@ Controllers;class<<self;def
44
44
  goes m;Apps<<a=eval(S.gsub(/Camping/,m.to_s),TOPLEVEL_BINDING);caller[0]=~/:/
45
45
  IO.read(a.set:__FILE__,$`)=~/^__END__/&&(b=$'.split /^@@\s*(.+?)\s*\r?\n/m).shift rescue nil
46
46
  a.set :_t,H[*b||[]];end;def call e;X.M
47
- p=e['PATH_INFO']=U.unescape(e['PATH_INFO']);k,m,*a=X.D p,e['REQUEST_METHOD'].
47
+ k,m,*a=X.D e["PATH_INFO"],e['REQUEST_METHOD'].
48
48
  downcase,e;k.new(e,m).service(*a).to_a;rescue;r500(:I,k,m,$!,:env=>e).to_a end
49
49
  def method_missing m,c,*a;X.M;h=Hash===a[-1]?a.pop: {};e=H[Rack::MockRequest.
50
50
  env_for('',h.delete(:env)||{})];k=X.const_get(c).new(e,m.to_s);h.each{|i,v|k.
@@ -1,23 +1,34 @@
1
1
  class MissingLibrary < Exception #:nodoc: all
2
2
  end
3
3
  begin
4
- require 'markaby'
4
+ require 'mab'
5
5
  rescue LoadError => e
6
- raise MissingLibrary, "Markaby could not be loaded (is it installed?): #{e.message}"
6
+ raise MissingLibrary, "Mab could not be loaded (is it installed?): #{e.message}"
7
7
  end
8
8
 
9
- $MAB_CODE = %{
10
- # The Mab class wraps Markaby, allowing it to run methods from Camping::Views
11
- # and also to replace :href, :action and :src attributes in tags by prefixing the root
12
- # path.
13
- class Mab < Markaby::Builder
14
- attr_reader :builder
15
-
9
+ $MAB_CODE = %q{
10
+ module Mab
11
+ include ::Mab::Mixin::HTML5
16
12
  include Views
17
- def tag!(*g,&b)
18
- h=g[-1]
19
- [:href,:action,:src].map{|a|(h[a]&&=self/h[a])rescue 0}
20
- super
13
+
14
+ alias << text!
15
+
16
+ def xhtml(*a, &b)
17
+ warn "xhtml_strict is no longer supported (or an active standard); using HTML5 instead"
18
+ html(*a, &b)
19
+ end
20
+
21
+ def xhtml_strict(*a, &b) xhtml(*a, &b) end
22
+ def xhtml_transitional(*a, &b) xhtml(*a, &b) end
23
+ def xhtml_frameset(*a, &b) xhtml(*a, &b) end
24
+
25
+ def helpers() self end
26
+
27
+ def html(*) doctype!; super end
28
+
29
+ def mab_done(tag)
30
+ h=tag.attributes
31
+ [:href,:action,:src].map{|a|h[a]&&=self/h[a]}
21
32
  end
22
33
  end
23
34
  }
@@ -32,157 +32,127 @@ module Camping
32
32
  #
33
33
  # You can also give Reloader more than one script.
34
34
  class Reloader
35
- attr_reader :scripts, :apps
35
+ attr_reader :file
36
36
 
37
- # This class is doing all the hard work; however, it only works on
38
- # single files. Reloader just wraps up support for multiple scripts
39
- # and hides away some methods you normally won't need.
40
- class Script # :nodoc:
41
- attr_reader :apps, :file, :dir, :extras
42
-
43
- def initialize(file, callback = nil)
44
- @file = File.expand_path(file)
45
- @dir = File.dirname(@file)
46
- @extras = File.join(@dir, File.basename(@file, ".rb"))
47
- @mtime = Time.at(0)
48
- @requires = []
49
- @apps = {}
50
- @callback = callback
37
+ def initialize(file, &blk)
38
+ @file = file
39
+ @mtime = Time.at(0)
40
+ @requires = []
41
+ @apps = {}
42
+ @callback = blk
43
+ end
44
+
45
+ def name
46
+ @name ||= begin
47
+ base = @file.dup
48
+ base = File.dirname(base) if base =~ /\bconfig\.ru$/
49
+ base.sub!(/\.[^.]+/, '')
50
+ File.basename(base).to_sym
51
51
  end
52
+ end
53
+
54
+ # Loads the apps availble in this script. Use <tt>apps</tt> to get
55
+ # the loaded apps.
56
+ def load_apps(old_apps)
57
+ all_requires = $LOADED_FEATURES.dup
58
+ all_apps = Camping::Apps.dup
59
+
60
+ load_file
61
+ ensure
62
+ @requires = []
63
+ dirs = []
64
+ new_apps = Camping::Apps - all_apps
52
65
 
53
- # Loads the apps availble in this script. Use <tt>apps</tt> to get
54
- # the loaded apps.
55
- def load_apps
56
- @requires.each do |path|
57
- $LOADED_FEATURES.delete(path)
66
+ @apps = new_apps.inject({}) do |hash, app|
67
+ if file = app.options[:__FILE__]
68
+ full = File.expand_path(file)
69
+ @requires << [file, full]
70
+ dirs << full.sub(/\.[^.]+$/, '')
58
71
  end
59
72
 
60
- all_requires = $LOADED_FEATURES.dup
61
- all_apps = Camping::Apps.dup
62
-
63
- begin
64
- load(@file)
65
- rescue Exception => e
66
- puts "!! Error loading #{@file}:"
67
- puts "#{e.class}: #{e.message}"
68
- puts e.backtrace
69
- puts "!! Error loading #{@file}, see backtrace above"
70
- end
73
+ key = app.name.to_sym
74
+ hash[key] = app
71
75
 
72
- @requires = ($LOADED_FEATURES - all_requires).map do |req|
73
- full = full_path(req)
74
- full if full == @file or full.index(@extras) == 0
76
+ if !old_apps.include?(key)
77
+ @callback.call(app) if @callback
78
+ app.create if app.respond_to?(:create)
75
79
  end
76
-
77
- @mtime = mtime
78
-
79
- new_apps = (Camping::Apps - all_apps)
80
- old_apps = @apps.dup
81
-
82
- @apps = new_apps.inject({}) do |hash, app|
83
- key = app.name.to_sym
84
- hash[key] = app
85
-
86
- if !old_apps.include?(key)
87
- @callback.call(app) if @callback
88
- app.create if app.respond_to?(:create)
89
- end
90
- hash
91
- end
92
-
93
- self
94
- end
95
-
96
- # Removes all the apps defined in this script.
97
- def remove_apps
98
- @apps.each do |name, app|
99
- Camping::Apps.delete(app)
100
- Object.send :remove_const, name
101
- end
102
- end
103
-
104
- # Reloads the file if needed. No harm is done by calling this multiple
105
- # times, so feel free call just to be sure.
106
- def reload!
107
- return if @mtime >= mtime
108
- remove_apps
109
- load_apps
110
- end
111
-
112
- # Checks if both scripts watches the same file.
113
- def ==(other)
114
- @file == other.file
80
+
81
+ hash
115
82
  end
116
-
117
- private
118
-
119
- def mtime
120
- (@requires + [@file]).compact.map do |fname|
121
- File.mtime(fname)
122
- end.reject{|t| t > Time.now }.max
83
+
84
+ ($LOADED_FEATURES - all_requires).each do |req|
85
+ full = full_path(req)
86
+ @requires << [req, full] if dirs.any? { |x| full.index(x) == 0 }
123
87
  end
88
+
89
+ @mtime = mtime
124
90
 
125
- # Figures out the full path of a required file.
126
- def full_path(req)
127
- dir = $LOAD_PATH.detect { |l| File.exists?(File.join(l, req)) }
128
- if dir
129
- File.expand_path(req, File.expand_path(dir))
130
- else
131
- req
132
- end
133
- end
91
+ self
134
92
  end
135
93
 
136
- # Creates the reloader, assigns a +script+ to it and initially loads the
137
- # application. Pass in the full path to the script, otherwise the script
138
- # will be loaded relative to the current working directory.
139
- def initialize(*scripts)
140
- @scripts = []
141
- @apps = {}
142
- update(*scripts)
94
+ def load_file
95
+ if @file =~ /\.ru$/
96
+ @app,_ = Rack::Builder.parse_file(@file)
97
+ else
98
+ load(@file)
99
+ end
143
100
  end
144
101
 
145
- def on_reload(&blk)
146
- @callback = blk
102
+ # Removes all the apps defined in this script.
103
+ def remove_apps
104
+ @requires.each do |(path, full)|
105
+ $LOADED_FEATURES.delete(path)
106
+ end
107
+
108
+ @apps.each do |name, app|
109
+ Camping::Apps.delete(app)
110
+ Object.send :remove_const, name
111
+ end.dup
112
+ ensure
113
+ @apps.clear
147
114
  end
148
115
 
149
- # Updates the reloader to only use the scripts provided:
150
- #
151
- # reloader.update("examples/blog.rb", "examples/wiki.rb")
152
- def update(*scripts)
153
- old_scripts = @scripts.dup
154
- clear
155
-
156
- @scripts = scripts.map do |script|
157
- file = File.expand_path(script)
158
- old_scripts.detect { |s| s.file == file } or
159
- Script.new(script, @callback)
160
- end
161
-
116
+ # Reloads the file if needed. No harm is done by calling this multiple
117
+ # times, so feel free call just to be sure.
118
+ def reload
119
+ return if @mtime >= mtime rescue nil
162
120
  reload!
163
121
  end
164
-
165
- # Removes all the scripts from the reloader.
166
- def clear
167
- @scripts = []
168
- @apps = {}
122
+
123
+ def reload!
124
+ load_apps(remove_apps)
169
125
  end
170
126
 
171
- # Returns the script which provided the given app.
172
- def script(app)
173
- @scripts.each do |script|
174
- return script if script.apps.values.include?(app)
127
+ # Checks if both scripts watches the same file.
128
+ def ==(other)
129
+ @file == other.file
130
+ end
131
+
132
+ def apps
133
+ if @app
134
+ { name => @app }
135
+ else
136
+ @apps
175
137
  end
176
-
177
- false
178
138
  end
179
139
 
180
- # Simply calls reload! on all the Script objects.
181
- def reload!
182
- @apps = {}
183
- @scripts.each do |script|
184
- script.reload!
185
- @apps.update(script.apps)
140
+ private
141
+
142
+ def mtime
143
+ @requires.map do |(path, full)|
144
+ File.mtime(full)
145
+ end.reject {|t| t > Time.now }.max || Time.now
146
+ end
147
+
148
+ # Figures out the full path of a required file.
149
+ def full_path(req)
150
+ return req if File.exists?(req)
151
+ dir = $LOAD_PATH.detect { |l| File.exists?(File.join(l, req)) }
152
+ if dir
153
+ File.expand_path(req, File.expand_path(dir))
154
+ else
155
+ req
186
156
  end
187
157
  end
188
158
  end