camping 1.4 → 1.4.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +12 -1
- data/COPYING +18 -0
- data/Rakefile +89 -0
- data/bin/camping +200 -42
- data/lib/camping-unabridged.rb +32 -15
- data/lib/camping.rb +11 -10
- metadata +74 -90
- data/examples/blog/blog.db +0 -0
- data/examples/blog/blog.sqlite3 +0 -21
- data/examples/blog/camping.log +0 -82
- data/examples/blog/foo.log +0 -0
- data/examples/blog/start +0 -6
- data/examples/blog/test.yml +0 -0
- data/examples/camping.log +0 -1111
- data/examples/charts/1.gif +0 -0
- data/examples/charts/2.gif +0 -0
- data/examples/charts/3.gif +0 -0
- data/examples/charts/start +0 -6
- data/examples/serve +0 -105
- data/examples/serve.db +0 -0
- data/examples/tepee/start +0 -6
data/CHANGELOG
CHANGED
@@ -1,5 +1,16 @@
|
|
1
|
+
= 1.4.2
|
2
|
+
=== 10th May, 2006
|
3
|
+
|
4
|
+
* Efficient file uploads for multipart/form-data POSTs.
|
5
|
+
* Camping tool now uses Mongrel, if available. If not, sticks with WEBrick.
|
6
|
+
|
7
|
+
= 1.4.1
|
8
|
+
=== 3rd May, 2006
|
9
|
+
|
10
|
+
* Streaming HTTP support. If body is IO, will simply pass to the controller. Mongrel, in particular, supports this nicely.
|
11
|
+
|
1
12
|
= 1.4
|
2
|
-
===
|
13
|
+
=== 11th April, 2006
|
3
14
|
|
4
15
|
* Moved Camping::Controllers::Base to Camping::Base.
|
5
16
|
* Moved Camping::Controllers::R to Camping::R.
|
data/COPYING
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Copyright (c) 2006 why the lucky stiff
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to
|
5
|
+
deal in the Software without restriction, including without limitation the
|
6
|
+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
7
|
+
sell copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
16
|
+
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
17
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
18
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/clean'
|
3
|
+
require 'rake/gempackagetask'
|
4
|
+
require 'rake/rdoctask'
|
5
|
+
require 'fileutils'
|
6
|
+
include FileUtils
|
7
|
+
|
8
|
+
NAME = "camping"
|
9
|
+
VERS = "1.4.2"
|
10
|
+
CLEAN.include ['**/.*.sw?', '*.gem', '.config']
|
11
|
+
RDOC_OPTS = ['--quiet', '--title', "Camping, the Documentation",
|
12
|
+
"--template", "extras/flipbook_rdoc.rb",
|
13
|
+
"--opname", "index.html",
|
14
|
+
"--line-numbers",
|
15
|
+
"--main", "README",
|
16
|
+
"--inline-source"]
|
17
|
+
|
18
|
+
desc "Packages up Camping."
|
19
|
+
task :default => [:package]
|
20
|
+
task :package => [:clean]
|
21
|
+
|
22
|
+
task :doc => [:before_doc, :rdoc, :after_doc]
|
23
|
+
|
24
|
+
task :before_doc do
|
25
|
+
mv "lib/camping.rb", "lib/camping-mural.rb"
|
26
|
+
mv "lib/camping-unabridged.rb", "lib/camping.rb"
|
27
|
+
end
|
28
|
+
|
29
|
+
Rake::RDocTask.new do |rdoc|
|
30
|
+
rdoc.rdoc_dir = 'doc'
|
31
|
+
rdoc.options += RDOC_OPTS
|
32
|
+
rdoc.template = "extras/flipbook_rdoc.rb"
|
33
|
+
rdoc.main = "README"
|
34
|
+
rdoc.title = "Camping, the Documentation"
|
35
|
+
rdoc.rdoc_files.add ['README', 'CHANGELOG', 'COPYING', 'lib/camping.rb']
|
36
|
+
end
|
37
|
+
|
38
|
+
task :after_doc do
|
39
|
+
mv "lib/camping.rb", "lib/camping-unabridged.rb"
|
40
|
+
mv "lib/camping-mural.rb", "lib/camping.rb"
|
41
|
+
cp "extras/Camping.gif", "doc/"
|
42
|
+
cp "extras/permalink.gif", "doc/"
|
43
|
+
sh %{scp -r doc/* #{ENV['USER']}@rubyforge.org:/var/www/gforge-projects/camping/}
|
44
|
+
end
|
45
|
+
|
46
|
+
spec =
|
47
|
+
Gem::Specification.new do |s|
|
48
|
+
s.name = NAME
|
49
|
+
s.version = VERS
|
50
|
+
s.platform = Gem::Platform::RUBY
|
51
|
+
s.has_rdoc = true
|
52
|
+
s.extra_rdoc_files = ["README", "CHANGELOG", "COPYING"]
|
53
|
+
s.rdoc_options += RDOC_OPTS + ['--exclude', '^(examples|extras)\/', '--exclude', 'lib/camping.rb']
|
54
|
+
s.summary = "minature rails for stay-at-home moms"
|
55
|
+
s.description = s.summary
|
56
|
+
s.author = "why the lucky stiff"
|
57
|
+
s.email = 'why@ruby-lang.org'
|
58
|
+
s.homepage = 'http://code.whytheluckystiff.net/camping/'
|
59
|
+
s.executables = ['camping']
|
60
|
+
|
61
|
+
s.add_dependency('activerecord', '>=1.14.2')
|
62
|
+
s.add_dependency('markaby', '>=0.4')
|
63
|
+
s.add_dependency('metaid')
|
64
|
+
s.required_ruby_version = '>= 1.8.2'
|
65
|
+
|
66
|
+
s.files = %w(COPYING README Rakefile) +
|
67
|
+
Dir.glob("{bin,doc/rdoc,test,lib,extras}/**/*") +
|
68
|
+
Dir.glob("ext/**/*.{h,c,rb}") +
|
69
|
+
Dir.glob("examples/**/*.rb") +
|
70
|
+
Dir.glob("tools/*.rb")
|
71
|
+
|
72
|
+
s.require_path = "lib"
|
73
|
+
# s.extensions = FileList["ext/**/extconf.rb"].to_a
|
74
|
+
s.bindir = "bin"
|
75
|
+
end
|
76
|
+
|
77
|
+
Rake::GemPackageTask.new(spec) do |p|
|
78
|
+
p.need_tar = true
|
79
|
+
p.gem_spec = spec
|
80
|
+
end
|
81
|
+
|
82
|
+
task :install do
|
83
|
+
sh %{rake package}
|
84
|
+
sh %{sudo gem install pkg/#{NAME}-#{VERS}}
|
85
|
+
end
|
86
|
+
|
87
|
+
task :uninstall => [:clean] do
|
88
|
+
sh %{sudo gem uninstall #{NAME}}
|
89
|
+
end
|
data/bin/camping
CHANGED
@@ -2,62 +2,220 @@
|
|
2
2
|
|
3
3
|
# this line prevents other db adapters from being loaded (oci8 was
|
4
4
|
# causing some pain.)
|
5
|
-
|
5
|
+
unless Object.const_defined? :RAILS_CONNECTION_ADAPTERS
|
6
|
+
RAILS_CONNECTION_ADAPTERS = []
|
7
|
+
end
|
8
|
+
RAILS_CONNECTION_ADAPTERS.replace %w[sqlite]
|
6
9
|
|
10
|
+
require 'delegate'
|
11
|
+
require 'optparse'
|
7
12
|
require 'stringio'
|
8
|
-
require '
|
9
|
-
require 'camping
|
13
|
+
require 'rubygems'
|
14
|
+
require 'camping'
|
15
|
+
|
16
|
+
host = '0.0.0.0'
|
17
|
+
port = 3301
|
18
|
+
|
19
|
+
db = nil
|
20
|
+
homes = []
|
21
|
+
homes << File.join( ENV['HOME'], '.camping.db' ) if ENV['HOME']
|
22
|
+
homes << File.join( ENV['APPDATA'], 'Camping.db' ) if ENV['APPDATA']
|
23
|
+
homes.each do |db|
|
24
|
+
break if File.exists?( db )
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
opts = OptionParser.new do |opts|
|
29
|
+
opts.banner = "Usage: camping app1.rb, app2.rb..."
|
30
|
+
opts.define_head "#{File.basename($0)}, the microframework ON-button for ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
|
31
|
+
opts.separator ""
|
32
|
+
opts.separator "Specific options:"
|
33
|
+
|
34
|
+
opts.on("-h", "--host HOSTNAME", "Host for web server to bind to (default is all IPs)") do |h|
|
35
|
+
host = h
|
36
|
+
end
|
10
37
|
|
11
|
-
(
|
12
|
-
|
13
|
-
|
14
|
-
USAGE
|
38
|
+
opts.on("-p", "--port NUM", "Port for web server (defaults to #{port})") do |p|
|
39
|
+
port = p
|
40
|
+
end
|
15
41
|
|
16
|
-
|
42
|
+
opts.on("-d", "--database FILE", "Database file (defaults to #{db})") do |d|
|
43
|
+
db = d
|
44
|
+
end
|
17
45
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
46
|
+
opts.separator ""
|
47
|
+
opts.separator "Common options:"
|
48
|
+
|
49
|
+
# No argument, shows at tail. This will print an options summary.
|
50
|
+
# Try it and see!
|
51
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
52
|
+
puts opts
|
53
|
+
exit
|
24
54
|
end
|
55
|
+
|
56
|
+
# Another typical switch to print the version.
|
57
|
+
opts.on_tail("--version", "Show version") do
|
58
|
+
class << Gem; attr_accessor :loaded_specs; end
|
59
|
+
puts Gem.loaded_specs['camping'].version
|
60
|
+
exit
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
opts.parse! ARGV
|
65
|
+
if ARGV.length < 1
|
66
|
+
puts opts
|
67
|
+
exit
|
25
68
|
end
|
26
69
|
|
27
70
|
Camping::Models::Base.establish_connection :adapter => 'sqlite3', :database => db
|
28
71
|
|
29
|
-
class
|
30
|
-
attr_accessor :klass, :mtime
|
31
|
-
|
72
|
+
class CampingReloader
|
73
|
+
attr_accessor :klass, :mtime, :mount
|
74
|
+
|
75
|
+
def initialize(script)
|
76
|
+
@script = script
|
77
|
+
@mount = File.basename(script, '.rb')
|
78
|
+
load_app
|
79
|
+
end
|
32
80
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
81
|
+
def load_app
|
82
|
+
@mtime = File.mtime(@script)
|
83
|
+
title = File.basename(@script)[/^(\w+)/,1]
|
84
|
+
begin
|
85
|
+
load @script
|
86
|
+
rescue Exception => e
|
87
|
+
puts "!! trouble loading #{title}: [#{e.class}] #{e.message}"
|
88
|
+
@klass = nil
|
89
|
+
return
|
90
|
+
end
|
91
|
+
|
92
|
+
@klass = Object.const_get(Object.constants.grep(/^#{title}$/i)[0]) rescue nil
|
93
|
+
unless @klass.const_defined? :C
|
94
|
+
puts "!! trouble loading #{title}: not a Camping app"
|
95
|
+
@klass = nil
|
96
|
+
return
|
97
|
+
end
|
98
|
+
@klass.create if @klass.respond_to? :create
|
99
|
+
@klass
|
100
|
+
end
|
101
|
+
|
102
|
+
# Load the script, locate the module
|
103
|
+
def reload_app
|
104
|
+
newtime = File.mtime( @script )
|
105
|
+
return if @klass and @mtime and newtime <= @mtime
|
106
|
+
|
107
|
+
k = @klass
|
108
|
+
Object.instance_eval { remove_const k.name } if k
|
109
|
+
load_app
|
110
|
+
end
|
111
|
+
|
112
|
+
def run(*a)
|
113
|
+
reload_app
|
114
|
+
if @klass
|
115
|
+
@klass.run(*a)
|
116
|
+
else
|
117
|
+
Camping.run(*a)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def view_source
|
122
|
+
File.read(@script)
|
123
|
+
end
|
45
124
|
end
|
46
125
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
126
|
+
apps = ARGV.map { |script| CampingReloader.new(script) }
|
127
|
+
def apps.index_page
|
128
|
+
welcome = "You are Camping"
|
129
|
+
apps = self
|
130
|
+
b = Markaby::Builder.new({}, {})
|
131
|
+
b = b.instance_eval do
|
132
|
+
html do
|
133
|
+
head do
|
134
|
+
title welcome
|
135
|
+
style <<-END, :type => 'text/css'
|
136
|
+
body {
|
137
|
+
font-family: verdana, arial, sans-serif;
|
138
|
+
padding: 10px 40px;
|
139
|
+
margin: 0;
|
140
|
+
}
|
141
|
+
h1, h2, h3, h4, h5, h6 {
|
142
|
+
font-family: utopia, georgia, serif;
|
143
|
+
}
|
144
|
+
END
|
145
|
+
end
|
146
|
+
body do
|
147
|
+
h1 welcome
|
148
|
+
p %{Good day. These are the Camping apps you've mounted.}
|
149
|
+
ul do
|
150
|
+
apps.each do |app|
|
151
|
+
next unless app.klass
|
152
|
+
li do
|
153
|
+
h3(:style => "display: inline") { a app.klass.name, :href => "/#{app.mount}" }
|
154
|
+
small { text " / " ; a "View Source", :href => "/code/#{app.mount}" }
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
54
160
|
end
|
55
|
-
|
56
|
-
nil
|
161
|
+
b.to_s
|
57
162
|
end
|
58
163
|
|
59
|
-
|
60
|
-
|
61
|
-
|
164
|
+
begin
|
165
|
+
require 'mongrel'
|
166
|
+
require 'mongrel/camping'
|
167
|
+
class IndexHandler < Mongrel::HttpHandler
|
168
|
+
def initialize(apps)
|
169
|
+
@apps = apps
|
170
|
+
end
|
171
|
+
def process(req, res)
|
172
|
+
res.start(200) do |head, out|
|
173
|
+
out << @apps.index_page
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
class ViewSource < Mongrel::HttpHandler
|
178
|
+
def initialize(app)
|
179
|
+
@app = app
|
180
|
+
end
|
181
|
+
def process(req, res)
|
182
|
+
res.start(200) do |head, out|
|
183
|
+
head['Content-Type'] = 'text/plain'
|
184
|
+
out << @app.view_source
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
config = Mongrel::Configurator.new :host => host do
|
189
|
+
listener :port => port do
|
190
|
+
apps.each do |app|
|
191
|
+
uri "/#{app.mount}", :handler => Mongrel::Camping::CampingHandler.new(app)
|
192
|
+
uri "/code/#{app.mount}", :handler => ViewSource.new(app)
|
193
|
+
end
|
194
|
+
uri "/", :handler => IndexHandler.new(apps)
|
195
|
+
uri "/favicon.ico", :handler => Mongrel::Error404Handler.new("")
|
196
|
+
trap("INT") { stop }
|
197
|
+
run
|
198
|
+
end
|
199
|
+
end
|
200
|
+
config.join
|
201
|
+
rescue LoadError
|
202
|
+
require 'webrick/httpserver'
|
203
|
+
require 'camping/webrick'
|
204
|
+
|
205
|
+
# Mount the root
|
206
|
+
s = WEBrick::HTTPServer.new(:BindAddress => host, :Port => port)
|
207
|
+
apps.each do |app|
|
208
|
+
s.mount "/#{app.mount}", WEBrick::CampingHandler, app
|
209
|
+
s.mount_proc("/code/#{app.mount}") do |req, resp|
|
210
|
+
resp['Content-Type'] = 'text/plain'
|
211
|
+
resp.body = app.view_source
|
212
|
+
end
|
213
|
+
end
|
214
|
+
s.mount_proc("/") { |req, resp| resp.body = apps.index_page }
|
215
|
+
|
216
|
+
# Server up
|
217
|
+
trap(:INT) do
|
218
|
+
s.shutdown
|
219
|
+
end
|
220
|
+
s.start
|
62
221
|
end
|
63
|
-
s.start
|
data/lib/camping-unabridged.rb
CHANGED
@@ -123,6 +123,7 @@ module Camping
|
|
123
123
|
raise NoMethodError, "#{m}"
|
124
124
|
end
|
125
125
|
end
|
126
|
+
alias_method :u, :regular_update
|
126
127
|
end
|
127
128
|
|
128
129
|
# Helpers contains methods available in your controllers and views. You may add
|
@@ -252,7 +253,7 @@ module Camping
|
|
252
253
|
def URL c='/',*a
|
253
254
|
c = R(c, *a) if c.respond_to? :urls
|
254
255
|
c = self/c
|
255
|
-
c = "
|
256
|
+
c = "//"+@env.HTTP_HOST+c if c[/^\//]
|
256
257
|
URI(c)
|
257
258
|
end
|
258
259
|
end
|
@@ -356,21 +357,37 @@ module Camping
|
|
356
357
|
qs = C.qs_parse(e.QUERY_STRING)
|
357
358
|
@in = r
|
358
359
|
if %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)|n.match(e.CONTENT_TYPE)
|
359
|
-
b = "--#$1"
|
360
|
-
@in.
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
360
|
+
b = /(?:\r?\n|\A)#{Regexp::quote("--#$1")}(?:--)?\r$/
|
361
|
+
until @in.eof?
|
362
|
+
fh=H[]
|
363
|
+
for l in @in
|
364
|
+
case l
|
365
|
+
when "\r\n": break
|
366
|
+
when /^Content-Disposition: form-data;/
|
367
|
+
fh.u H[*$'.scan(/(?:\s(\w+)="([^"]+)")/).flatten]
|
368
|
+
when /^Content-Type: (.+?)(\r$|\Z)/m
|
369
|
+
puts "=> fh[type] = #$1"
|
370
|
+
fh[:type] = $1
|
371
|
+
end
|
372
|
+
end
|
373
|
+
fn=fh[:name]
|
374
|
+
o=if fh[:filename]
|
375
|
+
o=fh[:tempfile]=Tempfile.new(:C)
|
376
|
+
o.binmode
|
369
377
|
else
|
370
|
-
fh=
|
378
|
+
fh=""
|
379
|
+
end
|
380
|
+
while l=@in.read(16384)
|
381
|
+
if l=~b
|
382
|
+
o<<$`.chomp
|
383
|
+
@in.seek(-$'.size,IO::SEEK_CUR)
|
384
|
+
break
|
385
|
+
end
|
386
|
+
o<<l
|
371
387
|
end
|
372
388
|
qs[fn]=fh if fn
|
373
|
-
|
389
|
+
fh[:tempfile].rewind if fh.is_a?H
|
390
|
+
end
|
374
391
|
elsif @method == "post"
|
375
392
|
qs.merge!(C.qs_parse(@in.read))
|
376
393
|
end
|
@@ -540,11 +557,11 @@ module Camping
|
|
540
557
|
# #=> {'post' => {'id' => '1', 'user' => '_why'}}
|
541
558
|
#
|
542
559
|
def qs_parse(qs, d = '&;')
|
543
|
-
m = proc {|_,o,n|o.
|
560
|
+
m = proc {|_,o,n|o.u(n,&m)rescue([*o]<<n)}
|
544
561
|
(qs||'').
|
545
562
|
split(/[#{d}] */n).
|
546
563
|
inject(H[]) { |h,p| k, v=un(p).split('=',2)
|
547
|
-
h.
|
564
|
+
h.u(k.split(/[\]\[]+/).reverse.
|
548
565
|
inject(v) { |x,i| H[i,x] },&m)
|
549
566
|
}
|
550
567
|
end
|