Thu Aug 19 16:07:43 +0200 2010

Camping, the Reference

Module Camping

If you’re new to Camping, you should probably start by reading the first chapters of The Camping Book.

Okay. So, the important thing to remember is that Camping.goes :Nuts copies the Camping module into Nuts. This means that you should never use any of these methods/classes on the Camping module, but rather on your own app. Here’s a short explanation on how Camping is organized:

Camping also ships with:

More importantly, Camping also installs The Camping Server, please see Camping::Server.


The Camping Server (for development)

Camping includes a pretty nifty server which is built for development. It follows these rules:

  • Load all Camping apps in a directory or a file.
  • Load new apps that appear in that directory or that file.
  • Mount those apps according to their name. (e.g. Blog is mounted at /blog.)
  • Run each app’s create method upon startup.
  • Reload the app if its modification time changes.
  • Reload the app if it requires any files under the same directory and one of their modification times changes.
  • Support the X-Sendfile header.

Run it like this:

  camping examples/        # Mounts all apps in that directory
  camping blog.rb          # Mounts Blog at /

And visit localhost:3301/ in your browser.

Methods

Public Class method: ::call(e)

Ruby web servers use this method to enter the Camping realm. The e argument is the environment variables hash as per the Rack specification. And array with [status, headers, body] is expected at the output.

See: rack.rubyforge.org/doc/SPEC.html

     # File lib/camping-unabridged.rb, line 609
609:     def call(e)
610:       X.M
611:       p = e['PATH_INFO'] = U.unescape(e['PATH_INFO'])
612:       k,m,*a=X.D p,e['REQUEST_METHOD'].downcase,e
613:       k.new(e,m).service(*a).to_a
614:     rescue
615:       r500(:I, k, m, $!, :env => e).to_a
616:     end

Public Class method: ::goes(m)

When you are running many applications, you may want to create independent modules for each Camping application. Camping::goes defines a toplevel constant with the whole MVC rack inside:

  require 'camping'
  Camping.goes :Nuts

  module Nuts::Controllers; ... end
  module Nuts::Models;      ... end
  module Nuts::Views;       ... end

All the applications will be available in Camping::Apps.

     # File lib/camping-unabridged.rb, line 600
600:     def goes(m)
601:       Apps << eval(S.gsub(/Camping/,m.to_s), TOPLEVEL_BINDING)
602:     end

Public Class method: ::method_missing(m, c, *a)

The Camping scriptable dispatcher. Any unhandled method call to the app module will be sent to a controller class, specified as an argument.

  Blog.get(:Index)
  #=> #<Blog::Controllers::Index ... >

The controller object contains all the @cookies, @body, @headers, etc. formulated by the response.

You can also feed environment variables and query variables as a hash, the final argument.

  Blog.post(:Login, :input => {'username' => 'admin', 'password' => 'camping'})
  #=> #<Blog::Controllers::Login @user=... >

  Blog.get(:Info, :env => {'HTTP_HOST' => 'wagon'})
  #=> #<Blog::Controllers::Info @headers={'HTTP_HOST'=>'wagon'} ...>
     # File lib/camping-unabridged.rb, line 636
636:     def method_missing(m, c, *a)
637:       X.M
638:       h = Hash === a[-1] ? a.pop : {}
639:       e = H[Rack::MockRequest.env_for('',h.delete(:env)||{})]
640:       k = X.const_get(c).new(e,m.to_s)
641:       h.each { |i, v| k.send("#{i}=", v) }
642:       k.service(*a)
643:     end

Public Class method: ::options()

A hash where you can set different settings.

     # File lib/camping-unabridged.rb, line 657
657:     def options
658:       O
659:     end

Public Class method: ::set(k, v)

Shortcut for setting options:

  module Blog
    set :secret, "Hello!"
  end
     # File lib/camping-unabridged.rb, line 666
666:     def set(k, v)
667:       O[k] = v
668:     end

Public Class method: ::use(*a, &b)

