camping 2.1.467 → 2.1.523
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/{README → README.md} +45 -43
- data/Rakefile +20 -1
- data/book/{01_introduction → 01_introduction.md} +4 -4
- data/book/{02_getting_started → 02_getting_started.md} +196 -196
- data/book/{51_upgrading → 51_upgrading.md} +54 -54
- data/lib/camping-unabridged.rb +7 -7
- data/lib/camping.rb +4 -4
- data/lib/camping/mab.rb +24 -13
- data/lib/camping/reloader.rb +98 -128
- data/lib/camping/server.rb +22 -89
- data/test/app_helpers.rb +12 -1
- data/test/app_markup.rb +54 -0
- data/test/app_reloader.rb +70 -0
- data/test/app_simple.rb +12 -0
- data/test/apps/reloader.rb +7 -0
- data/test/apps/reloader/config.ru +5 -0
- data/test/apps/reloader/reload_me.rb +2 -0
- data/test/apps/reloader_indirect.rb +3 -0
- data/test/test_helper.rb +4 -5
- metadata +67 -55
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
9
|
-
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
21
|
-
|
|
20
|
+
##From 1.5 to 2.0
|
|
21
|
+
###Rack
|
|
22
22
|
|
|
23
|
-
The biggest change in 2.0 is that it now uses Rack
|
|
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
|
-
|
|
29
|
+
###`require 'camping/db'`
|
|
30
30
|
|
|
31
31
|
In earlier versions of Camping, you loaded database support by:
|
|
32
32
|
|
|
33
|
-
|
|
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
|
-
|
|
44
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
59
|
-
|
|
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
|
-
|
|
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
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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.
|
data/lib/camping-unabridged.rb
CHANGED
|
@@ -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"
|
|
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,}?(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
data/lib/camping.rb
CHANGED
|
@@ -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,}?(
|
|
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;(
|
|
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
|
-
|
|
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.
|
data/lib/camping/mab.rb
CHANGED
|
@@ -1,23 +1,34 @@
|
|
|
1
1
|
class MissingLibrary < Exception #:nodoc: all
|
|
2
2
|
end
|
|
3
3
|
begin
|
|
4
|
-
|
|
4
|
+
require 'mab'
|
|
5
5
|
rescue LoadError => e
|
|
6
|
-
|
|
6
|
+
raise MissingLibrary, "Mab could not be loaded (is it installed?): #{e.message}"
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
-
$MAB_CODE = %{
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
}
|
data/lib/camping/reloader.rb
CHANGED
|
@@ -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 :
|
|
35
|
+
attr_reader :file
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
61
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
|
|
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
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
-
|
|
146
|
-
|
|
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
|
-
#
|
|
150
|
-
#
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
|
|
166
|
-
|
|
167
|
-
@scripts = []
|
|
168
|
-
@apps = {}
|
|
122
|
+
|
|
123
|
+
def reload!
|
|
124
|
+
load_apps(remove_apps)
|
|
169
125
|
end
|
|
170
126
|
|
|
171
|
-
#
|
|
172
|
-
def
|
|
173
|
-
@
|
|
174
|
-
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
@
|
|
184
|
-
|
|
185
|
-
|
|
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
|