camping 1.4.2 → 1.5

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,8 +1,51 @@
1
+ = 1.5
2
+ === 3rd Oct, 2006
3
+
4
+ * Camping::Apps stores an array of classes for all loaded apps.
5
+ * bin/camping can be given a directory. Like: <tt>camping examples/</tt>
6
+ * Console mode -- thank zimbatm. Use: camping -C yourapp.rb
7
+ * Call controllers with Camping.method_missing.
8
+
9
+ Tepee.get(:Index) #=> (Response)
10
+ Blog.post(:Delete, id) #=> (Response)
11
+
12
+ Blog.post(:Login, :input => {'username' => 'admin', 'password' => 'camping'})
13
+ #=> #<Blog::Controllers::Login @user=... >
14
+
15
+ Blog.get(:Info, :env => {:HTTP_HOST => 'wagon'})
16
+ #=> #<Blog::Controllers::Info @env={'HTTP_HOST'=>'wagon'} ...>
17
+
18
+ * Using \r\n instead of \n on output. FastCGI has these needs.
19
+ * ActiveRecord no longer required or installed.
20
+ * If you refer to Models::Base, however, ActiveRecord will be loaded with autoload. (see lib/camping/db.rb)
21
+ * new Camping::FastCGI.serve which will serve a whole directory of apps
22
+ (see http://code.whytheluckystiff.net/camping/wiki/TheCampingServer)
23
+ * ~/.campingrc can contain database connection info if you want your default to be something other than SQLite.
24
+
25
+ database:
26
+ adapter: mysql
27
+ username: camping
28
+ socket: /tmp/mysql.sock
29
+ password: NOFORESTFIRES
30
+ database: camping
31
+
32
+ * controllers are now *ordered*. uses the inherited hook to keep track of all
33
+ classes created with R. those classes are scanned, in order, when a request is
34
+ made. any other controllers are handled first. so if you plan on overriding the
35
+ urls method, be sure to subclass from R().
36
+ * Console mode will load .irbrc in the working directory, if present.
37
+ (for example, in my ~/git/balloon directory, i have this in the .irbrc:
38
+ include Balloon::Models
39
+ when camping -C balloon.rb gets run, the models all get included in main.)
40
+ * And, of course, many other bugfixes from myself and the loyal+kind zimbatm...
41
+ * Markaby updated to 0.5. (See its CHANGELOG.)
42
+
1
43
  = 1.4.2
2
- === 10th May, 2006
44
+ === 18th May, 2006
3
45
 
4
46
  * Efficient file uploads for multipart/form-data POSTs.
5
47
  * Camping tool now uses Mongrel, if available. If not, sticks with WEBrick.
48
+ * Multiple apps can be loaded with the camping tool, each mounted according to their file name.
6
49
 
7
50
  = 1.4.1
8
51
  === 3rd May, 2006
data/README CHANGED
@@ -47,13 +47,6 @@ A skeletal Camping blog could look like this:
47
47
  end
48
48
  end
49
49
 
50
- if __FILE__ == $0
51
- Blog::Models::Base.establish_connection :adapter => 'sqlite3',
52
- :database => 'blog3.db'
53
- Blog::Models::Base.logger = Logger.new('camping.log')
54
- puts Blog.run
55
- end
56
-
57
50
  Some things you might have noticed:
58
51
 
59
52
  * Camping::Models uses ActiveRecord to do its work. We love ActiveRecord!
@@ -100,9 +93,9 @@ If you run them from the commandline, you'll probably just see a pile of HTML.
100
93
  Camping comes with an tool for launching apps from the commandline:
101
94
 
102
95
  * Run: <tt>camping blog.rb</tt>
103
- * Visit http://localhost:3301/ to use the app.
96
+ * Visit http://localhost:3301/blog/ to use the app.
104
97
 
105
- == Debugging Camping Apps
98
+ == How the Camping Tool Works
106
99
 
107
100
  If your application isn't working with the <tt>camping</tt> tool, keep in mind
108
101
  that the tool expects the following conventions to be used:
@@ -111,7 +104,7 @@ that the tool expects the following conventions to be used:
111
104
  http://code.whytheluckystiff.net/camping/wiki/BeAlertWhenOnSqlite3 for instructions.)
112
105
  2. If your script is called <tt>test.rb</tt>, Camping expects your application to
113
106
  be stored in a module called <tt>Test</tt>. Case is not imporant, though. The
114
- module can be called <tt>TeSt</tt> or any other permuation.
107
+ module can be called <tt>TeSt</tt> or any other permutation.
115
108
  3. Your script's postamble (anything enclosed in <tt>if __FILE__ == $0</tt>) will be
116
109
  ignored by the tool, since the tool will create an SQLite3 database at
117
110
  <tt>~/.camping.db</tt>. Or, on Windows, <tt>$USER/Application Data/Camping.db</tt>.
@@ -120,7 +113,7 @@ that the tool expects the following conventions to be used:
120
113
 
121
114
  == The Rules of Thumb
122
115
 
123
- Once you've started writing your own Camping app, I'd highly recommend becoming familiar
116
+ Once you've started writing your own Camping app, I'd highly recommend that you become familiar
124
117
  with the Camping Rules of Thumb which are listed on the wiki:
125
118
  http://code.whytheluckystiff.net/camping/wiki/CampingRulesOfThumb
126
119
 
data/Rakefile CHANGED
@@ -6,10 +6,10 @@ require 'fileutils'
6
6
  include FileUtils
7
7
 
8
8
  NAME = "camping"
9
- VERS = "1.4.2"
9
+ REV = File.read(".svn/entries")[/committed-rev="(\d+)"/, 1] rescue nil
10
+ VERS = ENV['VERSION'] || ("1.5" + (REV ? ".#{REV}" : ""))
10
11
  CLEAN.include ['**/.*.sw?', '*.gem', '.config']
11
12
  RDOC_OPTS = ['--quiet', '--title', "Camping, the Documentation",
12
- "--template", "extras/flipbook_rdoc.rb",
13
13
  "--opname", "index.html",
14
14
  "--line-numbers",
15
15
  "--main", "README",
@@ -27,20 +27,20 @@ task :before_doc do
27
27
  end
28
28
 
29
29
  Rake::RDocTask.new do |rdoc|
30
- rdoc.rdoc_dir = 'doc'
30
+ rdoc.rdoc_dir = 'doc/rdoc'
31
31
  rdoc.options += RDOC_OPTS
32
32
  rdoc.template = "extras/flipbook_rdoc.rb"
33
33
  rdoc.main = "README"
34
34
  rdoc.title = "Camping, the Documentation"
35
- rdoc.rdoc_files.add ['README', 'CHANGELOG', 'COPYING', 'lib/camping.rb']
35
+ rdoc.rdoc_files.add ['README', 'CHANGELOG', 'COPYING', 'lib/camping.rb', 'lib/camping/*.rb']
36
36
  end
37
37
 
38
38
  task :after_doc do
39
39
  mv "lib/camping.rb", "lib/camping-unabridged.rb"
40
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/}
41
+ cp "extras/Camping.gif", "doc/rdoc/"
42
+ cp "extras/permalink.gif", "doc/rdoc/"
43
+ sh %{scp -r doc/rdoc/* #{ENV['USER']}@rubyforge.org:/var/www/gforge-projects/camping/}
44
44
  end
45
45
 
46
46
  spec =
@@ -58,13 +58,13 @@ spec =
58
58
  s.homepage = 'http://code.whytheluckystiff.net/camping/'
59
59
  s.executables = ['camping']
60
60
 
61
- s.add_dependency('activerecord', '>=1.14.2')
62
- s.add_dependency('markaby', '>=0.4')
61
+ s.add_dependency('activesupport', '>=1.3.1')
62
+ s.add_dependency('markaby', '>=0.5')
63
63
  s.add_dependency('metaid')
64
64
  s.required_ruby_version = '>= 1.8.2'
65
65
 
66
66
  s.files = %w(COPYING README Rakefile) +
67
- Dir.glob("{bin,doc/rdoc,test,lib,extras}/**/*") +
67
+ Dir.glob("{bin,doc,test,lib,extras}/**/*") +
68
68
  Dir.glob("ext/**/*.{h,c,rb}") +
69
69
  Dir.glob("examples/**/*.rb") +
70
70
  Dir.glob("tools/*.rb")
@@ -1,60 +1,58 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- # this line prevents other db adapters from being loaded (oci8 was
4
- # causing some pain.)
5
- unless Object.const_defined? :RAILS_CONNECTION_ADAPTERS
6
- RAILS_CONNECTION_ADAPTERS = []
7
- end
8
- RAILS_CONNECTION_ADAPTERS.replace %w[sqlite]
9
-
10
3
  require 'delegate'
11
4
  require 'optparse'
5
+ require 'ostruct'
6
+
12
7
  require 'stringio'
13
- require 'rubygems'
14
8
  require 'camping'
9
+ require 'camping/reloader'
10
+ require 'yaml'
15
11
 
16
- host = '0.0.0.0'
17
- port = 3301
12
+ conf = OpenStruct.new(:host => '0.0.0.0', :port => 3301)
18
13
 
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 )
14
+ # Setup paths
15
+ if home = ENV['HOME'] # POSIX
16
+ conf.db = File.join(home, '.camping.db')
17
+ conf.rc = File.join(home, '.campingrc')
18
+ elsif home = ENV['APPDATA'] # MSWIN
19
+ conf.db = File.join(home, 'Camping.db')
20
+ conf.rc = File.join(home, 'Campingrc')
25
21
  end
26
22
 
23
+ # Load configuration if any
24
+ if conf.rc and File.exists?( conf.rc )
25
+ YAML.load_file(conf.rc).each do |k,v|
26
+ conf.send("#{k}=", v)
27
+ end
28
+ end
27
29
 
30
+ # Parse options
28
31
  opts = OptionParser.new do |opts|
29
32
  opts.banner = "Usage: camping app1.rb, app2.rb..."
30
33
  opts.define_head "#{File.basename($0)}, the microframework ON-button for ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
31
34
  opts.separator ""
32
35
  opts.separator "Specific options:"
33
36
 
34
- opts.on("-h", "--host HOSTNAME", "Host for web server to bind to (default is all IPs)") do |h|
35
- host = h
36
- end
37
-
38
- opts.on("-p", "--port NUM", "Port for web server (defaults to #{port})") do |p|
39
- port = p
40
- end
41
-
42
- opts.on("-d", "--database FILE", "Database file (defaults to #{db})") do |d|
43
- db = d
44
- end
37
+ opts.on("-h", "--host HOSTNAME", "Host for web server to bind to (default is all IPs)") { |conf.host| }
38
+ opts.on("-p", "--port NUM", "Port for web server (defaults to #{conf.port})") { |conf.port| }
39
+ opts.on("-d", "--database FILE", "Database file (defaults to #{conf.db})") { |conf.db| }
40
+ opts.on("-l", "--log FILE", "Start a database log ('-' for STDOUT)") { |conf.log| }
41
+ opts.on("-C", "--console", "Run in console mode with IRB") { conf.server = :console }
42
+ opts.on("-s", "--server NAME", "Server to force (mongrel, webrick, console)") { |s| conf.server = s.to_sym }
45
43
 
46
44
  opts.separator ""
47
45
  opts.separator "Common options:"
48
46
 
49
47
  # No argument, shows at tail. This will print an options summary.
50
48
  # Try it and see!
51
- opts.on_tail("-h", "--help", "Show this message") do
49
+ opts.on_tail("-?", "--help", "Show this message") do
52
50
  puts opts
53
51
  exit
54
52
  end
55
53
 
56
54
  # Another typical switch to print the version.
57
- opts.on_tail("--version", "Show version") do
55
+ opts.on_tail("-v", "--version", "Show version") do
58
56
  class << Gem; attr_accessor :loaded_specs; end
59
57
  puts Gem.loaded_specs['camping'].version
60
58
  exit
@@ -67,63 +65,47 @@ if ARGV.length < 1
67
65
  exit
68
66
  end
69
67
 
70
- Camping::Models::Base.establish_connection :adapter => 'sqlite3', :database => db
71
-
72
- class CampingReloader
73
- attr_accessor :klass, :mtime, :mount
68
+ # Default database
69
+ unless conf.database
70
+ unless conf.db
71
+ puts "!! No home directory found. Please specify a database file, see --help."; exit
72
+ end
73
+ conf.database = {:adapter => 'sqlite3', :database => conf.db}
74
+ end
74
75
 
75
- def initialize(script)
76
- @script = script
77
- @mount = File.basename(script, '.rb')
78
- load_app
76
+ # Load apps
77
+ PATHS = ARGV.dup
78
+ apps = PATHS.inject([]) do |apps, script|
79
+ if File.directory? script
80
+ apps.push(*Dir[File.join(script, '*.rb')])
81
+ else
82
+ apps << script
79
83
  end
84
+ end
80
85
 
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
86
+ Camping::Reloader.database = conf.database
87
+ Camping::Reloader.log = conf.log
88
+ apps.map! { |script| Camping::Reloader.new(script) }
89
+ abort("** No apps successfully loaded") unless apps.detect { |app| app.klass }
91
90
 
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
91
+ def apps.find_new_scripts
92
+ each { |app| app.reload_app }
93
+ PATHS.each do |path|
94
+ Dir[File.join(path, '*.rb')].each do |script|
95
+ smount = File.basename(script, '.rb')
96
+ next if detect { |x| x.mount == smount }
106
97
 
107
- k = @klass
108
- Object.instance_eval { remove_const k.name } if k
109
- load_app
110
- end
98
+ puts "** Discovered new #{script}"
99
+ app = Camping::Reloader.new(script)
100
+ next unless app
111
101
 
112
- def run(*a)
113
- reload_app
114
- if @klass
115
- @klass.run(*a)
116
- else
117
- Camping.run(*a)
102
+ yield app
103
+ self << app
118
104
  end
119
105
  end
120
-
121
- def view_source
122
- File.read(@script)
123
- end
106
+ self.sort! { |x, y| x.mount <=> y.mount }
124
107
  end
125
108
 
126
- apps = ARGV.map { |script| CampingReloader.new(script) }
127
109
  def apps.index_page
128
110
  welcome = "You are Camping"
129
111
  apps = self
@@ -161,15 +143,34 @@ def apps.index_page
161
143
  b.to_s
162
144
  end
163
145
 
164
- begin
146
+ # Check that mongrel exists
147
+ unless conf.server
148
+ begin
149
+ require 'mongrel'
150
+ require 'mongrel/camping'
151
+ conf.server = :mongrel
152
+ rescue LoadError
153
+ conf.server = :webrick
154
+ end
155
+ end
156
+
157
+ # Running the selected server
158
+ case conf.server.to_s
159
+ when 'mongrel'
165
160
  require 'mongrel'
166
161
  require 'mongrel/camping'
162
+
167
163
  class IndexHandler < Mongrel::HttpHandler
168
- def initialize(apps)
164
+ def initialize(apps, server)
169
165
  @apps = apps
166
+ @server = server
170
167
  end
171
168
  def process(req, res)
172
169
  res.start(200) do |head, out|
170
+ @apps.find_new_scripts do |app|
171
+ @server.register "/#{app.mount}", Mongrel::Camping::CampingHandler.new(app)
172
+ @server.register "/code/#{app.mount}", ViewSource.new(app)
173
+ end
173
174
  out << @apps.index_page
174
175
  end
175
176
  end
@@ -185,37 +186,94 @@ begin
185
186
  end
186
187
  end
187
188
  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)