Injects a middleware:

  module Blog
    use Rack::MethodOverride
    use Rack::Session::Memcache, :key => "session"
  end
     # File lib/camping-unabridged.rb, line 651
651:     def use(*a, &b)
652:       m = a.shift.new(method(:call), *a, &b)
653:       meta_def(:call) { |e| m.call(e) }
654:     end

Module Camping::Base

Camping::Base is built into each controller by way of the generic routing class Camping::R. In some ways, this class is trying to do too much, but it saves code for all the glue to stay in one place. Forgivable, considering that it’s only really a handful of methods and accessors.

Everything in this module is accessable inside your controllers.

Methods

Public Instance method: #lookup(n)

Finds a template, returning either:

  false             # => Could not find template
  true              # => Found template in Views
  instance of Tilt  # => Found template in a file
     # File lib/camping-unabridged.rb, line 250
250:     def lookup(n)
251:       T.fetch(n.to_sym) do |k|
252:         t = Views.method_defined?(k) ||
253:           (f = Dir[[O[:views] || "views", "#{n}.*"]*'/'][0]) &&
254:           Template.new(f, O[f[/\.(\w+)$/, 1].to_sym] || {})
255:         
256:         O[:dynamic_templates] ? t : T[k] = t
257:       end
258:     end

Public Instance method: #mab(&b)

You can directly return HTML form your controller for quick debugging by calling this method and pass some Markaby to it.

  module Nuts::Controllers
    class Info
      def get; mab{ code @headers.inspect } end
    end
  end

You can also pass true to use the :layout HTML wrapping method

     # File lib/camping-unabridged.rb, line 293
293:     def mab(&b)
294:       (@mab ||= Mab.new({},self)).capture(&b)
295:     end

Public Instance method: #r(s, b, h = {})

A quick means of setting this controller’s status, body and headers based on a Rack response:

  r(302, 'Location' => self / "/view/12", '')
  r(*another_app.call(@env))

You can also switch the body and the header if you want:

  r(404, "Could not find page")

See also: r404, r500 and r501

     # File lib/camping-unabridged.rb, line 308
308:     def r(s, b, h = {})
309:       b, h = h, b if Hash === b
310:       @status = s
311:       @headers.merge!(h)
312:       @body = b
313:     end

Public Instance method: #r404(p)

Called when a controller was not found. You can override this if you want to customize the error page:

  module Nuts
    def r404(path)
      @path = path
      render :not_found
    end
  end
     # File lib/camping-unabridged.rb, line 342
342:     def r404(p)
343:       P % "#{p} not found"
344:     end

Public Instance method: #r500(k,m,e)

Called when an exception is raised. However, if there is a parse error in Camping or in your application’s source code, it will not be caught.

k is the controller class, m is the request method (GET, POST, etc.) and e is the Exception which can be mined for useful info.

Be default this simply re-raises the error so a Rack middleware can handle it, but you are free to override it here:

  module Nuts
    def r500(klass, method, exception)
      send_email_alert(klass, method, exception)
      render :server_error
    end
  end
     # File lib/camping-unabridged.rb, line 361
361:     def r500(k,m,e)
362:       raise e
363:     end

Public Instance method: #r501(m)

Called if an undefined method is called on a controller, along with the request method m (GET, POST, etc.)

     # File lib/camping-unabridged.rb, line 367
367:     def r501(m)
368:       P % "#{m.upcase} not implemented"
369:     end

Public Instance method: #redirect(*a)

Formulate a redirect response: a 302 status with Location header and a blank body. Uses Helpers#URL to build the location from a controller route or path.

So, given a root of localhost:3301/articles:

  redirect "view/12"  # redirects to "//localhost:3301/articles/view/12"
  redirect View, 12   # redirects to "//localhost:3301/articles/view/12"

NOTE: This method doesn’t magically exit your methods and redirect. You’ll need to return redirect(...) if this isn’t the last statement in your code, or throw :halt if it’s in a helper.

See: Controllers

     # File lib/camping-unabridged.rb, line 329
