camping 1.5.180 → 2.0.rc0

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.
Files changed (64) hide show
  1. data/CHANGELOG +35 -0
  2. data/README +43 -68
  3. data/Rakefile +155 -86
  4. data/bin/camping +64 -246
  5. data/book/01_introduction +19 -0
  6. data/book/02_getting_started +443 -0
  7. data/book/51_upgrading +93 -0
  8. data/doc/api.html +1953 -0
  9. data/doc/book.html +73 -0
  10. data/doc/book/01_introduction.html +57 -0
  11. data/doc/book/02_getting_started.html +573 -0
  12. data/doc/book/51_upgrading.html +146 -0
  13. data/doc/created.rid +1 -0
  14. data/{extras → doc/images}/Camping.gif +0 -0
  15. data/doc/images/loadingAnimation.gif +0 -0
  16. data/{extras → doc/images}/permalink.gif +0 -0
  17. data/doc/index.html +148 -0
  18. data/doc/js/camping.js +79 -0
  19. data/doc/js/jquery.js +32 -0
  20. data/doc/rdoc.css +117 -0
  21. data/examples/blog.rb +280 -181
  22. data/extras/images/badge.gif +0 -0
  23. data/extras/images/boys-life.png +0 -0
  24. data/extras/images/deerputer.png +0 -0
  25. data/extras/images/diagram.png +0 -0
  26. data/extras/images/hill.png +0 -0
  27. data/extras/images/i-wish.png +0 -0
  28. data/extras/images/latl.png +0 -0
  29. data/extras/images/little-wheels.png +0 -0
  30. data/extras/images/square-badge.png +0 -0
  31. data/extras/images/uniform.png +0 -0
  32. data/extras/images/whale-bounce.png +0 -0
  33. data/extras/rdoc/generator/singledarkfish.rb +205 -0
  34. data/extras/rdoc/generator/template/flipbook/images/Camping.gif +0 -0
  35. data/extras/rdoc/generator/template/flipbook/images/loadingAnimation.gif +0 -0
  36. data/extras/rdoc/generator/template/flipbook/images/permalink.gif +0 -0
  37. data/extras/rdoc/generator/template/flipbook/js/camping.js +79 -0
  38. data/extras/rdoc/generator/template/flipbook/js/jquery.js +32 -0
  39. data/extras/rdoc/generator/template/flipbook/page.rhtml +30 -0
  40. data/extras/rdoc/generator/template/flipbook/rdoc.css +117 -0
  41. data/extras/rdoc/generator/template/flipbook/readme.rhtml +31 -0
  42. data/extras/rdoc/generator/template/flipbook/reference.rhtml +71 -0
  43. data/extras/rdoc/generator/template/flipbook/toc.rhtml +43 -0
  44. data/lib/camping-unabridged.rb +420 -481
  45. data/lib/camping.rb +40 -55
  46. data/lib/camping/{db.rb → ar.rb} +5 -8
  47. data/lib/camping/mab.rb +26 -0
  48. data/lib/camping/reloader.rb +175 -147
  49. data/lib/camping/server.rb +178 -0
  50. data/lib/camping/session.rb +34 -121
  51. data/test/apps/env_debug.rb +65 -0
  52. data/test/apps/forms.rb +95 -0
  53. data/test/apps/forward_to_other_controller.rb +60 -0
  54. data/test/apps/migrations.rb +97 -0
  55. data/test/apps/misc.rb +86 -0
  56. data/test/apps/sessions.rb +38 -0
  57. metadata +120 -80
  58. data/doc/camping.1.gz +0 -0
  59. data/examples/campsh.rb +0 -630
  60. data/examples/tepee.rb +0 -242
  61. data/extras/flipbook_rdoc.rb +0 -491
  62. data/lib/camping/fastcgi.rb +0 -244
  63. data/lib/camping/webrick.rb +0 -65
  64. data/test/test_xhtml_trans.rb +0 -55
