pax_global_header 0000666 0000000 0000000 00000000064 14270445267 0014525 g ustar 00root root 0000000 0000000 52 comment=2b629e572c8c430f399f5cf9ef49362efc8f52b0
camping-2.3/ 0000775 0000000 0000000 00000000000 14270445267 0013007 5 ustar 00root root 0000000 0000000 camping-2.3/.github/ 0000775 0000000 0000000 00000000000 14270445267 0014347 5 ustar 00root root 0000000 0000000 camping-2.3/.github/workflows/ 0000775 0000000 0000000 00000000000 14270445267 0016404 5 ustar 00root root 0000000 0000000 camping-2.3/.github/workflows/ruby.yml 0000664 0000000 0000000 00000001647 14270445267 0020120 0 ustar 00root root 0000000 0000000 # This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
name: Ruby
on: [push, pull_request]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ 'ubuntu-latest', 'macos-latest'] # 'windows-latest'
ruby: [ '3.0', 2.7, 2.6, 2.5, ruby-head ]
exclude:
- os: windows-latest
ruby: jruby-head
steps:
- uses: actions/checkout@v2
- uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
- name: Install dependencies
run: bundle install
- name: Run tests
run: bundle exec rake
camping-2.3/.gitignore 0000664 0000000 0000000 00000000025 14270445267 0014774 0 ustar 00root root 0000000 0000000 pkg
doc
Gemfile.lock
camping-2.3/.travis.yml 0000664 0000000 0000000 00000000473 14270445267 0015124 0 ustar 00root root 0000000 0000000 rvm:
- 1.8.7
- 1.9.3
- 2.2.0
sudo: false
env:
- "RACK='~>1.5.0'"
- "RACK='~>1.4.0'"
- "RACK='~>1.3.0'"
- "AR='~>2.3'"
- "AR='~>3.2.0'"
- "AR='~>4.1'"
install: bundle install --without development
script: bundle exec rake test
notifications:
email: false
irc:
- "irc.freenode.org#camping"
camping-2.3/CHANGELOG 0000664 0000000 0000000 00000014475 14270445267 0014234 0 ustar 00root root 0000000 0000000 = 2.3
=== 28th July, 2022
* New routes command line helper command.
* plugin support added via the gear method.
* Restored support for mounting multiple apps.
* Add url_prefix option for mounting apps at different urls.
* Increase camping test limit to 5120 bytes. We needed just a bit more space.
* Add a book stub about Models.
* Add a chapter about middleware and how it works.
* Camping a '/' is now forcibly terminating each route. May revert this back if we run into trouble.
= 2.2
=== Never Released
* Updated ActiveRecord migrations class to reference version 6.1
* Removed deprecated gemfile definitions
* Rename deprecated methods
* Get tests to pass
= 2.1
=== 19th Aug, 2010 (whyday)
* Helpers#R now calls to_param on any object it passes in
* Fix route generation issue with routes including "." (#22)
* Improved tests
* Improved 1.9 support
* Camping::Server is now built upon Rack::Server
* Add support for ERB, Haml etc through Tilt
* Introducing Camping.options and Camping#set
* Camping::Server only loads ActiveRecord when needed
= 2.0
=== 9th Apr, 2010
* Speed-up of Camping::Mab (thanks zimbatm!)
* @state is now an alias of @env['rack.session']
* Camping.use injects a Rack middleware.
* Update Flipbook to RDoc 2.4
* Removed old examples.
* Updated examples/blog.rb
* Camping::Apps returns!
* Session-cookies now timeout naturally (thanks jenna!)
* You can now `throw :halt` to halt the response in a helper.
* Camping::H#u is gone (was an alias to merge!)
* Camping::Session now uses session-cookies. The AR-backend is gone for now.
* camping/db.rb has been renamed to camping/ar.rb.
* Camping now uses Rack internally. Every app responds to #call.
= 1.6
=== Never released
* Camping::Apps removed, it wasn't reliable.
* bin/camping server kinds splitted in various files.
* NotFound and ServerError controllers changed to methods :
r404 : called when a controller was not found
r500 : called on uncaught exception
r501 : called on undefined method
All of those can be overridden at your taste.
* Markaby no longer required. Like AR, is it autoloaded on (Mab) usage.
* Camping::H is now inheriting from Hash instead of HashWithIndifferentAccess.
* Which made possible to remove the last strict dependency : active_support
* #errors_for removed, it wasn't really used
* Bug fixes !
= 1.5
=== 3rd Oct, 2006
* Camping::Apps stores an array of classes for all loaded apps.
* bin/camping can be given a directory. Like: camping examples/
* Console mode -- thank zimbatm. Use: camping -C yourapp.rb
* Call controllers with Camping.method_missing.
Tepee.get(:Index) #=> (Response)
Blog.post(:Delete, id) #=> (Response)
Blog.post(:Login, :input => {'username' => 'admin', 'password' => 'camping'})
#=> #
Blog.get(:Info, :env => {:HTTP_HOST => 'wagon'})
#=> #'wagon'} ...>
* Using \r\n instead of \n on output. FastCGI has these needs.
* ActiveRecord no longer required or installed.
* If you refer to Models::Base, however, ActiveRecord will be loaded with autoload. (see lib/camping/db.rb)
* new Camping::FastCGI.serve which will serve a whole directory of apps
(see http://code.whytheluckystiff.net/camping/wiki/TheCampingServer)
* ~/.campingrc can contain database connection info if you want your default to be something other than SQLite.
database:
adapter: mysql
username: camping
socket: /tmp/mysql.sock
password: NOFORESTFIRES
database: camping
* controllers are now *ordered*. uses the inherited hook to keep track of all
classes created with R. those classes are scanned, in order, when a request is
made. any other controllers are handled first. so if you plan on overriding the
urls method, be sure to subclass from R().
* Console mode will load .irbrc in the working directory, if present.
(for example, in my ~/git/balloon directory, i have this in the .irbrc:
include Balloon::Models
when camping -C balloon.rb gets run, the models all get included in main.)
* And, of course, many other bugfixes from myself and the loyal+kind zimbatm...
* Markaby updated to 0.5. (See its CHANGELOG.)
= 1.4.2
=== 18th May, 2006
* Efficient file uploads for multipart/form-data POSTs.
* Camping tool now uses Mongrel, if available. If not, sticks with WEBrick.
* Multiple apps can be loaded with the camping tool, each mounted according to their file name.
= 1.4.1
=== 3rd May, 2006
* Streaming HTTP support. If body is IO, will simply pass to the controller. Mongrel, in particular, supports this nicely.
= 1.4
=== 11th April, 2006
* Moved Camping::Controllers::Base to Camping::Base.
* Moved Camping::Controllers::R to Camping::R.
* New session library (lib/camping/session.rb).
* WEBrick handler (lib/camping/webrick.rb) and Mongrel handler (lib/camping/mongrel.rb).
* Helpers#URL, builds a complete URL for a route. Returns a URI object. This way relative links could just return self.URL.path.
* Base#initialize takes over some of Base#service's duties.
* ENV now available as @env in controllers and views.
* Beautiful multi-page docs without frames!
= 1.3
=== 28th January, 2006
* bin/camping: an application launcher.
* Camping.run(request, response) now changed to controller = Camping.run(request, env)
* This means outputting the response is the wrapper/server's job. See bin/camping, you can do a controller.to_s at the least.
* Controllers::Base.env is the new thread-safe home for ENV.
* The input hash now works more like Rails params. You can call keys
like methods or with symbols or strings.
* Queries are now parsed more like PHP/Rails, in that you can denote
structure with brackets: post[user]=_why;post[id]=2
* Auto-prefix table names, to help prevent name clash.
* Helpers.errors_for simple validation.
* Lots of empty :href and :action attributes, a bug.
* New single-page flipbook RDoc template.
= 1.2
=== 23rd January, 2006
* Camping.goes allows fresh modules build from all Camping parts.
* File uploads now supported (multipart/form-data).
* Helpers.R can rebuild routes.
* Helpers./ for tracing paths from the root.
= 1.1
=== 19th January, 2006
* Allowed request and response streams to be passed in, to allow WEBrick and FastCGI support.
= 1.0
=== 17th January, 2006
* Initial checkin, see announcement at http://redhanded.hobix.com/bits/campingAMicroframework.html.
camping-2.3/COPYING 0000664 0000000 0000000 00000002030 14270445267 0014035 0 ustar 00root root 0000000 0000000 Copyright (c) 2006 why the lucky stiff
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
camping-2.3/Gemfile 0000664 0000000 0000000 00000000527 14270445267 0014306 0 ustar 00root root 0000000 0000000 source 'https://rubygems.org'
gem 'rake'
gem 'rack'
gem 'mab'
group :extras do
gem 'tilt'
if ENV['AR']
gem 'activerecord', ENV['AR']
gem 'sqlite3'
end
end
group :development do
gem 'parser'
gem 'unparser'
end
group :test do
gem 'minitest', '~> 5.0'
gem 'minitest-reporters'
gem 'rack-test'
gem 'ruby_parser'
end
camping-2.3/README.md 0000664 0000000 0000000 00000006154 14270445267 0014274 0 ustar 00root root 0000000 0000000 
# Camping, a Microframework
Camping is a web framework which consistently stays at less than 4kB of code.
You can probably view the complete source code on a single page. But, you
know, it's so small that, if you think about it, what can it really do?
The idea here is to store a complete fledgling web application in a single
file like many small CGIs. But to organize it as a Model-View-Controller
application like Rails does. You can then easily move it to Rails once you've
got it going.
## A Camping Skeleton
A skeletal Camping blog could look like this:
```ruby
require 'camping'
Camping.goes :Blog
module Blog::Models
class Post < Base; belongs_to :user; end
class Comment < Base; belongs_to :user; end
class User < Base; end
end
module Blog::Controllers
class Index
def get
@posts = Post.find :all
render :index
end
end
end
module Blog::Views
def layout
html do
head { title "My Blog" }
body do
h1 "My Blog"
self << yield
end
end
end
def index
@posts.each do |post|
h1 post.title
end
end
end
```
## Installation
Interested yet? Luckily it's quite easy to install Camping. We'll be using
a tool called RubyGems, so if you don't have that installed yet, go grab it!
Once that's sorted out, open up a Terminal or Command Line and enter:
```
gem install camping
```
Even better, install the Camping Omnibus, a full package of recommended libs:
```
gem install camping-omnibus
```
If not, you should be aware of that Camping itself only depends on
[Rack](https://github.com/rack/rack), and if you're going to use the views you also
need to install **[markaby](https://github.com/markaby/markaby)**, and if you're going to use the database you need
**activerecord** as well.
```
gem install markaby
gem install activerecord
```
## Learning
First of all, you should read [the first chapters](/book/01_introduction.md)
of The Camping Book. It should hopefully get you started pretty quick. While
you're doing that, you should be aware of the _reference_ which contains
documentation for all the different parts of Camping.
[The wiki](https://github.com/camping/camping/wiki) is the place for all tiny,
useful tricks that we've collected over the years. Don't be afraid to share
your own discoveries; the more, the better!
And if there's anything you're wondering about, don't be shy, but rather
subscribe to [the mailing list](http://rubyforge.org/mailman/listinfo/camping-list)
and ask there. We also have an IRC channel over at Freenode, so if you feel
like chatting with us, you should join [#camping @ irc.freenode.net](http://java.freenode.net/?channel=camping).
## Authors
Camping was originally crafted by [why the lucky stiff](http://en.wikipedia.org/wiki/Why_the_lucky_stiff),
but is now maintained by the _community_. This simply means that if we like your
patch, it will be applied. Everything is managed through [the mailing list](http://rubyforge.org/mailman/listinfo/camping-list),
so just subscribe and you can instantly take part in shaping Camping.
camping-2.3/Rakefile 0000664 0000000 0000000 00000006743 14270445267 0014466 0 ustar 00root root 0000000 0000000 $:.unshift 'extras'
begin
require 'rake/dsl_definition'
require 'rake/alt_system'
rescue LoadError
else
begin
if defined?(Rake::DeprecatedObjectDSL)
Rake::DeprecatedObjectDSL.class_eval do
private_instance_methods(false).each do |meth|
remove_method meth
end
end
end
rescue Exception
end
end
require 'rake'
require 'rake/clean'
require 'rake/testtask'
require 'tempfile'
require 'open3'
require File.expand_path('../constants', __FILE__)
CLEAN.include ['**/.*.sw?', '*.gem', '.config', 'test/test.log', '.*.pt']
task :default => :check
## RDoc
begin
gem 'rdoc', '~>3.9.0'
rescue LoadError
task :docs do
puts "** Camping needs RDoc 3.9 in order to use the Flipbook template."
end
else
require 'rdoc/task'
RDoc::Task.new(:docs) do |rdoc|
# We have a recent version of RDoc, so let's use flipbook.
require 'rdoc/generator/singledarkfish'
rdoc.options += ['-f', 'singledarkfish', *RDOC_OPTS]
rdoc.template = "flipbook"
rdoc.rdoc_dir = 'doc'
rdoc.title = "Camping, a Microframework"
rdoc.rdoc_files.add ['README', 'lib/camping-unabridged.rb', 'lib/camping/**/*.rb', 'book/*']
end
end
task :rubygems_docs do
require 'rubygems/doc_manager'
def spec.installation_path; '.' end
def spec.full_gem_path; '.' end
manager = Gem::DocManager.new(spec)
manager.generate_rdoc
end
desc "Packages Camping."
task :package => :clean
## Tests
Rake::TestTask.new(:test) do |t|
t.libs << "test"
t.test_files = FileList['test/app_*.rb']
end
## Diff
desc "Compare camping and camping-unabridged"
task :diff do
require 'parser/current'
require 'unparser'
require 'pp'
u = Tempfile.new('unabridged')
m = Tempfile.new('mural')
usexp = Parser::CurrentRuby.parse(File.read("lib/camping-unabridged.rb"))
msexp = Parser::CurrentRuby.parse(File.read("lib/camping.rb"))
u << Unparser.unparse(usexp)
m << Unparser.unparse(msexp)
u.flush
m.flush
sh "diff -u #{u.path} #{m.path} | less"
u.delete
m.delete
end
error = false
## Check
task :check => ["test", "check:valid", "check:equal", "check:size", "check:lines", "check:exit"]
namespace :check do
desc "Check source code validity"
task :valid do
sh "ruby -c lib/camping.rb"
end
desc "Check equality between mural and unabridged"
task :equal do
require 'ruby_parser'
u = RubyParser.new.parse(File.read("lib/camping-unabridged.rb"))
m = RubyParser.new.parse(File.read("lib/camping.rb"))
u.reject! do |sexp|
sexp.is_a?(Sexp) and sexp[1] == s(:gvar, :$LOADED_FEATURES)
end
unless u == m
STDERR.puts "camping.rb and camping-unabridged.rb are not synchronized."
error = true
else
puts "✅ synchronized....."
end
end
SIZE_LIMIT = 5120
desc "Compare camping sizes to unabridged"
task :size do
FileList["lib/camping*.rb"].each do |path|
s = File.size(path)
puts "%21s : % 6d % 4d%%" % [File.basename(path), s, (100 * s / SIZE_LIMIT)]
end
if File.size("lib/camping.rb") > SIZE_LIMIT
STDERR.puts "lib/camping.rb: file is too big (> #{SIZE_LIMIT})"
error = true
end
end
desc "Verify that line length doesn't exceed 80 (120) chars for camping.rb"
task :lines do
i = 1
File.open("lib/camping.rb").each_line do |line|
if line.size > 121 # 1 added for \n
error = true
STDERR.puts "lib/camping.rb:#{i}: line too long (#{line[-10..-1].inspect})"
end
i += 1
end
end
task :exit do
exit 1 if error
end
end
camping-2.3/bin/ 0000775 0000000 0000000 00000000000 14270445267 0013557 5 ustar 00root root 0000000 0000000 camping-2.3/bin/camping 0000775 0000000 0000000 00000000661 14270445267 0015126 0 ustar 00root root 0000000 0000000 #!/usr/bin/env ruby
$:.unshift File.dirname(__FILE__) + "/../lib"
require 'camping'
# require 'camping-unabridged' # comment out the above line and uncomment this line to run camping unabridged with all sorts of stuff in it.
require 'camping/server'
begin
Camping::Server.start
rescue OptionParser::ParseError => ex
STDERR.puts "!! #{ex.message}"
puts "** use `#{File.basename($0)} --help` for more details..."
exit 1
end
camping-2.3/book/ 0000775 0000000 0000000 00000000000 14270445267 0013741 5 ustar 00root root 0000000 0000000 camping-2.3/book/01_introduction.md 0000664 0000000 0000000 00000001624 14270445267 0017307 0 ustar 00root root 0000000 0000000 # Introduction
Camping is a small web framework, less than 4k, a little white blood cell in
the vein of Rails. This little book will start with a tutorial which takes
about fifteen minutes - by the end you should have a little Camping site up.
The following chapters will eventually go deeper into how Camping, HTTP and
Rack work.
("Eventually", because these chapters are not written yet. This book is
currently a very much work in progress, and we'll be very grateful if you want
to help out.)
If you at any moment need some help or have any questions or comments, we
highly recommend [the mailing list](http://rubyforge.org/mailman/listinfo/camping-list)
which got plenty of nice people willing to help. We also have an IRC-channel
at [#camping @ irc.freenode.net](http://java.freenode.net/?channel=camping)
if you're into that sort of thing.
Enough talk. Ready? Let's ["get started"](02_getting_started.md).
camping-2.3/book/02_getting_started.md 0000664 0000000 0000000 00000030525 14270445267 0017760 0 ustar 00root root 0000000 0000000 # Getting Started
Start a new text file called nuts.rb. Here's what you put inside:
```ruby
Camping.goes :Nuts
```
Save it. Then, open a command prompt in the same directory. You'll want to
run:
```ruby
$ camping nuts.rb
```
And you should get a message which reads:
```ruby
** Camping running on 0.0.0.0:3301.
```
This means that right now The Camping Server is running on port 3301 on your
machine. Open your browser and visit http://localhost:3301/.
Your browser window should show:
```
Camping Problem!
/ Not found
```
No problem with that. The Camping Server is running, but it doesn't know what
to show. Let's tell him.
## Hello clock
So, you've got Camping installed and it's running. Keep it running. You can
edit files and The Camping Server will reload automatically. When you need to
stop the server, press Control-C.
Let's show something. At the bottom of nuts.rb add:
```ruby
module Nuts::Controllers
class Index < R '/'
def get
Time.now.to_s
end
end
end
```
Save the file and refresh the browser window. Your browser window should show
the time, e.g.
```
Sun Jul 15 12:56:15 +0200 2007
```
## Enjoying the view
The Camping microframework allows us to separate our code using the MVC
(Model-View-Controller) design pattern. Let's add a view to our Nuts
application. Replace the module Nuts::Controllers with:
```ruby
module Nuts::Controllers
class Index < R '/'
def get
@time = Time.now
render :sundial
end
end
end
```
```ruby
module Nuts::Views
def layout
html do
head do
title { "Nuts And GORP" }
end
body { self << yield }
end
end
def sundial
p "The current time is: #{@time}"
end
end
```
Save the file, refresh your browser window and it should show a message
like:
```
The current time is: Sun Jul 15 13:05:41 +0200 2013
```
And the window title reads "Nuts And GORP".
Here you can see we call render :sundial from our controller. This
does exactly what it says, and renders our sundial method. We've also
added a special method called layout which Camping will automatically
wrap our sundial output in. If you're familiar with HTML, you'll see that our
view contains what looks HTML tag names. This is Markaby, which is like
writing HTML using Ruby!
Soon enough, you'll find that you can return anything from the controller, and
it will be sent to the browser. But let's keep that for later and start
investigating the routes.
## Routes
You probably noticed the weird R '/' syntax in the previous page.
This is an uncommon feature of Ruby that is used in our favorite
microframework, to describe the routes which the controller can be accessed
on.
These routes can be very powerful, but we're going to have look at the
simplest ones first.
```ruby
module Nuts::Controllers
class Words < R '/welcome/to/my/site'
def get
"You got here by: /welcome/to/my/site"
end
end
class Digits < R '/nuts/(\d+)'
def get(number)
"You got here by: /nuts/#{number}"
end
end
class Segment < R '/gorp/([^/]+)'
def get(everything_else_than_a_slash)
"You got here by: /gorp/#{everything_else_than_a_slash}"
end
end
class DigitsAndEverything < R '/nuts/(\d+)/([^/]+)'
def get(number, everything)
"You got here by: /nuts/#{number}/#{everything}"
end
end
end
```
Add this to `nuts.rb` and try if you can hit all of the controllers.
Also notice how everything inside a parenthesis gets passed into the method,
and is ready at your disposal.
### Simpler routes
This just in:
```ruby
module Nuts::Controllers
class Index
def get
"You got here by: /"
end
end
class WelcomeToMySite
def get
"You got here by: /welcome/to/my/site"
end
end
class NutsN
def get(number)
"You got here by: /nuts/#{number}"
end
end
class GorpX
def get(everything_else_than_a_slash)
"You got here by: /gorp/#{everything_else_than_a_slash}"
end
end
class NutsNX
def get(number, everything)
"You got here by: /nuts/#{number}/#{everything}"
end
end
end
```
Drop the < R-part and it attemps to read your mind. It won't always
succeed, but it can simplify your application once in a while.
## Modeling the world
You can get pretty far with what you've learned now, and hopefully you've been
playing a bit off-book, but it's time to take the next step: Storing data.
Let's start over again.
```ruby
Camping.goes :Nuts
module Nuts::Models
class Page < Base
end
end
```
Obviously, this won't do anything, since we don't have any controllers, but
let's rather have a look at what we _do_ have.
We have a model named Page. This means we now can store wiki pages and
retrieve them later. In fact, we can have as many models as we want. Need one
for your users and one for your blog posts? Well, I think you already know how
to do it.
However, our model is missing something essential: a skeleton.
```ruby
Camping.goes :Nuts
module Nuts::Models
class Page < Base
end
class BasicFields < V 1.0
def self.up
create_table Page.table_name do |t|
t.string :title
t.text :content
# This gives us created_at and updated_at
t.timestamps
end
end
def self.down
drop_table Page.table_name
end
end
end
```
Now we have our first version of our model. It says:
```
If you want to migrate up to version one,
create the skeleton for the Page model,
which should be able to store,
"title" which is a string,
"content" which is a larger text,
"created_at" which is the time it was created,
"updated_at" which is the previous time it was updated.
If you want to migrate down from version one,
remove the skeleton for the Page model.
```
This is called a
[migration](http://api.rubyonrails.org/classes/ActiveRecord/Migration.html).
Whenever you want to change or add new models you simply add a new migration
below, where you increase the version number. All of these migrations builds
upon each other like LEGO blocks. Each new Migrations must have different
class's names, is a good idea name migration's explicit. For example:
```ruby
class AddTagColumn < V 1.1
def self.change
add_column Page.table_name, :tag, :string
Page.reset_column_information
end
end
```
Now we just need to tell Camping to use our migration. Write this at the bottom of nuts.rb
```ruby
def Nuts.create
Nuts::Models.create_schema
end
```
When The Camping Server boots up, it will automatically call
Nuts.create. You can put all kind of startup-code here, but right now
we only want to create our skeleton (or upgrade if needed). Start The Camping
Server again and observe:
```bash
$ camping nuts.rb
** Starting Mongrel on 0.0.0.0:3301
-- create_table("nuts_schema_infos")
-> 0.1035s
== Nuts::Models::BasicFields: migrating ===================================
-- create_table(:nuts_pages)
-> 0.0033s
== Nuts::Models::BasicFields: migrated (0.0038s) ==========================
```
Restart it, and enjoy the silence. There's no point of re-creating the
skeleton this time.
Before we go on, there's one rule you must know: Always place your models
before your migrations.
## Using our model
Let's explore how our model works by going into the _console_. The console
is good way to familiarize with your models. Test your models adding some
data by bare hand before addin it to the application.
```bash
$ camping -C nuts.rb
** Starting console
>>
```
Now it's waiting for your input, and will give you the answer when you press
Enter. Here's what I did, leaving out the boring answers. You should add your
own pages.
```bash
>> Page = Nuts::Models::Page
>> hiking = Page.new(:title => "Hiking")
>> hiking.content = "You can also set the values like this."
>> hiking.save
>> page = Page.find_by_title("Hiking")
=> #
>> page = Page.find(1)
=> #
>> page.title
>> page.content
>> page.created_at
>> page.updated_at
>> Page.find_by_title("Fishing")
=> nil
## Page.create automatically saves the page for you.
>> Page.create(:title => "Fishing", :content => "Go fish!")
>> Page.count
=> 2
```
Now I have two pages: One about hiking and one about fishing.
## Wrapping it up
Wouldn't it be nice if we could show this wonderful our pages in a browser?
Update nuts.rb so it also contains something like this:
```ruby
module Nuts::Controllers
class Pages
def get
# Only fetch the titles of the pages.
@pages = Page.all(:select => "title")
render :list
end
end
class PageX
def get(title)
@page = Page.find_by_title(title)
render :view
end
end
end
module Nuts::Views
def list
h1 "All pages"
ul do
@pages.each do |page|
li do
a page.title, :href => R(PageX, page.title)
end
end
end
end
def view
h1 @page.title
self << @page.content
end
end
```
Here we meet our first _helper_:
```ruby
R(PageX, page.title)
```
This is the reversed router and it generates a URL based on a
controller. R takes the controller you want to link to, followed by the router
parameters. . Instead of typing:
```ruby
:href=>'/welcome/to/my/site'
```
You can let Camping do the hard work for you.
```ruby
:href=>R(Words)
```
If the route would have some parameter, you shall write like this:
```ruby
:href=>R(WordsX,'someword')
```
Camping ships with a few, but very useful, helpers, and you can easily add your
owns. Have a look at Camping::Helpers for how you use these.
There's a lot of improvements you could do here. Let me suggest:
* Show when the page was created and last updated.
* What happens when the page doesn't exist?
* What should the front page show?
* Allow or disallow authenticated users.
* Add a layout.
* Jazz it up a bit.
Helpers can work generating Markaby's code. You could write a helper for show
some kind of data and call it from your views (Add a layout).
## The last touch
We have one major flaw in our little application. You can't edit or add new
pages. Let's see if we can fix that:
```ruby
module Nuts::Controllers
class PageX
def get(title)
if @page = Page.find_by_title(title)
render :view
else
redirect PageXEdit, title
end
end
def post(title)
# If it doesn't exist, initialize it:
@page = Page.find_or_initialize_by_title(title)
# This is the same as:
# @page = Page.find_by_title(title) || Page.new(:title => title)
@page.content = @input.content
@page.save
redirect PageX, title
end
end
class PageXEdit
def get(title)
@page = Page.find_or_initialize_by_title(title)
render :edit
end
end
end
```
The core of this code lies in the new post method in the PageX
controller. When someone types an address or follows a link, they'll end up at
the get method, but you can easily create a form which rather sends
you to the post when submitted.
There are other names you can use, but they won't always work. So for now,
don't be fancy and just stick to get and post. We'll show
you how this really works later.
You might also notice that we use @input.content. The
@input-hash contains any extra parameters sent, like those in the
forms and those in the URL (/posts?page=50).
Here's an edit-view, but you can probably do better. See if you can
integrate all of this with what you already have.
```ruby
module Nuts::Views
def edit
h1 @page.title
form :action => R(PageX, @page.title), :method => :post do
textarea @page.content, :name => :content,
:rows => 10, :cols => 50
br
input :type => :submit, :value => "Submit!"
end
end
end
```
## Phew.
You've taken quite a few steps in the last minutes. You deserve a break. But
let's recap for a moment:
* Always place Camping.goes :App at the top of your file.
* Every route ends at a controller, but ...
* ... the controller only delegates the work.
* @input contains the extra parameters.
* The views are HTML disguised as Ruby.
* They can access the instances variables (those that starts with a single
at-sign) from the controller.
* The models allows you to store all kinds of data.
* Place your models before your migrations.
* Helpers are helpful.
If you wanna dive deep, stay reading for a [more extended tutorial](03_more_about_controllers.md) step by step...
Enjoy the code
camping-2.3/book/03_more_about_controllers.md 0000664 0000000 0000000 00000010023 14270445267 0021343 0 ustar 00root root 0000000 0000000 # Controllers
What are these _controllers_? This is a good question for a Camping newb. In the [MVC
paradigm](http://en.wikipedia.org/wiki/Model%E2%80%93View%E2%80%93Controller#Overview)
a Models could be described as a very weird and hard to understand thing.
When you look in the browser's navigation bar, you will see something like:
`http://localhost:3301/welcome/to/my/site`
The Universal Resource Locator (URL) is showing you a "structured" web site
running inside a server that listening in the `3301` port. The site's internal
routes are "drawing" the path: Inside the `root_dir/` is the directory `/welcome/`
and it recursively adds the names of the deeper directories: "to", "my", and "site" (`./to/my/site`).
But that is virtual, the site doesn't really have a directory structure like that...
That would be useless. The site uses a "route drawing system" to get _control_
of that route; this is what *the controller* does.
## Camping Routes and Controllers
In camping, each _capitalized_ word in a camel-cased contoller declaration is like the
words between each slash in a URL. For example:
```ruby
WelcomeToMySite
```
will draw the route:
```
/welcome/to/my/site
```
Or you could instead use the weird R helper to get more specific control of your routes:
```ruby
Welcome < R '/welcome/to/my/site'
```
All of this will be declared inside the Nuts::Controllers module.
## Controller Parameters
Controllers can also handle your application's parameters. For example,
when the client asks for a route like `/post/1`, a static web server would
look out for the directory named "1" and serve the content in that directory.
It would need a lot of "number-named" directories to do that simple job.
But in our case, the controller draws a dynamic path for every asked post. We just need
to tell him about the size of the flock.
In camping, adding `N` or `X` to a controller's declaration tells it to expect a parameter.
The `N` suffix, which will match a numbered route, is declared like this:
```ruby
class PostN
```
With this controller, adding a number to the simple `/post` route in your browser will trigger this route. For example,
either of these will work:
```
/post/1
/post/99
```
But this `N` route will not match against a word. For example, a request for `/pots/mypost`
will return 404 (Not Found). Because the `PostN` declaration will only match _Numeric_ parameters.
If you would like to match something other than a number, you should use the `X` suffix:
```ruby
class PostX
```
The _X_ tells the controller to match anything, including number and words. For example, it will match:
```
/post/1
/post/99
/post/mypost
```
But it will NOT match: `/post/mypost/1` (or anything that has "/" in the name). Since slashes signify
deeper directories, you would need to tell the controller to recognize the deeper directory before using a parameter.
You can do this using camel case, followed by the "X" or "N":
```ruby
class PostMypostX
```
## Getting parameters from the controller
Ok, we have the controller that match parameters; and now what?
Say that you want to show the post number N requested in the controller. You'll need the
number.
```ruby
class PostN
def get number
p "the parameter was: #{number}"
end
end
```
Please, do not try that at home. It's very dirty to use a _view_ inside the controller (more on that soon).
The method `get`, for the `/post/N route`, will take a parameter. That number will by
inside the "number parameter". From now, if you want to route something to your
post route, you can write a link 100% pragmatically like this:
```ruby
@post=rand 1..9
a "See the post number: #{@post}",:href=>R(PostN,@post)
```
For that example, we just choose a random post and then displayed a link to its path.
> but you told me that I shouldn't write that in the controller...
Yep...I said that. These things are called "views" because they will
be in the user's face. Our client will not see the controllers; they will be hidden from them when they visit our site.
Our client will only see the [views](04_more_about_views.md).
camping-2.3/book/04_more_about_views.md 0000664 0000000 0000000 00000006502 14270445267 0020142 0 ustar 00root root 0000000 0000000 # Views
The view is the scene for our show. The user is sitting in his chair
(the browser) and see on screen actors (the view). Enjoy the show
without think that behind the scenes there is a whole team. The team
behind the cameras is our controller but the user don't care about
that.
The user only see their browser and our application is just and HTML document.
## Camping Views
Inside the Nut::Views module, we will write methods. That method shall called
with the render sentence. The views do not use class.
```ruby
module Nust::Views
def post_number
p "you asked the post number @postn"
end
end
```
Well, well, that was a views, but now: How we show it to the user? We will call
the view from the controller. And we pass to the view all the parameters that we
want to show.
```ruby
module Nuts::Controller
class PostN
def get number
@postn=number
render :post_number
end
end
end
```
We just declared a controller for the route /post/(number here). When the browser
ask for the route /post/1 the controller will be trigged and the get
method defined inside the class, will respond to the "get" request in
the web server.
The first instruction in our controller, will by write the number in the @postn
variable and then "render"
But what is `render`? This sentence is not from ruby, this is a camping's
sentence and mean: -show now the view named (:symbol). It take a symbol
as parameter, and the symbol's name, shall be one of these methods
declared in the views. Now we have only a view named post_number.
You could "associate" it MENTALLY as a hash like this:
```ruby
def my_view => render :my_view
```
But that will happen in your mind. In camping these will be happening in
the modules, not in a hash, therefore, their are very associated too.
Imagine your applications as a big building. The controller as the
corridors and the views as the offices. Where are the offices and we do
in each office?
## Views and Controllers
Model View and Controller, are joined but not scrambled. The views use
R(ControllerName) for call the controllers and "move". The controller
will use "render" for call the view.
-And now... what do you think we have behind the curtains?
```ruby
module Why::Controllers
class CircusCourtains
def get
require 'endertromb'
@monkey=Endertromb::Animals::StartMonkey.new :frog=>true,:place=>:hand
render :behind_curtain
end
end
end
```
-OMG! It's a monkey with a start in the hand!
-yes, ladies and gentleman, we have it just here for you
```ruby
module Views
def behind_curtain
p @monkey
end
end
```
No, we don't have it behind the curtains, but the user believe it. There
is the view, enjoy the show.
## Template engine
We spoke about the views, and HTML, but we are not using html's tags for
our view...
How happening this?
The rubyist have not necessary to write HTML code. Exists some options
named template. That the "p" before the `@monkey` in the view. A template
engine is a kind of "pseudo-language" for handle HTML code. In some
template engines you will also write HTML in a more easy way. Their also
handle ruby data inside HTML. Templates engine are the wheels of the
views.
In camping, you will write views using a template engine named
[Markaby](05_more_about_markaby.md) and you will write HTML pragmatically.
camping-2.3/book/05_more_about_markaby.md 0000664 0000000 0000000 00000012131 14270445267 0020427 0 ustar 00root root 0000000 0000000 ## Where Markaby's come from
A great musician, writer and programmer (and a bit crazy) named
[_why](http://en.wikipedia.org/wiki/Why_the_lucky_stiff) wrote camping. Before
banish himself to the dimension from where him come (a place named "the
keyhole"); hi also wanted, that the developers, should have not write pure HTML
code. Him dream with a world were the programmer write html in their
programming language.
Rails users are very hard guys. Their eat the soup using a fork and generate
HTML views in a template engine named Erb. Their use a long set of weird tools
for generate that Embedded Ruby (erb), their call it "scaffolding". The basic
idea is: "let me rails write html code for you" Many framework have in the
README a line that say: "support more populars template engines" but normally
peoples uses the default in the framework.
"Mab" is the template engine used by default in camping. _why Wrote the first
implementation named Markaby and then, judofyr and Jenna, power-up markaby
writing a more compact version of it.
While you are writing with mab, will see ruby code front you are eyes, but your
mind, will be seen pure HTML without tags. We will be "metaprograming HTML
code". In order to show a Header-1 tag, we just call a method named h1 and send
the content as parameter. It will make the dirty work writing all the tags just
like this:
```ruby
h1 'This is a header one'
```
"Write HTML pragmatically" mean: -write html using not html tags.
Markaby is a way for write Hyper Text Markup Language (HTML) using Ruby.
That do not mean "html knowledge unneeded"
But that is only the beginning, we can do more wild things.
## Writing HTML pragmatically
For example: if we want show a table for show users and their real
names:
```ruby
# call me using render :users
# from a get in the controllers
def users
table do
th 'User'
th 'Realname'
@users.each do |user,realname|
tr do
td user
td realname
end # tr
end # each
end # table
end # def
```
Take a look better [here](https://github.com/camping/mab/blob/master/README.md)
## Markaby and the layout
There is a special view named "layout". The layout, is view that will be
rendered each time any other view are rendered. In fact, the layout will take
the other views for compose himself. The layout is rendered before anything.
Each time you use the "render" sentence, you will be rendering the
layout and the desired view. Because that, wee need some "special"
tweaks in the layout. It must have a door with a poster that say:
"other view will enter using this door"
The layout will be rendered, and in some place you will put the other view.
This will be done with the "yield" sentence. Put the yield sentence whenever
you want rendering all the other views.
This is very useful, for example: We wan to write a footer in ALL the
pages that we are rendering. You can write the footer in every views
definition, or just write the footer in the layout.
Without a layout, if you render the table's views. Camping will drop out the
table just like that to the browser's render. It will not draw any body or head
tag. Writing body and head in every page could be a very bored task. But do not
worry, the layout is here.
Lets write our layout, with all the HTML shape and including a footer
```ruby
def layout
html do
head do
title 'My Blog'
end
body do
div.wrapper! do
self << yield
end
p.footer! do
text 'Powered by Camping'
end
end
end
end # layout
```
Well, that was wild. Let's see: First we have everything inside a block, the
html's block. At the next level, the first block is head, that is rendering
something like:
```html
My Blog
```
If you look at the HTML's source of camping, you will see a VERY LOOOONG
line with every the HTML code, better do not look it.
Then, come the more weird thing. A div with a estrange sentence:
```ruby
self << yield
```
That mean: -put just right here, the other view called.
In that place, in the center of the div.wrapper, will be placed all our
views called using render's sentence.
When you call:
```ruby
render :someview
```
Camping will rendering before, the layout view, and put all the content
of "someview" inside div.wrapper! You shall not write a lot of tag like
head or title.
Finally, it will be rendering a "p" named footer. That will be the footer in
all our pages.
## Tip
What would happen? If you do this in the layout:
```ruby
head do
title "#{@title}"
end
```
Hummm... You could "rewrite" the titles of each pages. In the
controller, you just need to declare the variable @title, and that will
be the title for that page.
Remember:
* Views take @variables from the controller
* Layout is rendered before any called view.
* Markaby is ruby code and it can be embedded
* View's modules use not `class` declarations
In the table example, we used a hash named @users. But. Where come from all
thats data?
It come from the controller, but the controller took it from the
[model](06_more_about_models.md). The M of the MVC, the layer who stare
the whole bunch of persistent data.
camping-2.3/book/06_more_about_models.md 0000664 0000000 0000000 00000003643 14270445267 0020275 0 ustar 00root root 0000000 0000000 ## More about Models
Models are used to persist data. That data could be anything. The balance in a bank account, A list of your favorite restaurants, your blog posts, you name it. Camping uses the *ActiveRecord* Gem, an ORM (object-relational mapper), that maps Database tables to objects.
We define models by inheriting from a base model named Base:
```ruby
class User < Base end
```
Very creative. Base is really just an alias for ActiveRecord, nothing fancy. We put our models into a namespaced module named after our App:
```ruby
Camping.goes :Nuts
module Nuts::Models
class User < Base end
end
```
Remember from earlier that Models need to be defined before our controllers, otherwise we can't use em. So keep them close to the top.
The new User model we've defined has a small problem, it's completely empty, it doesn't have any data that can be stored in it. Camping models map Database tables to objects automatically, but this model doesn't have a database table yet. To fix that we'll create a migration:
```ruby
Camping.goes :Nuts
module Nuts::Models
class User < Base; end
# Define a migration to add users
class AddUser < V 1.2
def self.up
create_table User.table_name do |t|
t.string :username
t.string :email
t.timestamps
end
end
def self.down
drop_table User.table_name
end
end
end
```
Databases, like birds, migrate. Migrations move our database from one configuration to another. In our case we're adding users. So cool. User's should be able to log in, sign up, maybe make some pages. We could make the next myspace. To get our database to make a users table we need force our app to create the schema.
```ruby
def Nuts.create
Nuts::Models.create_schema
end
```
Now that puppy will migrate when we launch our app.
Our Users now have greater hope to survive. So great. I love it.
camping-2.3/book/06_rules_of_thumb.md 0000664 0000000 0000000 00000007477 14270445267 0017624 0 ustar 00root root 0000000 0000000 # Keep it in one file
Generally, the idea is keep your app small and store in a single file. Your app will end up with four sections:
1. Camping setup
```ruby
do
require 'rubygems'
require 'camping'
Camping.goes :Blog
end
```
2. Models
```ruby
do
module Blog::Models
class User < Base; end
class Post < Base; belongs_to :user end
end
end
```
3. Controllers
```ruby
do
module Blog::Controllers
class Index < R '/'
def get; render :index end
end
end
end
```
4. Views
```ruby
do
module Blog::Views
def layout
html { body { self << yield } }
end
def index
div.page "Welcome!"
end
end
end
```
# What if things get out of hand?
If you’re piling up models and controllers, your file may begin to exceed 200 lines, which means lots of paging up and down. Go ahead and store your models, controllers and views in three separate files. Your directory structure should end up like this:
```
blog.rb
blog/
models.rb
controllers.rb
views.rb
```
(Note, for the development reloading to work, your required files (models.rb etc.) must be in a subdirectory named after your app.)
Your blog.rb would still contain the setup (No. 1):
```ruby
do
require 'rubygems'
require 'camping'
Camping.goes :Blog
require 'blog/helpers' # if needed
require 'blog/models'
require 'blog/views'
require 'blog/controllers'
end
```
# Small apps, many mounts
Rather than building huge Camping apps, the idea here is to write small apps which can each be mounted at directories on your web server. One restriction: these apps will share a database. However, this allows applications to access each other’s tables and simplifies setup and configuration.
To mount multiple camping apps, you can `require` the app files. When you mount more than one app they won't be mounted according to their parent directory, routing is explicit. If you'd like to give one of your apps a prefix, set the `url_prefix` option in your app:
```ruby
# camp.rb
require 'camping'
Camping.goes :Blog
require 'blog.rb'
Camping.goes :Wiki
require 'wiki.rb'
Wiki.set :url_prefix, 'tepee/'
Camping.goes :Charty
require 'charty.rb'
Charty.set :url_prefix, 'charts/'
```
By default Camping will look for a file called `camp.rb` in the root where Camping is executed. You can also supply a ruby filename to load your apps from there.
You’ll end up with:
```
http://localhost:3301/blog, a blogging app.
http://localhost:3301/tepee, a wiki app.
http://localhost:3301/charts, a charting app.
```
In your app, if you’re using the R() method to build your links, Camping will make sure the mount is added properly to links.
For example, if R(View, 1) is used in the blogging app mounted at /blog, the link will be written as /blog/view/1. If later you mount the blog at /articles instead, Camping will write the link as /articles/view/1.
# Give us a create method
```ruby
do
def Blog.create
# Code in here will run when the app starts, or reloads, but not when requests happen.
# You can use it to create database bits and pieces, files, folders, and so on.
end
end
```
# The camping server
The Camping Server is basically a set of rules. At the very least, The Camping Server must:
- Load all Camping apps in a directory.
- Load new apps that appear in that directory.
- Mount those apps according to their filename. (e.g. blog.rb 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.
# Bin/camping
Camping comes with a very simple version of The Camping Server. bin/camping uses either WEBrick or Mongrel (if you have it installed.)
Run it like this: `camping /var/www/camping/*.` It will follow all of the rules above.
camping-2.3/book/07_philosophy.md 0000664 0000000 0000000 00000006062 14270445267 0016773 0 ustar 00root root 0000000 0000000 # Why's origin story
The philosophy of Camping is a long and winding tail of origin, met by the ideals for the future of many mad hackers. The story of camping begins in 2006 on a cool winters night in Pittsburgh, where we find a hacker hunched over his computer, typing ruby code with his right hand and playing a laser theremin with his left. To the spooky sounds of his own left hand, he hacked through the night. This night, camping was born.
Lets step back though time for a minute though, to the origins of this man. Once an upstanding PHP developer, he grew weary and tired of his day job, writing endless login pages and checkouts. He dreamt of a world free of his C-flavoured prison. Tales of promised lands, where snake powered ponies run wild, dancing around campfires full of rubies glowing red as blood. They worked him to the bone, until one day his bones just up and left. He couldn't do it any longer! He was on a mission to find that fire which fueled his dreams.
In seclusion, there isn't much known about this odd man's life. Some say he went crazy. Others say he became a well respected professor. Still others suggest both of these are true. But what we do know, is that it is here, that he developed his love of chunky bacon, foxes, and children shaped like keyholes.
And so he went on, crafting his mad writings, scribblings of foxes explaining ruby symbols, and making strange music. Soon this man found himself concerned that children had no good way to make their own eBay competitors. For this reason, he created Camping.
The End.
# Actual philosophy
Why The Lucky Stiff is no longer around, so those of us who contributed early on to Camping have since become its caretakers. We continue to push the framework foward, to be more compact, to be faster, easier, more fun. These are our guiding principals:
- Camping is for everyone. It sure is great to turn an idea in to a single ruby file which creates a website. It's also great to organise an app in to several files sometimes. You can plug bits together all in a row, and grow your evil monkey in to an entire army of evil circus animals!
- Camping isn't for making money. You can make money using camping. Nobody will stop you. But we don't have any buzzwords to offer, we won't make your unit tests easier, nor help you do market research. Our main contributors certainly aren't using camping in large scale deployments, and while camping is blazingly fast, we have no idea how well it would work if you ran it on lots of servers!
- Camping is really simple. You don't need to know much, to make nifty things with it, and you can really easily add more detailed bits as needed. Your brain will thank you.
- Camping apps are easy to automatically reload. Because most of them are just one ruby file.
- Camping encourages experimentation. The whole thing is an experiment.
- If you're new to ruby, there are heaps of quirky hacks in here which will teach you all sorts of obscure, nifty, and outright strange things about the Ruby language.
- Camping doesn't take itself too seriously. We're a fun bunch, living on the edge of zany!
camping-2.3/book/08_publishing_an_app.md 0000664 0000000 0000000 00000015373 14270445267 0020265 0 ustar 00root root 0000000 0000000 Once you've built your Camping app, you'll almost certainly want to get it online some place, so others can get at it! There are tons of ways to do this, some easy, some hard, some free, and some expensive. Some of these techniques also come with limitations.
# Using a spare computer
Cost: Usually free; Limitations: None really; Extra Requirements: Need to have a computer you can leave on all the time.
This is probably the easiest way. Just find a computer you can leave on all the time, and use The Camping Server on Port 80, by running something like this in the Command Prompt or Terminal:
```
camping --port 80 myapp.rb
```
Then check to make sure it's working by visiting http://localhost:80/ on that computer. Next, you'll need to find out if your internet provider gives you a Static IP address, or a Dynamic IP. Most home internet providers give you a Dynamic IP, where most business accounts come with a Static IP. If you don't know, you can find out by phoning your internet provider.
# If you have a static IP address
This is really a great situation to be in. You'll next want to get a domain name of some sort. You can register one on a Domain Name Registrar, but this will generally cost about $12 per year to keep. You can also get a free subdomain name from services like afraid.org. Regardless how you do it, you'll want to create an A Record, and for it's value, provide your IP address. You can usually find out your IP address by visiting an IP Lookup Website, or by asking your Internet Provider.
# If you have a dynamic IP address
Your IP Address will change from time to time, so you'll need to use a Dynamic DNS service. Popular DDNS services include Afraid.org, DynDNS, and No-IP. These will either require you to run a little program on your computer to watch when your IP changes and update the domain name, or have you enter a special username and password in to certain home routers.
# Troubleshooting
If you find others can't access your website through the domain name, and you have a dynamic IP, your Internet Provider might be applying Port Blocking. If you phone them, they might be able to remove the blocks. Otherwise, you'll need to run the camping server on a different port. 8080 is a common alternative. Of note, you'll need to include :8080 (or whatever) after the domain name and before the slash in your web address for this to work. For example: http://funkytown.afraid.org:8080/ - otherwise, make sure the DDNS updating software is running and working correctly.
# Using Dreamhost
Cost: About $5 per month; Limitations: None really; Extra Requirements: SFTP or FTP software, and an SSH client like PuTTY on windows, or the Terminal on Linuxes and Mac OS.
Dreamhost have some really cheap hosting plans, and are quite easy to use. They include support for Rack on their shared hosting plans, and are fairly reliable and fast.
Enable the "Passenger (Ruby/Python apps only):" option in a domain's settings, from the Manage Domains page within the Dreamhost Panel. Ensure the "Shell account - allows SFTP/FTP plus ssh access." option is enabled on your main user, from the Manage Users page. Use your SSH software to login, and if you haven't already, set up rubygems. Once that's done, it's time to install stuff!
```
gem install camping-omnibus
```
Install any other gems you might need for your app, then use your SFTP or FTP software to upload the file to the folder labeled with your domain's name. Lastly, you'll need to add a few things:
A folder called 'tmp', containing a file called 'restart.txt', which you'll need to change whenever you need to make Dreamhost reload your app's source code (while you're trying things out, you can change this filename to 'always_restart.txt' to reload the app with each request).
A file called config.ru, containing something like:
```ruby
require 'rubygems'
ENV['GEM_PATH'] = File.join(File.dirname(__FILE__), '..', '.gems') if File.exist?('/dh')
Gem.clear_paths
require 'rack'
require 'blog.rb'
use Rack::ShowExceptions # optional, but helpful if there's any errors
Blog.create if Blog.respond_to? :create
run Blog
```
Make sure it's all uploaded, and your app should be online! Yay!
# Using Heroku
Cost: None for small apps; Limitations: Cannot change files in the filesystem, must use database; Extra Requirements: Need to install and use git to upload your app to the Heroku servers.
Someone should really add stuff to this section. For now, see How to run Camping 2.0 apps on Heroku or Heroku's own guide for Rack-based apps (scroll down for Camping).
# Using Google App Engine
Cost: None for small apps; Limitations: Cannot change files in the filesystem, must use the google database; Extra Requirements: ???.
Someone should also add information to this section. It's possible using jRuby somehow. I imagine this guide would help a lot!
# Using a CGI Webhost
Firstly, make sure your webhost can run ruby apps. Most can, thankfully. If you have shell access, you can check by entering 'which ruby' and seeing if it finds anything. Unfortunately, different webhosts work differently, so we can't provide specific instructions for installing rubygems on your webhost. If all else fails, you can extract the files from the lib folder within each gem, put the files in a folder, and add the big folder to your load path. Once you've done this, you'll just need to make sure your camping app file starts with:
```ruby
#!/usr/bin/ruby
```
And then either the simple postamble, or the complex postamble:
```ruby
# Plug it in to CGI
Rack::Handler::CGI.run(Blog)) if __FILE__ == $0
```
Then upload it to your server, and change the file's permissions to have all the executable bits set. You should now be able to visit the file using your web browser (for example, http://example.com/blog.rb). In fact, you don't even need the .rb. the #!/usr/bin/ruby line lets your server know what kind of file it is. :)
# Using a Rack Compatible Web Host
Follow your Webhost's instructions, and in the config.ru file, add a little something like this:
```ruby
... require 'blog' run Blog end
```
To set up Passenger and Rack for really easy deployment under Apache or Nginx see the screencasts on the Passenger site. If you have (e.g.) Apache on your computer, this is also good for local testing too.
You can also use Rack::URLMap to plug a whole bunch of different apps in to one folder. A camping app here, a rails project there, a sinatra doodad over there in the corner messing up the whole global namespace. The possibilities are severely limited!
# And Also Some Luck
We wish you good luck! Publishing your app to a server can be an annoying experience the first time you do it. Often little bugs will appear you never noticed before on your computer, or things just might not work for mysterious reasons. Stick to it, and don't be afraid to ask for help from those more battle hardened.
camping-2.3/book/09_upgrade_notes.md 0000664 0000000 0000000 00000005635 14270445267 0017443 0 ustar 00root root 0000000 0000000 # Upgrade notes
This document includes everything needed in order to upgrade your applications. If you’re looking for all the new features in a version, please have a look at the CHANGELOG in the Camping source.
# From 2.0 to 2.1
## Options
In Camping 2.1 there is now a built-in way to store options and settings. If you use cookie session, it means that you’ll now have to change to:
```ruby
module Nuts
set :secret, "Very secret text, which no-one else should know!"
include Camping::Session
end
```
# From 1.5 to 2.0
## Rack
The biggest change in 2.0 is that it now uses Rack internally. This means that you’ll now have to deploy Camping differently, but hopefully more easily too. Now every Camping application is also a Rack application, so simply check out the documentation to the server of your choice.
## Require 'camping/db'
In earlier versions of Camping, you loaded database support by:
```ruby
require 'camping/db'
```
Actually, this loaded a very thin layer on top of ActiveRecord, and in the future we want to experiment with other libraries. Therefore you should now simply remove the line, and instead just inherit from Base right away. This also means you’ll have to place your migrations after the models.
We also encourage you to use `Model.table_name` instead of `:appname_model`, just to make sure it’s named correctly.
```ruby
## Don't require anything:
# require 'camping/db'
module Nuts::Models
## Just inherit Base:
class Page < Base; end
## Migrations have to come *after* the models:
class CreateTheBasics < V 0.1
def self.up
create_table Page.table_name do |t|
...
end
end
def self.down
drop_table Page.table_name
end
end
end
```
## Cookie Sessions
Camping 2.0 now uses a cookie-based session system, which means you no longer need a database in order to use sessions. The disadvantage of this is that you are restricted to only around 4k of data. See below for the changes required, and see Camping::Session more details.
```ruby
module Nuts
## Include Camping::Session as before:
include Camping::Session
## But also define a secret:
secret "Very secret text, which no-one else should know!"
end
def Nuts.create
## And remove the following line:
# Camping::Models::Session.create_schema
end
```
## Error Handling
Camping now uses three methods in order to handle errors. These replaces the old classes NotFound and ServerError.
r404 is called when a route can’t be found.
r501 is called when a route is found, but doesn’t respond to the method.
r500 is called when an error happens.
You can override these in your application:
```ruby
module Nuts
def r404(path)
"Sorry, but I can't find #{path}."
end
def r501(method)
"Sorry, but I can't respond to #{method}."
end
def r500(klass, method, ex)
"Sorry, but #{klass}##{method} failed with #{ex}."
end
end
```
It should be noted that this might change in the future.
camping-2.3/book/10_middleware.md 0000664 0000000 0000000 00000003754 14270445267 0016711 0 ustar 00root root 0000000 0000000 Middleware in Camping is just Rack Middleware, and if you're familiar with Rack middleware then you'll be right at home. I'm going to assume that you have no idea what Rack or Rack middleware *is*, Let's learn about it.
# The HTTP Request
The web works in a request -> response pattern. When we *GO* to a webpage our browser is making a *request* to a web server. That request is processed and a *response* is sent. We have already covered how a response is mapped to our ruby code through controllers in Chapter 3. Those responses could be any of a number of things, HTML, an image, JSON, CSS, Web servers are pretty capable. When using Camping you'll generally respond with HTML, CSS, or Javascript, maybe some images.
As that request passes through your application you may need to make some decisions about it. Does the request try to get something with restricted access? Is it a request to a dynamic or cached file? Whatever it is we can write some Ruby code to make some decisions about that request before passing it along to Camping's router. That's what Middleware is for.
Middleware is Rack based and follows Rack Middleware conventions. Write a class, name it whatever you want but it must have an `#initialize` and a `#call` method.
```ruby
class MyMiddleware
def initialize(app)
@app = app
end
def call(env)
status, headers, body = @app.call(env)
[status, headers, body]
end
end
```
The `#initialize` method must store a reference to an `app` object that is passed in as a parameter. The `#call` method accepts an environment parameter that we call `env` and returns an array with 3 values: `status`, `headers`, and `body`. Now, when I first saw this I was confused, why do we immediately Call
### notes:
* really good railscast about it that's like... 13 years old: http://railscasts.com/episodes/151-rack-middleware?autoplay=true
* An extremely old article about it: https://web.archive.org/web/20150105094611/https://www.amberbit.com/blog/2011/07/13/introduction-to-rack-middleware/
camping-2.3/book/11_gear.md 0000664 0000000 0000000 00000002023 14270445267 0015477 0 ustar 00root root 0000000 0000000 # Pack Your Gear
Camping provides a way to include and expand Camping without messing with it's innards too much, we call these plugins gear.
To use gear you need to *pack* it into camping:
```ruby
Camping.goes :Blog
module Blog
pack Camping::Gear::CSRF
end
# or
Blog.pack Camping::Gear::CSRF
```
Define your gear by opening a module:
```ruby
module Royalty
def queens
@queens ||= [ "Beyonce", "Niki", "Doja"]
end
end
```
Gear define methods and helpers that are included in your app. Define a `ClassMethods` module to have class methods included:
```ruby
module Royalty
module ClassMethods
def secret_sauce
@_secret_sauce ||= SecureRandom.base64(32)
end
end
# /...
end
```
You can also supply a setup callback method that runs after your gear is packed:
```ruby
module Royalty
# Run a setup routine with this Gear.
def self.setup(app)
@app = app
@app.set :saucy_secret, "top_secret_sauce"
end
end
```
We'll be adding some really great gear soon. In the meantime, try making your own gear.
camping-2.3/camping-omnibus.gemspec 0000664 0000000 0000000 00000000100 14270445267 0017433 0 ustar 00root root 0000000 0000000 require File.expand_path('../constants', __FILE__)
camping_omni
camping-2.3/camping.gemspec 0000664 0000000 0000000 00000000100 14270445267 0015761 0 ustar 00root root 0000000 0000000 require File.expand_path('../constants', __FILE__)
camping_spec
camping-2.3/constants.rb 0000664 0000000 0000000 00000003140 14270445267 0015346 0 ustar 00root root 0000000 0000000 require 'rake'
NAME = "camping"
BRANCH = "2.3"
GIT = ENV['GIT'] || "git"
REV = `#{GIT} rev-list HEAD`.strip.split.length
VERS = ENV['VERSION'] || (REV.zero? ? BRANCH : [BRANCH, REV] * '.')
RDOC_OPTS = ["--line-numbers", "--quiet", "--main", "README"]
def camping_spec
@spec ||= Gem::Specification.new do |s|
s.name = NAME
s.version = VERS
s.platform = Gem::Platform::RUBY
# s.extra_rdoc_files = FileList["README.md", "CHANGELOG", "COPYING", "book/*"].to_a
s.rdoc_options += RDOC_OPTS + ['--exclude', '^(examples|extras)\/', '--exclude', 'lib/camping.rb']
s.summary = "miniature rails for anyone"
s.author = "why the lucky stiff"
s.email = 'why@ruby-lang.org'
s.homepage = 'http://camping.rubyforge.org/'
s.executables = ['camping']
s.add_dependency('rack', '>=1.0')
s.add_dependency('mab', '>=0.0.3')
s.required_ruby_version = '>= 1.8.2'
s.files = %w(COPYING README.md Rakefile) +
Dir.glob("{bin,doc,test,lib,extras,book}/**/*") +
Dir.glob("ext/**/*.{h,c,rb}") +
Dir.glob("examples/**/*.rb") +
Dir.glob("tools/*.rb")
s.require_path = "lib"
s.bindir = "bin"
end
end
def camping_omni
@omni ||= Gem::Specification.new do |s|
s.name = "camping-omnibus"
s.version = VERS
s.platform = Gem::Platform::RUBY
s.summary = "the camping meta-package for updating ActiveRecord, and SQLite3 bindings"
%w[author email homepage].each { |x| s.__send__("#{x}=", camping_spec.__send__(x)) }
s.add_dependency('camping', ">=#{BRANCH}")
s.add_dependency('activerecord')
s.add_dependency('sqlite3', '>=1.1.0.1')
end
end
camping-2.3/examples/ 0000775 0000000 0000000 00000000000 14270445267 0014625 5 ustar 00root root 0000000 0000000 camping-2.3/examples/README 0000664 0000000 0000000 00000000357 14270445267 0015512 0 ustar 00root root 0000000 0000000 If you've got camping installed, why not run all these examples using TheCampingServer?
From this directory, run: `camping blog.rb` for example.
NOTE: You'll need the active_record and acts_as_versioned gems installed for some of these.
camping-2.3/examples/blog.rb 0000775 0000000 0000000 00000016462 14270445267 0016111 0 ustar 00root root 0000000 0000000 #!/usr/bin/env ruby
$:.unshift File.dirname(__FILE__) + '/../lib'
require 'camping'
require 'camping/ar'
require 'camping/session'
require 'redcloth'
Camping.goes :Blog
module Blog
include Camping::Session
module Models
class Post < Base
belongs_to :user
before_save do |record|
cloth = RedCloth.new(record.body)
cloth.hard_breaks = false
record.html_body = cloth.to_html
end
end
class Comment < Base; belongs_to :user; end
class User < Base; end
class BasicFields < V 1.1
def self.up
create_table :blog_posts, :force => true do |t|
t.integer :user_id, :null => false
t.string :title, :limit => 255
t.text :body, :html_body
t.timestamps
end
create_table :blog_users, :force => true do |t|
t.string :username, :password
end
create_table :blog_comments, :force => true do |t|
t.integer :post_id, :null => false
t.string :username
t.text :body, :html_body
t.timestamps
end
User.create :username => 'admin', :password => 'camping'
end
def self.down
drop_table :blog_posts
drop_table :blog_users
drop_table :blog_comments
end
end
end
module Controllers
class Index
def get
@posts = Post.all(:order => 'updated_at DESC')
render :index
end
end
class PostNew
def get
require_login!
@post = Post.new
render :add
end
def post
require_login!
post = Post.create(:title => input.post_title, :body => input.post_body,
:user_id => @state.user_id)
redirect PostN, post
end
end
class PostN
def get(post_id)
@post = Post.find(post_id)
render :view
end
end
class Edit < R '/post/(\d+)/edit'
def get(post_id)
require_login!
@post = Post.find(post_id)
render :edit
end
def post(post_id)
require_login!
@post = Post.find(post_id)
@post.update :title => input.post_title, :body => input.post_body
redirect PostN, @post
end
end
class Login
def get
render :login
end
def post
@user = User.find_by_username_and_password(input.username, input.password)
if @user
@state.user_id = @user.id
redirect R(Index)
else
@info = 'Wrong username or password.'
end
render :login
end
end
class Logout
def get
@state.user_id = nil
redirect Index
end
end
class Style < R '/styles\.css'
STYLE = File.read(__FILE__).gsub(/.*__END__/m, '')
def get
@headers['Content-Type'] = 'text/css; charset=utf-8'
STYLE
end
end
end
module Helpers
def logged_in?
!!@state.user_id
end
def require_login!
unless logged_in?
redirect Controllers::Login
throw :halt
end
end
end
module Views
def layout
html do
head do
title 'My Blog'
link :rel => 'stylesheet', :type => 'text/css',
:href => '/styles.css', :media => 'screen'
end
body do
h1 { a 'My Blog', :href => R(Index) }
div.wrapper! do
self << yield
end
hr
p.footer! do
if logged_in?
_admin_menu
else
a 'Login', :href => R(Login)
text ' to the adminpanel'
end
text! ' – Powered by '
a 'Camping', :href => 'http://camping.rubyforge.org/'
end
end
end
end
def index
if @posts.empty?
h2 'No posts'
p do
text 'Could not find any posts. Feel free to '
a 'add one', :href => R(PostNew)
text ' yourself. '
end
else
@posts.each do |post|
_post(post)
end
end
end
def login
h2 'Login'
p.info @info if @info
form :action => R(Login), :method => 'post' do
input :name => 'to', :type => 'hidden', :value => @to if @to
label 'Username', :for => 'username'
input :name => 'username', :id => 'username', :type => 'text'
label 'Password', :for => 'password'
input :name => 'password', :id => 'password', :type => 'password'
input :type => 'submit', :class => 'submit', :value => 'Login'
end
end
def add
_form(@post, :action => R(PostNew))
end
def edit
_form(@post, :action => R(Edit, @post))
end
def view
_post(@post)
end
# partials
def _admin_menu
text! [['Log out', R(Logout)], ['New', R(PostNew)]].map { |name, to|
mab { a name, :href => to}
}.join(' – ')
end
def _post(post)
h2 { a post.title, :href => R(PostN, post) }
p.info do
text! "Written by #{post.user.username} "
text post.updated_at.strftime('%B %M, %Y @ %H:%M ')
_post_menu(post)
end
text! post.html_body
end
def _post_menu(post)
if logged_in?
a '(edit)', :href => R(Edit, post)
end
end
def _form(post, opts)
form({:method => 'post'}.merge(opts)) do
label 'Title', :for => 'post_title'
input :name => 'post_title', :id => 'post_title', :type => 'text',
:value => post.title
label 'Body', :for => 'post_body'
textarea post.body, :name => 'post_body', :id => 'post_body'
input :type => 'hidden', :name => 'post_id', :value => post.id
input :type => 'submit', :class => 'submit', :value => 'Submit'
end
end
end
end
def Blog.create
Blog::Models.create_schema :assume => (Blog::Models::Post.table_exists? ? 1.0 : 0.0)
end
__END__
* {
margin: 0;
padding: 0;
}
body {
font: normal 14px Arial, 'Bitstream Vera Sans', Helvetica, sans-serif;
line-height: 1.5;
}
h1, h2, h3, h4 {
font-family: Georgia, serif;
font-weight: normal;
}
h1 {
background-color: #EEE;
border-bottom: 5px solid #6F812D;
outline: 5px solid #9CB441;
font-weight: normal;
font-size: 3em;
padding: 0.5em 0;
text-align: center;
}
h2 {
margin-top: 1em;
font-size: 2em;
}
h1 a { color: #143D55; text-decoration: none }
h1 a:hover { color: #143D55; text-decoration: underline }
h2 a { color: #287AA9; text-decoration: none }
h2 a:hover { color: #287AA9; text-decoration: underline }
#wrapper {
margin: 3em auto;
width: 700px;
}
p {
margin-bottom: 1em;
}
p.info, p#footer {
color: #999;
margin-left: 1em;
}
p.info a, p#footer a {
color: #999;
}
p.info a:hover, p#footer a:hover {
text-decoration: none;
}
a {
color: #6F812D;
}
a:hover {
color: #9CB441;
}
hr {
border-width: 5px 0;
border-style: solid;
border-color: #9CB441;
border-bottom-color: #6F812D;
height: 0;
}
p#footer {
font-size: 0.9em;
margin: 0;
padding: 1em;
text-align: center;
}
label {
display: block;
width: 100%;
}
input, textarea {
padding: 5px;
margin-bottom: 1em;
margin-right: 490px;
width: 200px;
}
input.submit {
width: auto;
}
textarea {
font: normal 14px Arial, 'Bitstream Vera Sans', Helvetica, sans-serif;
height: 300px;
width: 400px;
}
camping-2.3/extras/ 0000775 0000000 0000000 00000000000 14270445267 0014315 5 ustar 00root root 0000000 0000000 camping-2.3/extras/images/ 0000775 0000000 0000000 00000000000 14270445267 0015562 5 ustar 00root root 0000000 0000000 camping-2.3/extras/images/badge.gif 0000664 0000000 0000000 00000024741 14270445267 0017323 0 ustar 00root root 0000000 0000000 GIF89a 3 f 3 33 3f 3 3 3 f f3 ff f f f 3 f 3 f ̙ 3 f 3 3 33 f3 3 3 33 33333f3333333f 3f33ff3f3f3f3 333f3333 333f3̙333 333f333f f 3f ff f f f3 f33f3ff3f3f3ff ff3ffffffffff f3ffffff f3fff̙fff f3fffff 3 f ̙ 3 333f33̙3f f3ffff̙f 3f̙ 3f̙̙ 3f̙ 3 f 3 333f333f f3fffff̙ ̙3̙f̙̙̙ 3f̙ 3f 3 f 3 333f333f f3fffff 3f 3f̙ 3fM ! , H*\ȰÇ#JHŋ3jȱǏ?
TG$L@\2:{`~g+5|BIM,
H
= d
ўR
4ui =4yB2L3ZaʊCΚ
Ī"lʓO)RLMbJV R
1TM*K{3?eRn-hN
m[hp@Y-&\4ĩ"=Zkd%fpQ^ر%{7VȝG[57y·֭t]g=U!Wy֜'x%('\pvU^T_,&X{x\3`7`%X"ȕZbjDvVy65,8)W\rX%qФVuML
|\) +g\Msz]eZF,b^j`p2:5W d M
[[{<Te_iy'䊄h̀Ҝ يuWUkY9"˽9%4Zq=byN:nRɊJحRYa]ZԲG|Ι颜!VLzSIxXضT+b+&ն)cECUr(dYY{@wp{ rma RWx|tqX<kMʘ/iLR=zwc^l+2A]-0C3u4D1,Wl=),_ \)}ԹGmrl{E3R4М}^\1tWHxX}dsJxyy[y.M4^,93D92'2Mr0ⴰwUOHka^Qx1]_9
dK9 8ʆ?ͬfWV\P1ݮTEYL 2MZp_Pb# w6,4laZ\9ax`=4=R)(X!9|ADSu@,S[v<غVr_$
1<-Ӹ3(TUCE1Myz. 30ix?%kC~^[d5agH3GIx/b\J2f'<p Ke$2RE" :q6t*AP'1aA\<~k