329:     def redirect(*a)
330:       r(302,'','Location'=>URL(*a).to_s)
331:     end

Public Instance method: #render(v, *a, &b)

Display a view, calling it by its method name v. If a layout method is found in Camping::Views, it will be used to wrap the HTML.

  module Nuts::Controllers
    class Show
      def get
        @posts = Post.find :all
        render :index
      end
    end
  end
     # File lib/camping-unabridged.rb, line 272
272:     def render(v, *a, &b)
273:       if t = lookup(v)
274:         o = Hash === a[-1] ? a.pop : {}
275:         s = (t == true) ? mab{ send(v, *a, &b) } : t.render(self, o[:locals] || {}, &b)
276:         s = render(L, o.merge(L => false)) { s } if v.to_s[0] != ?_ && o[L] != false && lookup(L)
277:         s
278:       else
279:         raise "Can't find template #{v}"
280:       end
281:     end

Public Instance method: #service(*a)

All requests pass through this method before going to the controller. Some magic in Camping can be performed by overriding this method.

     # File lib/camping-unabridged.rb, line 415
415:     def service(*a)
416:       r = catch(:halt){send(@method, *a)}
417:       @body ||= r 
418:       self
419:     end

Public Instance method: #to_a()

Turn a controller into a Rack response. This is designed to be used to pipe controllers into the r method. A great way to forward your requests!

  class Read < '/(\d+)'
    def get(id)
      Post.find(id)
    rescue
      r *Blog.get(:NotFound, @headers.REQUEST_URI)
    end
  end
     # File lib/camping-unabridged.rb, line 382
382:     def to_a
383:       @env['rack.session'] = Hash[@state]
384:       r = Rack::Response.new(@body, @status, @headers)
385:       @cookies.each do |k, v|
386:         next if @old_cookies[k] == v
387:         v = { :value => v, :path => self / "/" } if String === v
388:         r.set_cookie(k, v)
389:       end
390:       r.to_a
391:     end

Module Camping::Controllers

Controllers receive the requests and sends a response back to the client. A controller is simply a class which must implement the HTTP methods it wants to accept:

  module Nuts::Controllers
    class Index
      def get
        "Hello World"
      end
    end

    class Posts
      def post
        Post.create(@input)
        redirect Index
      end
    end
  end

Defining a controller

There are two ways to define controllers: Just defining a class and let Camping figure out the route, or add the route explicitly using R.

If you don’t use R, Camping will first split the controller name up by words (HelloWorld => Hello and World). Then it would do the following:

  • Replace Index with /
  • Replace X with ([^/]+)
  • Replace N with (\d+)
  • Everything else turns into lowercase
  • Join the words with slashes

Here’s a few examples:

  Index   # => /
  PostN   # => /post/(\d+)
  PageX   # => /page/([^/]+)
  Pages   # => /pages

The request

You have these variables which describes the request:

  • @env contains the environment as defined in rack.rubyforge.org/doc/SPEC.html
  • @request is Rack::Request.new(@env)
  • @root is the path where the app is mounted
  • @cookies is a hash with the cookies sent by the client
  • @state is a hash with the sessions (see Camping::Session)
  • @method is the HTTP method in lowercase

The response

You can change these variables to your needs:

  • @status is the HTTP status (defaults to 200)
  • @headers is a hash with the headers
  • @body is the body (a string or something which responds to each)
  • Any changes in @cookies and @state will also be sent to the client

If you haven’t set @body, it will use the return value of the method:

  module Nuts::Controllers
    class Index
      def get
        "This is the body"
      end
    end

    class Posts
      def get
        @body = "Hello World!"
        "This is ignored"
      end
    end
  end

Methods

Public Class method: ::D(p, m, e)

Dispatch routes to controller classes. For each class, routes are checked for a match based on their order in the routing list given to Controllers::R. If no routes were given, the dispatcher uses a slash followed by the name of the controller lowercased.

