camping 1.4.2 → 1.5

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/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