@@ -0,0 +1,178 @@
1
+ require 'irb'
2
+ require 'rack'
3
+ require 'camping/reloader'
4
+
5
+ # == The Camping Server (for development)
6
+ #
7
+ # Camping includes a pretty nifty server which is built for development.
8
+ # It follows these rules:
9
+ #
10
+ # * Load all Camping apps in a directory or a file.
11
+ # * Load new apps that appear in that directory or that file.
12
+ # * Mount those apps according to their name. (e.g. Blog is mounted at /blog.)
13
+ # * Run each app's <tt>create</tt> method upon startup.
14
+ # * Reload the app if its modification time changes.
15
+ # * Reload the app if it requires any files under the same directory and one
16
+ # of their modification times changes.
17
+ # * Support the X-Sendfile header.
18
+ #
19
+ # Run it like this:
20
+ #
21
+ # camping examples/ # Mounts all apps in that directory
22
+ # camping blog.rb # Mounts Blog at /
23
+ #
24
+ # And visit http://localhost:3301/ in your browser.
25
+ class Camping::Server
26
+ attr_reader :reloader
27
+ attr_accessor :conf
28
+
29
+ def initialize(conf, paths)
30
+ @conf = conf
31
+ @paths = paths
32
+ @reloader = Camping::Reloader.new
33
+ connect(@conf.database) if @conf.database
34
+ end
35
+
36
+ def connect(db)
37
+ unless Camping.autoload?(:Models)
38
+ Camping::Models::Base.establish_connection(db)
39
+ end
40
+ end
41
+
42
+ def find_scripts
43
+ scripts = @paths.map do |path|
44
+ case
45
+ when File.file?(path)
46
+ path
47
+ when File.directory?(path)
48
+ Dir[File.join(path, '*.rb')]
49
+ end
50
+ end.flatten.compact
51
+ @reloader.update(*scripts)
52
+ end
53
+
54
+ def index_page(apps)
55
+ welcome = "You are Camping"
56
+ header = <<-HTML
57
+ <html>
58
+ <head>
59
+ <title>#{welcome}</title>
60
+ <style type="text/css">
61
+ body {
62
+ font-family: verdana, arial, sans-serif;
63
+ padding: 10px 40px;
64
+ margin: 0;
65
+ }
66
+ h1, h2, h3, h4, h5, h6 {
67
+ font-family: utopia, georgia, serif;
68
+ }
69
+ </style>
70
+ </head>
71
+ <body>
72
+ <h1>#{welcome}</h1>
73
+ HTML
74
+ footer = '</body></html>'
75
+ main = if apps.empty?
76
+ "<p>Good day. I'm sorry, but I could not find any Camping apps. "\
77
+ "You might want to take a look at the console to see if any errors "\
78
+ "have been raised.</p>"
79
+ else
80
+ "<p>Good day. These are the Camping apps you've mounted.</p><ul>" +
81
+ apps.map do |mount, app|
82
+ "<li><h3 style=\"display: inline\"><a href=\"/#{mount}\">#{app}</a></h3><small> / <a href=\"/code/#{mount}\">View source</a></small></li>"
83
+ end.join("\n") + '</ul>'
84
+ end
85
+
86
+ header + main + footer
87
+ end
88
+
89
+ def app
90
+ reload!
91
+ all_apps = apps
92
+ rapp = case all_apps.length
93
+ when 0
94
+ proc{|env|[200,{'Content-Type'=>'text/html'},index_page([])]}
95
+ when 1
96
+ apps.values.first
97
+ else
98
+ hash = {
99
+ "/" => proc {|env|[200,{'Content-Type'=>'text/html'},index_page(all_apps)]}
100
+ }
101
+ all_apps.each do |mount, wrapp|
102
+ # We're doing @reloader.reload! ourself, so we don't need the wrapper.
103
+ app = wrapp.app
104
+ hash["/#{mount}"] = app
105
+ hash["/code/#{mount}"] = proc do |env|
106
+ [200,{'Content-Type'=>'text/plain','X-Sendfile'=>wrapp.script.file},'']
107
+ end
108
+ end
109
+ Rack::URLMap.new(hash)
110
+ end
111
+ rapp = Rack::ContentLength.new(rapp)
112
+ rapp = Rack::Lint.new(rapp)
113
+ rapp = XSendfile.new(rapp)
114
+ rapp = Rack::ShowExceptions.new(rapp)
115
+ end
116
+
117
+ def apps
118
+ @reloader.apps.inject({}) do |h, (mount, wrapp)|
119
+ h[mount.to_s.downcase] = wrapp
120
+ h
121
+ end
122
+ end
123
+
124
+ def call(env)
125
+ app.call(env)
126
+ end
127
+
128
+ def start
129
+ handler, conf = case @conf.server
130
+ when "console"
131
+ puts "** Starting console"
132
+ reload!
133
+ this = self; eval("self", TOPLEVEL_BINDING).meta_def(:reload!) { this.reload!; nil }
134
+ ARGV.clear
135
+ IRB.start
136
+ exit
137
+ when "mongrel"
138
+ puts "** Starting Mongrel on #{@conf.host}:#{@conf.port}"
139
+ [Rack::Handler::Mongrel, {:Port => @conf.port, :Host => @conf.host}]
140
+ when "webrick"
141
+ puts "** Starting WEBrick on #{@conf.host}:#{@conf.port}"
142
+ [Rack::Handler::WEBrick, {:Port => @conf.port, :BindAddress => @conf.host}]
143
+ end
144
+ reload!
145
+ handler.run(self, conf)
146
+ end
147
+
148
+ def reload!
149
+ find_scripts
150
+ @reloader.reload!
151
+ end
152
+
153
+ # A Rack middleware for reading X-Sendfile. Should only be used in
154
+ # development.
155
+ class XSendfile
156
+
157
+ HEADERS = [
158
+ "X-Sendfile",
159
+ "X-Accel-Redirect",
160
+ "X-LIGHTTPD-send-file"
161
+ ]
162
+
163
+ def initialize(app)
164
+ @app = app
165
+ end
166
+
167
+ def call(env)
168
+ status, headers, body = @app.call(env)
169
+ headers = Rack::Utils::HeaderHash.new(headers)
170
+ if header = HEADERS.detect { |header| headers.include?(header) }
171
+ path = headers[header]
172
+ body = File.read(path)
173
+ headers['Content-Length'] = body.length.to_s
174
+ end
175
+ [status, headers, body]
176
+ end
177
+ end
178
+ end
@@ -1,123 +1,36 @@
1
- # == About camping/session.rb
2
- #
3
- # This file contains two modules which supply basic sessioning to your Camping app.
4
- # Again, we're dealing with a pretty little bit of code: approx. 60 lines.
5
- #
6
- # * Camping::Models::Session is a module which adds a single <tt>sessions</tt> table
7
- # to your database.
8
- # * Camping::Session is a module which you will mix into your application (or into
9
- # specific controllers which require sessions) to supply a <tt>@state</tt> variable
10
- # you can use in controllers and views.
11
- #
12
- # For a basic tutorial, see the *Getting Started* section of the Camping::Session module.
13
- require 'camping'
14
-
15
- module Camping::Models
16
- # A database table for storing Camping sessions. Contains a unique 32-character hashid, a
17
- # creation timestamp, and a column of serialized data called <tt>ivars</tt>.
18
- class Session < Base
19
- serialize :ivars
20
- def []=(k, v) # :nodoc:
21
- self.ivars[k] = v
22
- end
23
- def [](k) # :nodoc:
24
- self.ivars[k] rescue nil
25
- end
26
-
27
- RAND_CHARS = [*'A'..'Z'] + [*'0'..'9'] + [*'a'..'z']
28
-
29
- # Generates a new session ID and creates a row for the new session in the database.
30
- def self.generate cookies
31
- rand_max = RAND_CHARS.size
32
- sid = (0...32).inject("") { |ret,_| ret << RAND_CHARS[rand(rand_max)] }
33
- sess = Session.create :hashid => sid, :ivars => Camping::H[]
34
- cookies.camping_sid = sess.hashid
35
- sess
36
- end
37
-
38
- # Gets the existing session based on the <tt>camping_sid</tt> available in cookies.
39
- # If none is found, generates a new session.
40
- def self.persist cookies
41
- if cookies.camping_sid
42
- session = Camping::Models::Session.find_by_hashid cookies.camping_sid
43
- end
44
- unless session
45
- session = Camping::Models::Session.generate cookies
46
- end
47
- session
48
- end
49
-
50
- # Builds the session table in the database. To be used in your application's
51
- # <tt>create</tt> method.
52
- #
53
- # Like so:
54
- #
55
- # def Blog.create
56
- # Camping::Models::Session.create_schema
57
- # unless Blog::Models::Post.table_exists?
58
- # ActiveRecord::Schema.define(&Blog::Models.schema)
59
- # end
60
- # end
61
- #
62
- def self.create_schema
63
- unless table_exists?
64
- ActiveRecord::Schema.define do
65
- create_table :sessions, :force => true do |t|
66
- t.column :id, :integer, :null => false
67
- t.column :hashid, :string, :limit => 32
68
- t.column :created_at, :datetime
69
- t.column :ivars, :text
70
- end
71
- end
72
- reset_column_information
73
- end
74
- end
75
- end
76
- end
77
-
78
1
  module Camping