Controllers are searched in this order:

  • Classes without routes, since they refer to a very specific URL.
  • Classes with routes are searched in order of their creation.

So, define your catch-all controllers last.

     # File lib/camping-unabridged.rb, line 545
545:       def D(p, m, e)
546:         p = '/' if !p || !p[0]
547:         r.map { |k|
548:           k.urls.map { |x|
549:             return (k.method_defined?(m)) ?
550:               [k, m, *$~[1..-1]] : [I, 'r501', m] if p =~ /^#{x}\/?$/
551:           }
552:         }
553:         [I, 'r404', p]
554:       end

Public Class method: ::M()

The route maker, this is called by Camping internally, you shouldn’t need to call it.

Still, it’s worth know what this method does. Since Ruby doesn’t keep track of class creation order, we’re keeping an internal list of the controllers which inherit from R(). This method goes through and adds all the remaining routes to the beginning of the list and ensures all the controllers have the right mixins.

Anyway, if you are calling the URI dispatcher from outside of a Camping server, you’ll definitely need to call this to set things up. Don’t call it too early though. Any controllers added after this method is called won’t work properly

     # File lib/camping-unabridged.rb, line 570
570:       def M
571:         def M #:nodoc:
572:         end
573:         constants.map { |c|
574:           k = const_get(c)
575:           k.send :include,C,X,Base,Helpers,Models
576:           @r=[k]+r if r-[k]==r
577:           k.meta_def(:urls){["/#{c.to_s.scan(/.[^A-Z]*/).map(&N.method(:[]))*'/'}"]}if !k.respond_to?:urls
578:         }
579:       end

Public Class method: ::R(*u)

Add routes to a controller class by piling them into the R method.

The route is a regexp which will match the request path. Anything enclosed in parenthesis will be sent to the method as arguments.

  module Camping::Controllers
    class Edit < R '/edit/(\d+)', '/new'
      def get(id)
        if id   # edit
        else    # new
        end
      end
    end
  end
     # File lib/camping-unabridged.rb, line 526
526:       def R *u
527:         r=@r
528:         Class.new {
529:           meta_def(:urls){u}
530:           meta_def(:inherited){|x|r<<x}
531:         }
532:       end

Class Camping::H < Hash

An object-like Hash. All Camping query string and cookie variables are loaded as this.

To access the query string, for instance, use the @input variable.

  module Blog::Controllers
    class Index < R '/'
      def get
        if (page = @input.page.to_i) > 0
          page -= 1
        end
        @posts = Post.all, :offset => page * 20, :limit => 20
        render :index
      end
    end
  end

In the above example if you visit /?page=2, you’ll get the second page of twenty posts. You can also use @input['page'] to get the value for the page query variable.

Methods

Public Instance method: #method_missing(m,*a)

Gets or sets keys in the hash.

  @cookies.my_favorite = :macadamian
  @cookies.my_favorite
  => :macadamian
    # File lib/camping-unabridged.rb, line 80