189
+ begin
190
+ config = Mongrel::Configurator.new :host => conf.host do
191
+ listener :port => conf.port do
192
+ if apps.length > 1
193
+ apps.each do |app|
194
+ uri "/#{app.mount}", :handler => Mongrel::Camping::CampingHandler.new(app)
195
+ uri "/code/#{app.mount}", :handler => ViewSource.new(app)
196
+ end
197
+ uri "/", :handler => IndexHandler.new(apps, @listener)
198
+ else
199
+ uri "/", :handler => Mongrel::Camping::CampingHandler.new(apps.first)
200
+ end
201
+ uri "/favicon.ico", :handler => Mongrel::Error404Handler.new("")
202
+ trap("INT") { stop }
203
+ run
193
204
  end
194
- uri "/", :handler => IndexHandler.new(apps)
195
- uri "/favicon.ico", :handler => Mongrel::Error404Handler.new("")
196
- trap("INT") { stop }
197
- run
198
- end
205
+ end
206
+
207
+ puts "** Camping running on #{conf.host}:#{conf.port}."
208
+ config.join
209
+ rescue Errno::EADDRINUSE
210
+ puts "** ERROR : address #{conf.host}:#{conf.port} already in use."
199
211
  end
200
- config.join
201
- rescue LoadError
212
+ when 'webrick'
202
213
  require 'webrick/httpserver'