79
- # The Camping::Session module is designed to be mixed into your application or into specific
80
- # controllers which require sessions. This module defines a <tt>service</tt> method which
81
- # intercepts all requests handed to those controllers.
82
- #
83
- # == Getting Started
84
- #
85
- # To get sessions working for your application:
86
- #
87
- # 1. <tt>require 'camping/session'</tt>
88
- # 2. Mixin the module: <tt>module YourApp; include Camping::Session end</tt>
89
- # 3. In your application's <tt>create</tt> method, add a call to <tt>Camping::Models::Session.create_schema</tt>
90
- # 4. Throughout your application, use the <tt>@state</tt> var like a hash to store your application's data.
91
- #
92
- # If you are unfamiliar with the <tt>create</tt> method, see
93
- # http://code.whytheluckystiff.net/camping/wiki/GiveUsTheCreateMethod.
94
- #
95
- # == A Few Notes
96
- #
97
- # * The session ID is stored in a cookie. Look in <tt>@cookies.camping_sid</tt>.
98
- # * The session data is stored in the <tt>sessions</tt> table in your database.
99
- # * All mounted Camping apps using this class will use the same database table.
100
- # * However, your application's data is stored in its own hash.
101
- # * Session data is only saved if it has changed.
102
- module Session
103
- # This <tt>service</tt> method, when mixed into controllers, intercepts requests
104
- # and wraps them with code to start and close the session. If a session isn't found
105
- # in the database it is created. The <tt>@state</tt> variable is set and if it changes,
106
- # it is saved back into the database.
107
- def service(*a)
108
- session = Camping::Models::Session.persist @cookies
109
- app = self.class.name.gsub(/^(\w+)::.+$/, '\1')
110
- @state = (session[app] ||= Camping::H[])
111
- hash_before = Marshal.dump(@state).hash
112
- s = super(*a)
113
- if session
114
- hash_after = Marshal.dump(@state).hash
115
- unless hash_before == hash_after
116
- session[app] = @state
117
- session.save
118
- end
119
- end
120
- s
2
+ # == Getting Started
3
+ #
4
+ # To get sessions working for your application:
5
+ # 1. <tt>require 'camping/session'</tt>
6
+ # 2. Mixin the module: <tt>include Camping::Session</tt>
7
+ # 3. Define a secret (and keep it secret): <tt>secret "SECRET!"</tt>
8
+ # 4. Throughout your application, use the <tt>@state</tt> var like a hash
9
+ # to store your application's data.
10
+ #
11
+ # require 'camping/session' # 1
12
+ #
13
+ # module Nuts
14
+ # include Camping::Session # 2
15
+ # secret "Oh yeah!" # 3
16
+ # end
17
+ #
18
+ # == Other backends
19
+ #
20
+ # Camping only ships with session-cookies. However, the <tt>@state</tt>
21
+ # variable is simply a shortcut for <tt>@env['rack.session']</tt>. Therefore
22
+ # you can also use any middleware which sets this variable:
23
+ #
24
+ # module Nuts
25
+ # use Rack::Session::Memcache
26
+ # end
27
+ module Session
28
+ def self.included(app)
29
+ key = "#{app}.state".downcase
30
+ secret = [__FILE__, File.mtime(__FILE__)].join(":")
31
+
32
+ app.meta_def(:secret) { |val| secret.replace(val) }
33
+ app.use Rack::Session::Cookie, :key => key, :secret => secret
121
34
  end