80:     def method_missing(m,*a)
81:         m.to_s=~/=$/?self[$`]=a[0]:a==[]?self[m.to_s]:super
82:     end

Module Camping::Helpers

Helpers contains methods available in your controllers and views. You may add methods of your own to this module, including many helper methods from Rails. This is analogous to Rails’ ApplicationHelper module.

Using ActionPack Helpers

If you’d like to include helpers from Rails’ modules, you’ll need to look up the helper module in the Rails documentation at api.rubyonrails.org/.

For example, if you look up the ActionView::Helpers::FormTagHelper class, you’ll find that it’s loaded from the action_view/helpers/form_tag_helper.rb file. You’ll need to have the ActionPack gem installed for this to work.

Often the helpers depends on other helpers, so you would have to look up the dependencies too. FormTagHelper for instance required the content_tag provided by TagHelper.

  require 'action_view/helpers/form_tag_helper'

  module Nuts::Helpers
    include ActionView::Helpers::TagHelper
    include ActionView::Helpers::FormTagHelper
  end

Return a response immediately

If you need to return a response inside a helper, you can use throw :halt.

  module Nuts::Helpers
    def requires_login!
      unless @state.user_id
        redirect Login
        throw :halt
      end
    end
  end

  module Nuts::Controllers
    class Admin
      def get
        requires_login!
        "Never gets here unless you're logged in"
      end
    end
  end

Methods

Public Instance method: #/(p)

Simply builds a complete path from a path p within the app. If your application is mounted at /blog:

  self / "/view/1"    #=> "/blog/view/1"
  self / "styles.css" #=> "styles.css"
  self / R(Edit, 1)   #=> "/blog/edit/1"
     # File lib/camping-unabridged.rb, line 201
201:     def /(p); p[0] == ?/ ? @root + p : p end

Public Instance method: #R(c,*g)

From inside your controllers and views, you will often need to figure out the route used to get to a certain controller c. Pass the controller class and any arguments into the R method, a string containing the route will be returned to you.

Assuming you have a specific route in an edit controller:

  class Edit < R '/edit/(\d+)'

A specific route to the Edit controller can be built with:

  R(Edit, 1)

Which outputs: /edit/1.

If a controller has many routes, the route will be selected if it is the first in the routing list to have the right number of arguments.

Using R in the View

Keep in mind that this route doesn’t include the root path. You will need to use / (the slash method above) in your controllers. Or, go ahead and use the Helpers#URL method to build a complete URL for a route.

However, in your views, the :href, :src and :action attributes automatically pass through the slash method, so you are encouraged to use R or URL in your views.

 module Nuts::Views
   def menu
     div.menu! do
       a 'Home', :href => URL()
       a 'Profile', :href => "/profile"
       a 'Logout', :href => R(Logout)
       a 'Google', :href => 'http://google.com'
     end
   end
 end

Let’s say the above example takes place inside an application mounted at localhost:3301/frodo and that a controller named Logout is assigned to route /logout. The HTML will come out as:

  <div id="menu">
    <a href="http://localhost:3301/frodo/">Home</a>
    <a href="/frodo/profile">Profile</a>
    <a href="/frodo/logout">Logout</a>
    <a href="http://google.com">Google</a>
  </div>
     # File lib/camping-unabridged.rb, line 183
183:     def R(c,*g)
184:       p,h=/\(.+?\)/,g.grep(Hash)
185:       g-=h
186:       raise "bad route" unless u = c.urls.find{|x|
187:         break x if x.scan(p).size == g.size && 
188:           /^#{x}\/?$/ =~ (x=g.inject(x){|x,a|
189:             x.sub p,U.escape((a.to_param rescue a))}.gsub(/\\(.)/){$1})
190:       }
191:       h.any?? u+"?"+U.build_query(h[0]) : u
192:     end

Public Instance method: #URL(c='/',*a)

Builds a URL route to a controller or a path, returning a URI object. This way you’ll get the hostname and the port number, a complete URL.

You can use this to grab URLs for controllers using the R-style syntax. So, if your application is mounted at test.ing/blog/ and you have a View controller which routes as R '/view/(d+)':

  URL(View, @post.id)    #=> #<URL:http://test.ing/blog/view/12>

Or you can use the direct path:

  self.URL               #=> #<URL:http://test.ing/blog/>
  self.URL + "view/12"   #=> #<URL:http://test.ing/blog/view/12>
  URL("/view/12")        #=> #<URL:http://test.ing/blog/view/12>

It’s okay to pass URL strings through this method as well:

  URL("http://google.com")  #=> #<URL:http://google.com>

Any string which doesn’t begin with a slash will pass through unscathed.

     # File lib/camping-unabridged.rb, line 224
224:     def URL c='/',*a
225:       c = R(c, *a) if c.respond_to? :urls
226:       c = self/c
227:       c = @request.url[/.{8,}?(?=\/)/]+c if c[0]==?/
228:       URI(c)
229:     end

Module Camping::Models

Models is an empty Ruby module for housing model classes derived from ActiveRecord::Base. As a shortcut, you may derive from Base which is an alias for ActiveRecord::Base.

  module Camping::Models
    class Post < Base; belongs_to :user end
    class User < Base; has_many :posts end
  end

Where Models are Used

Models are used in your controller classes. However, if your model class name conflicts with a controller class name, you will need to refer to it using the Models module.

  module Camping::Controllers
    class Post < R '/post/(\d+)'
      def get(post_id)
        @post = Models::Post.find post_id
        render :index
      end
    end
  end

Models cannot be referred to in Views at this time.

Methods

Module Camping::Models::Base

Methods

Public Class method: ::table_name_prefix()

The default prefix for Camping model classes is the topmost module name lowercase and followed with an underscore.

  Tepee::Models::Page.table_name_prefix
    #=> "tepee_pages"
    # File lib/camping/ar.rb, line 66
66:     def Base.table_name_prefix
67:         "#{name[/\w+/]}_".downcase.sub(/^(#{A}|camping)_/i,'')
68:     end

Class Camping::Reloader < Object

The Camping Reloader

Camping apps are generally small and predictable. Many Camping apps are contained within a single file. Larger apps are split into a handful of other Ruby libraries within the same directory.

Since Camping apps (and their dependencies) are loaded with Ruby’s require method, there is a record of them in $LOADED_FEATURES. Which leaves a perfect space for this class to manage auto-reloading an app if any of its immediate dependencies changes.

Wrapping Your Apps

Since bin/camping and the Camping::Server class already use the Reloader, you probably don’t need to hack it on your own. But, if you’re rolling your own situation, here’s how.

Rather than this:

  require 'yourapp'

Use this:

  require 'camping/reloader'
  reloader = Camping::Reloader.new('/path/to/yourapp.rb')
  blog = reloader.apps[:Blog]
  wiki = reloader.apps[:Wiki]

The blog and wiki objects will behave exactly like your Blog and Wiki, but they will update themselves if yourapp.rb changes.

You can also give Reloader more than one script.

Methods

Public Class method: ::new(*scripts)

Creates the reloader, assigns a script to it and initially loads the application. Pass in the full path to the script, otherwise the script will be loaded relative to the current working directory.

     # File lib/camping/reloader.rb, line 135
135:     def initialize(*scripts)
136:       @scripts = []
137:       @apps = {}
138:       update(*scripts)
139:     end

Public Instance method: #clear()

Removes all the scripts from the reloader.

     # File lib/camping/reloader.rb, line 162
162:     def clear
163:       @scripts = []
164:       @apps = {}
165:     end

Public Instance method: #on_reload(&blk)

     # File lib/camping/reloader.rb, line 141
141:     def on_reload(&blk)
142:       @callback = blk
143:     end

Public Instance method: #reload!()

Simply calls reload! on all the Script objects.

     # File lib/camping/reloader.rb, line 177
177:     def reload!
178:       @apps = {}
179:       @scripts.each do |script|
180:         script.reload!
181:         @apps.update(script.apps)
182:       end
183:     end

Public Instance method: #script(app)

Returns the script which provided the given app.

     # File lib/camping/reloader.rb, line 168
168:     def script(app)
169:       @scripts.each do |script|
170:         return script if script.apps.values.include?(app)
171:       end
172:       
173:       false
174:     end

Public Instance method: #update(*scripts)

Updates the reloader to only use the scripts provided:

  reloader.update("examples/blog.rb", "examples/wiki.rb")
     # File lib/camping/reloader.rb, line 148
148:     def update(*scripts)
149:       old_scripts = @scripts.dup
150:       clear
151:       
152:       @scripts = scripts.map do |script|
153:         file = File.expand_path(script)
154:         old_scripts.detect { |s| s.file == file } or
155:         Script.new(script, @callback)
156:       end
157:       
158:       reload!
159:     end

Class Camping::Server < Rack::Server

Methods

Public Class method: ::new(*)

     # File lib/camping/server.rb, line 98
 98:     def initialize(*)
 99:       super
100:       @reloader = Camping::Reloader.new
101:       @reloader.on_reload do |app|
102:         if !app.options.has_key?(:dynamic_templates)
103:                       app.options[:dynamic_templates] = true
104:               end
105:               
106:         if !Camping::Models.autoload?(:Base) && options[:database]
107:           Camping::Models::Base.establish_connection(
108:             :adapter => 'sqlite3',
109:             :database => options[:database]
110:           )
111:         end
112:       end
113:     end

Public Instance method: #app()

     # File lib/camping/server.rb, line 164
164:     def app
165:       self
166:     end

Public Instance method: #call(env)

     # File lib/camping/server.rb, line 168
168:     def call(env)
169:       reload!
170:       apps = @reloader.apps
171: 
172:       case apps.length
173:       when 0
174:         index_page(apps)
175:       when 1
176:         apps.values.first.call(env)
177:       else
178:         apps.each do |name, app|
179:           mount = name.to_s.downcase
180:           case env["PATH_INFO"]
181:           when %r{^/#{mount}}
182:             env["SCRIPT_NAME"] = env["SCRIPT_NAME"] + $&
183:             env["PATH_INFO"] = $'
184:             return app.call(env)
185:           when %r{^/code/#{mount}}
186:             return [200, {'Content-Type' => 'text/plain', 'X-Sendfile' => @reloader.script(app).file}, []]
187:           end
188:         end
189:         
190:         index_page(apps)
191:       end
192:     end

Public Instance method: #default_options()

     # File lib/camping/server.rb, line 119
119:     def default_options
120:       super.merge({
121:         :Port => 3301,
122:         :database => Options::DB
123:       })
124:     end

Public Instance method: #find_scripts()

     # File lib/camping/server.rb, line 148
148:     def find_scripts
149:       scripts = options[:scripts].map do |path|
150:         if File.file?(path)
151:           path
152:         elsif File.directory?(path)
153:           Dir[File.join(path, '*.rb')]
154:         end
155:       end.flatten.compact
156:           
157:       @reloader.update(*scripts)
158:     end

Public Instance method: #index_page(apps)

     # File lib/camping/server.rb, line 194
194:     def index_page(apps)
195:       [200, {'Content-Type' => 'text/html'}, [TEMPLATE.result(binding)]]
196:     end

Public Instance method: #middleware()

     # File lib/camping/server.rb, line 126
126:     def middleware
127:       h = super
128:       h["development"].unshift [XSendfile]
129:       h
130:     end

Public Instance method: #opt_parser()

     # File lib/camping/server.rb, line 115
115:     def opt_parser
116:       Options.new
117:     end

Public Instance method: #reload!()

     # File lib/camping/server.rb, line 160
160:     def reload!
161:       find_scripts
162:     end

Public Instance method: #start()

     # File lib/camping/server.rb, line 132
132:     def start
133:       if options[:server] == "console"
134:         puts "** Starting console"
135:         reload!
136:         this = self
137:         eval("self", TOPLEVEL_BINDING).meta_def(:reload!) { this.reload!; nil }
138:         ARGV.clear
139:         IRB.start
140:         exit
141:       else
142:         name = server.name[/\w+$/]
143:         puts "** Starting #{name} on #{options[:Host]}:#{options[:Port]}"
144:         super
145:       end
146:     end

Class Camping::Server::Options < Object

Methods

Public Instance method: #parse!(args)

    # File lib/camping/server.rb, line 42
42:       def parse!(args)
43:         args = args.dup
44:         options = {}
45:         
46:         opt_parser = OptionParser.new("", 24, '  ') do |opts|
47:           opts.banner = "Usage: camping app1.rb app2.rb..."
48:           opts.define_head "#{File.basename($0)}, the microframework ON-button for ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
49:           opts.separator ""
50:           opts.separator "Specific options:"
51:           
52:           opts.on("-h", "--host HOSTNAME",
53:           "Host for web server to bind to (default is all IPs)") { |v| options[:Host] = v }
54:           
55:           opts.on("-p", "--port NUM",
56:           "Port for web server (defaults to 3301)") { |v| options[:Port] = v }
57:           
58:           db = DB.sub(HOME, '~/') if DB
59:           opts.on("-d", "--database FILE",
60:           "SQLite3 database path (defaults to #{db ? db : '<none>'})") { |db_path| options[:database] = db_path }
61:           
62:           opts.on("-C", "--console",
63:           "Run in console mode with IRB") { options[:server] = "console" }
64:           
65:           server_list = ["mongrel", "webrick", "console"]
66:           opts.on("-s", "--server NAME",
67:           "Server to force (#{server_list.join(', ')})") { |v| options[:server] = v }
68: 
69:           opts.separator ""
70:           opts.separator "Common options:"
71:           
72:           # No argument, shows at tail.  This will print an options summary.
73:           # Try it and see!
74:           opts.on_tail("-?", "--help", "Show this message") do
75:             puts opts
76:             exit
77:           end
78: 
79:           # Another typical switch to print the version.
80:           opts.on_tail("-v", "--version", "Show version") do
81:             puts Gem.loaded_specs['camping'].version
82:             exit
83:           end
84:         end
85:         
86:         opt_parser.parse!(args)
87:         
88:         if args.empty?
89:           puts opt_parser
90:           exit
91:         end
92:         
93:         options[:scripts] = args
94:         options
95:       end

Class Camping::Server::XSendfile < Object

Methods

Public Class method: ::new(app)

     # File lib/camping/server.rb, line 238
238:       def initialize(app)
239:         @app = app
240:       end

Public Instance method: #call(env)

     # File lib/camping/server.rb, line 242
242:       def call(env)
243:         status, headers, body = @app.call(env)
244:         
245:         if key = headers.keys.grep(/X-Sendfile/i).first
246:           filename = headers[key]
247:           content = open(filename,'rb') { | io | io.read}
248:           headers['Content-Length'] = size(content).to_s
249:           body = [content]
250:         end
251:         
252:         return status, headers, body
253:       end

Public Instance method: #size(str)

     # File lib/camping/server.rb, line 256
256:         def size(str)
257:           str.bytesize
258:         end

Public Instance method: #size(str)

     # File lib/camping/server.rb, line 260
260:         def size(str)
261:           str.size
262:         end

Module Camping::Session

Getting Started

To get sessions working for your application:

  1. require 'camping/session'
  2. Define a secret (and keep it secret): set :secret, "SECRET!"
  3. Mixin the module: include Camping::Session
  4. Throughout your application, use the @state var like a hash to store your application’s data.
  require 'camping/session'    # 1

  module Nuts
    set :secret, "Oh yeah!"    # 2
    include Camping::Session   # 3
  end

Other backends

Camping only ships with session-cookies. However, the @state variable is simply a shortcut for @env. Therefore you can also use any middleware which sets this variable:

  module Nuts
    use Rack::Session::Memcache
  end

Methods

Public Class method: ::included(app)

    # File lib/camping/session.rb, line 28
28:     def self.included(app)
29:       key    = "#{app}.state".downcase
30:       secret = app.options[:secret] || [__FILE__, File.mtime(__FILE__)].join(":")
31:       
32:       app.use Rack::Session::Cookie, :key => key, :secret => secret
33:     end

Module Camping::Views

Views is an empty module for storing methods which create HTML. The HTML is described using the Markaby language.

Defining and calling templates

Templates are simply Ruby methods with Markaby inside:

  module Blog::Views
    def index
      p "Welcome to my blog"
    end

    def show
      h1 @post.title
      self << @post.content
    end
  end

In your controllers you just call render :template_name which will invoke the template. The views and controllers will share instance variables (as you can see above).

Using the layout method

If your Views module has a layout method defined, it will be called with a block which will insert content from your view:

  module Blog::Views
    def layout
      html do
        head { title "My Blog "}
        body { self << yield }
      end
    end
  end

Methods