203
214
  require 'camping/webrick'
204
215
 
205
216
  # 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
217
+ s = WEBrick::HTTPServer.new(:BindAddress => conf.host, :Port => conf.port)
218
+ if apps.length > 1
219
+ apps.each do |app|
220
+ s.mount "/#{app.mount}", WEBrick::CampingHandler, app
221
+ s.mount_proc("/code/#{app.mount}") do |req, resp|
222
+ resp['Content-Type'] = 'text/plain'
223
+ resp.body = app.view_source
224
+ end
225
+ end
226
+ s.mount_proc("/") do |req, resp|
227
+ apps.find_new_scripts do |app|
228
+ s.mount "/#{app.mount}", WEBrick::CampingHandler, app
229
+ s.mount_proc("/code/#{app.mount}") do |req, resp|
230
+ resp['Content-Type'] = 'text/plain'
231
+ resp.body = app.view_source
232
+ end
233
+ end
234
+ resp.body = apps.index_page
212
235
  end
236
+ else
237
+ s.mount "/", WEBrick::CampingHandler, apps.first
213
238
  end
214
- s.mount_proc("/") { |req, resp| resp.body = apps.index_page }
215
239
 
216
240
  # Server up
217
241
  trap(:INT) do
218
242
  s.shutdown
219
243
  end