122
- end
123
- end
35
+ end
36
+ end
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ begin require "rubygems" rescue LoadError end
4
+ require "camping"
5
+
6
+ Camping.goes :EnvDebug
7
+
8
+ module EnvDebug
9
+ module Controllers
10
+ class ShowEnv < R '/', '/(.*)'
11
+ def get(extra = nil)
12
+ @extra = extra
13
+ render :show_env
14
+ end
15
+ alias post get
16
+ end
17
+ end
18
+
19
+ module Views
20
+ def layout
21
+ html do
22
+ head{ title C }
23
+ body do
24
+ ul do
25
+ li{ a "show env", :href=>R(ShowEnv)}
26
+ end
27
+ p { yield }
28
+ end
29
+ end
30
+ end
31
+
32
+ def _print_hash(hash)
33
+ hash.keys.sort.each do |key|
34
+ value = hash[key]
35
+ pre "%30s: %s" % [key.inspect, value.inspect]
36
+ end
37
+ end
38
+
39
+ def show_env
40
+ b "extra: #{@extra.inspect}"
41
+ h2 "@status : #{@status.inspect}"
42
+ h2 "@method : #{@method.inspect}"
43
+ h2 "@root : #{@root.inspect}"
44
+ h2 "@env :"
45
+ _print_hash @env
46
+ h2 "@input : "
47
+ _print_hash @input
48
+ h2 "@headers :"
49
+ _print_hash @headers
50
+ end
51
+
52
+ end
53
+ end
54
+
55
+ # For CGI
56
+ if $0 == __FILE__
57
+ EnvDebug.create if EnvDebug.respond_to? :create
58
+ if ARGV.any?
59
+ require 'camping/fastcgi'
60
+ #Dir.chdir('/var/camping/blog/')
61
+ Camping::FastCGI.start(EnvDebug)
62
+ else
63
+ puts EnvDebug.run
64
+ end
65
+ end
@@ -0,0 +1,95 @@
1
+ require "camping"
2
+
3
+ Camping.goes :Forms
4
+
5
+ module Forms
6
+ module Controllers
7
+ class Index < R '/'
8
+ def get; render :index end
9
+ end
10
+ class GetForm
11
+ def get; render :get_form; end
12
+ end
13
+ class GetFormResult
14
+ def get; render :form_result; end
15
+ end
16
+ class PostForm
17
+ def get; render :post_form; end
18
+ def post; render :form_result; end
19
+ end
20
+ class FileForm
21
+ def get; render :file_form; end
22
+ def post; render :form_result; end
23
+ end
24
+ end
25
+
26
+ module Views
27
+ def layout
28
+ html do
29
+ head{ title C }
30
+ body do
31
+ ul do
32
+ li{ a "index", :href=>R(Index)}
33
+ li{ a "get form", :href=>R(GetForm)}
34
+ li{ a "post form", :href=>R(PostForm)}
35
+ li{ a "file form", :href=>R(FileForm)}
36
+ end
37
+ p { yield }
38
+ end
39
+ end
40
+ end
41
+
42
+ def form_result
43
+ p @input.inspect
44
+ end
45
+
46
+ def index
47
+ h1 "Welcome on the Camping test app"
48
+ end
49
+
50
+ def get_form
51
+ form :action=>R(GetFormResult), :method=>:get do
52
+ label "Give me your name!", :for=>:name
53
+ input :type=>:text, :name=>:name; br
54
+ input :type=>:text, :name=>"arr[]"; br
55
+ input :type=>:text, :name=>"arr[]"; br
56
+ input :type=>:text, :name=>"hash[x]"; br
57
+ input :type=>:text, :name=>"hash[y]"; br
58
+
59
+ input :type=>:submit
60
+ end
61
+ end
62
+
63
+ def post_form
64
+ form :action=>R(PostForm), :method=>:post do
65
+ label "Give me your name!", :for=>:name
66
+ input :type=>:text, :name=>:name; br
67
+ input :type=>:text, :name=>"arr[]"; br
68
+ input :type=>:text, :name=>"arr[]"; br
69
+ input :type=>:text, :name=>"hash[x]"; br
70
+ input :type=>:text, :name=>"hash[y]"; br
71
+
72
+ input :type=>:submit
73
+ end
74
+ end
75
+
76
+ def file_form
77
+ form :action=>R(FileForm), :method=>:post, :enctype=>"multipart/form-data" do
78
+ input :type=>:text, :name=>"arr"
79
+ input :type=>:text, :name=>"arr"; br
80
+ input :type=>:file, :name=>"first_file"; br
81
+ input :type=>:file, :name=>"files[]"; br
82
+ input :type=>:file, :name=>"files[]"; br
83
+ input :type=>:submit
84
+ end
85
+ end
86
+
87
+
88
+ end
89
+ end
90
+
91
+ # For CGI
92
+ if $0 == __FILE__
93
+ Forms.create
94
+ Forms.run
95
+ end