220
244
  s.start
245
+ when 'lighttpd'
246
+ require 'rbconfig'
247
+ ruby = File.join(Config::CONFIG['bindir'], Config::CONFIG['RUBY_INSTALL_NAME'])
248
+ dispatcher =<<SCRIPT
249
+ #!#{ruby}
250
+ require 'rubygems'
251
+ require 'camping/fastcgi'
252
+ Camping::Models::Base.establish_connection(Marshal.load(#{Marshal.dump(conf.database).dump}))
253
+ Camping::FastCGI.serve("")
254
+ SCRIPT
255
+ lighttpd_conf =<<CONF
256
+ server.port = #{conf.port}
257
+ server.bind = "#{conf.host}"
258
+ server.modules = ( "mod_fastcgi" )
259
+ server.document-root = "/dont/need/one"
260
+
261
+ #### fastcgi module
262
+ fastcgi.server = ( "/" => (
263
+ "localhost" => (
264
+ "socket" => "/tmp/camping-blog.socket",
265
+ "bin-path" => "#{conf.dispatcher}",
266
+ "check-local" => "disable",
267
+ "max-procs" => 1 ) ) )
268
+ CONF
269
+
270
+ when 'console'
271
+ ARGV.clear # Avoid passing args to IRB
272
+
273
+ require 'irb'
274
+ require 'irb/completion'
275
+ if File.exists? ".irbrc"
276
+ ENV['IRBRC'] = ".irbrc"
277
+ end
278
+ IRB.start
221
279
  end