camping 2.1.532 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +72 -53
- data/Rakefile +25 -20
- data/bin/camping +1 -0
- data/book/01_introduction.md +6 -6
- data/book/02_getting_started.md +348 -267
- data/book/03_more_about_controllers.md +124 -0
- data/book/04_more_about_views.md +118 -0
- data/book/05_more_about_markaby.md +173 -0
- data/book/06_more_about_models.md +58 -0
- data/book/06_rules_of_thumb.md +143 -0
- data/book/07_philosophy.md +23 -0
- data/book/08_publishing_an_app.md +118 -0
- data/book/09_upgrade_notes.md +96 -0
- data/book/10_middleware.md +69 -0
- data/book/11_gear.md +50 -0
- data/examples/blog.rb +38 -38
- data/lib/camping/ar.rb +20 -5
- data/lib/camping/commands.rb +388 -0
- data/lib/camping/gear/filters.rb +48 -0
- data/lib/camping/gear/inspection.rb +32 -0
- data/lib/camping/gear/kuddly.rb +178 -0
- data/lib/camping/gear/nancy.rb +170 -0
- data/lib/camping/loads.rb +15 -0
- data/lib/camping/mab.rb +1 -1
- data/lib/camping/reloader.rb +22 -17
- data/lib/camping/server.rb +145 -70
- data/lib/camping/session.rb +8 -5
- data/lib/camping/template.rb +1 -2
- data/lib/camping/tools.rb +43 -0
- data/lib/camping/version.rb +6 -0
- data/lib/camping-unabridged.rb +360 -133
- data/lib/camping.rb +78 -47
- data/lib/campingtrip.md +341 -0
- data/test/app_camping_gear.rb +121 -0
- data/test/app_camping_tools.rb +1 -0
- data/test/app_config.rb +30 -0
- data/test/app_cookies.rb +1 -1
- data/test/app_file.rb +3 -3
- data/test/app_goes_meta.rb +23 -0
- data/test/app_inception.rb +39 -0
- data/test/app_markup.rb +5 -20
- data/test/app_migrations.rb +16 -0
- data/test/app_partials.rb +1 -1
- data/test/app_prefixed.rb +88 -0
- data/test/app_reloader.rb +1 -2
- data/test/app_route_generating.rb +69 -2
- data/test/app_sessions.rb +24 -2
- data/test/app_simple.rb +18 -18
- data/test/apps/migrations.rb +82 -82
- data/test/apps/misc.rb +1 -1
- data/test/gear/gear_nancy.rb +129 -0
- data/test/test_helper.rb +69 -12
- metadata +152 -92
- data/CHANGELOG +0 -145
- data/book/51_upgrading.md +0 -110
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
module Gear
|
|
2
|
+
|
|
3
|
+
# Nancy
|
|
4
|
+
#
|
|
5
|
+
# Nancy is Camping gear that adds Sinatra style routing shortcuts to the Object
|
|
6
|
+
# namespace and to camping controllers themselves:
|
|
7
|
+
#
|
|
8
|
+
# get '/' {
|
|
9
|
+
# "Hello World"
|
|
10
|
+
# }
|
|
11
|
+
#
|
|
12
|
+
# Calling the get method creates a controller, and in the event of no default
|
|
13
|
+
# app yet, creates an app named Frank.
|
|
14
|
+
module Nancy
|
|
15
|
+
|
|
16
|
+
class << self
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# normalizes the routes provided to the controller, then returns some variables
|
|
20
|
+
# used in make_camping_route
|
|
21
|
+
def normalize_routes(routes)
|
|
22
|
+
s = ""
|
|
23
|
+
rs = ""
|
|
24
|
+
routes.each do |r|
|
|
25
|
+
if r == '/'
|
|
26
|
+
r = 'Index'
|
|
27
|
+
end
|
|
28
|
+
rs += "'#{r}'" + ","
|
|
29
|
+
r.split("/").each(&:capitalize!).each{|t|
|
|
30
|
+
s << t.gsub(/[^a-z0-9A-Z ]/, '')
|
|
31
|
+
}
|
|
32
|
+
# s << r
|
|
33
|
+
end
|
|
34
|
+
rs.chop!
|
|
35
|
+
|
|
36
|
+
symbol = s.to_sym
|
|
37
|
+
{s: s, rs: rs, symbol: symbol}
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# ensures an app exists for the controllers.
|
|
41
|
+
def ensure_app(app)
|
|
42
|
+
if Camping::Apps.count == 0
|
|
43
|
+
# In the case of a naked sinatra style invokation
|
|
44
|
+
Camping.goes :Frank
|
|
45
|
+
m = Camping::Apps.first
|
|
46
|
+
else
|
|
47
|
+
m = app
|
|
48
|
+
end
|
|
49
|
+
m
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Make a camping route provided with a method type, a route, an optional app, and
|
|
53
|
+
# a required block:
|
|
54
|
+
#
|
|
55
|
+
# get '/another/thing' do
|
|
56
|
+
# render :another_view
|
|
57
|
+
# end
|
|
58
|
+
#
|
|
59
|
+
# Calling the shorthand make route helper methods inside of an app module, adds
|
|
60
|
+
# The route to that App. If you don't have any apps yet, then an app named Frank
|
|
61
|
+
# will be made for you.
|
|
62
|
+
def make_camping_route(method, routes, app=nil, &block)
|
|
63
|
+
|
|
64
|
+
inf = caller.first.split(":")
|
|
65
|
+
file_name, line_number = inf[0], inf[1]
|
|
66
|
+
|
|
67
|
+
meth = method.to_s
|
|
68
|
+
|
|
69
|
+
self.normalize_routes(routes) => {s:, rs:, symbol:}
|
|
70
|
+
|
|
71
|
+
m = self.ensure_app app
|
|
72
|
+
|
|
73
|
+
# Controller name
|
|
74
|
+
cname = "#{meth.capitalize}#{symbol.to_s}"
|
|
75
|
+
|
|
76
|
+
begin
|
|
77
|
+
m.module_eval(%Q[
|
|
78
|
+
module Controllers
|
|
79
|
+
class #{cname} < R #{rs}
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
], file_name, line_number.to_i)
|
|
83
|
+
rescue => error
|
|
84
|
+
if error.message.include? "superclass mismatch for class"
|
|
85
|
+
raise "You've probably tried to define the same route twice using the sinatra method. ['#{rs}']"
|
|
86
|
+
else
|
|
87
|
+
raise error
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# This is an interesting block. At times we'll pass an App to a route
|
|
92
|
+
# which will implicitly call it's `to_proc` method. In those cases, it's
|
|
93
|
+
# that block that is set as the block here, and it returns a Rack response.
|
|
94
|
+
# If we have a rack response instead of string, then we need to extract
|
|
95
|
+
# the response then reassign the values. the r method is a great helper
|
|
96
|
+
# for that.
|
|
97
|
+
constantine = m::X.const_get("#{cname}")
|
|
98
|
+
if block.arity == -1
|
|
99
|
+
constantine.send(:define_method, meth) { |*args|
|
|
100
|
+
block[*args]
|
|
101
|
+
}
|
|
102
|
+
elsif block.arity == 1
|
|
103
|
+
constantine.send(:define_method, meth) {
|
|
104
|
+
res = block[@env] # if we're forwarding a response
|
|
105
|
+
status = res[0]
|
|
106
|
+
headers = res[1]
|
|
107
|
+
body = res[2].flatten.first
|
|
108
|
+
r(status, body, headers)
|
|
109
|
+
}
|
|
110
|
+
else # assuming arity is 0
|
|
111
|
+
constantine.send(:define_method, meth) {
|
|
112
|
+
block[]
|
|
113
|
+
}
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
return nil
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def included(mod)
|
|
120
|
+
mod.extend(ClassMethods)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# required for compliance reasons
|
|
124
|
+
def setup(app, *a, &block) end
|
|
125
|
+
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
module ClassMethods
|
|
129
|
+
|
|
130
|
+
# Helper methods added to your Camping app that facilitates
|
|
131
|
+
def get(*routes, &block) Nancy.make_camping_route('get', routes, self, &block) end
|
|
132
|
+
def put(*routes, &block) Nancy.make_camping_route('put', routes, self, &block) end
|
|
133
|
+
def post(*routes, &block) Nancy.make_camping_route('post', routes, self, &block) end
|
|
134
|
+
def delete(*routes, &block) Nancy.make_camping_route('delete', routes, self, &block) end
|
|
135
|
+
def head(*routes, &block) Nancy.make_camping_route('head', routes, self, &block) end
|
|
136
|
+
def patch(*routes, &block) Nancy.make_camping_route('patch', routes, self, &block) end
|
|
137
|
+
def link(*routes, &block) Nancy.make_camping_route('link', routes, self, &block) end
|
|
138
|
+
def unlink(*routes, &block) Nancy.make_camping_route('unlink', routes, self, &block) end
|
|
139
|
+
|
|
140
|
+
# Turns this App into a proc to be consumed by one of the block based route generators
|
|
141
|
+
# An easy way to forward requests to an app.
|
|
142
|
+
# a references self, that's then captured by the proc, which is a closure.
|
|
143
|
+
# because it's a closure, and because it captures self, we can then call
|
|
144
|
+
# this proc anywhere we want.
|
|
145
|
+
#
|
|
146
|
+
# The syntax: `a[e]` is an implicit call to the `#call` method. the brackets
|
|
147
|
+
# are syntatic sugar to get this to work. The following code is equivalent:
|
|
148
|
+
#
|
|
149
|
+
# e = [] # given e is a rack array.
|
|
150
|
+
# a.call(e)
|
|
151
|
+
# a.(e)
|
|
152
|
+
# a[e]
|
|
153
|
+
#
|
|
154
|
+
# This code is defined in the Nancy Camping Gear. Specifically in it's
|
|
155
|
+
# ClassMethods module. ClassMethods is then extended onto our Camping app,
|
|
156
|
+
# Giving it the appearance of being a method of the module. In our cases
|
|
157
|
+
# Our modules are our Apps.
|
|
158
|
+
# The code:
|
|
159
|
+
#
|
|
160
|
+
# def to_proc = method(:call).to_proc
|
|
161
|
+
#
|
|
162
|
+
# First gets a `Method` object from the app, then converts it to a proc.
|
|
163
|
+
# In our case we just want call, so this makes the whole api pretty simple.
|
|
164
|
+
# def to_proc = method(:call).to_proc
|
|
165
|
+
def to_proc = method(:call).to_proc
|
|
166
|
+
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
end
|
|
170
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Loads things into camping.rb
|
|
2
|
+
|
|
3
|
+
# external dependencies
|
|
4
|
+
require "uri"
|
|
5
|
+
require "rack"
|
|
6
|
+
require 'rubygems'
|
|
7
|
+
require 'bundler/setup'
|
|
8
|
+
|
|
9
|
+
# internal dependencies
|
|
10
|
+
require 'camping/tools'
|
|
11
|
+
require 'camping/gear/filters'
|
|
12
|
+
require 'camping/gear/nancy'
|
|
13
|
+
require 'camping/gear/inspection'
|
|
14
|
+
require 'camping/gear/kuddly'
|
|
15
|
+
|
data/lib/camping/mab.rb
CHANGED
data/lib/camping/reloader.rb
CHANGED
|
@@ -33,13 +33,14 @@ module Camping
|
|
|
33
33
|
# You can also give Reloader more than one script.
|
|
34
34
|
class Reloader
|
|
35
35
|
attr_reader :file
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
def initialize(file, &blk)
|
|
38
38
|
@file = file
|
|
39
39
|
@mtime = Time.at(0)
|
|
40
40
|
@requires = []
|
|
41
41
|
@apps = {}
|
|
42
42
|
@callback = blk
|
|
43
|
+
|
|
43
44
|
end
|
|
44
45
|
|
|
45
46
|
def name
|
|
@@ -50,8 +51,8 @@ module Camping
|
|
|
50
51
|
File.basename(base).to_sym
|
|
51
52
|
end
|
|
52
53
|
end
|
|
53
|
-
|
|
54
|
-
# Loads the apps
|
|
54
|
+
|
|
55
|
+
# Loads the apps available in this script. Use <tt>apps</tt> to get
|
|
55
56
|
# the loaded apps.
|
|
56
57
|
def load_apps(old_apps)
|
|
57
58
|
all_requires = $LOADED_FEATURES.dup
|
|
@@ -62,7 +63,7 @@ module Camping
|
|
|
62
63
|
@requires = []
|
|
63
64
|
dirs = []
|
|
64
65
|
new_apps = Camping::Apps - all_apps
|
|
65
|
-
|
|
66
|
+
|
|
66
67
|
@apps = new_apps.inject({}) do |hash, app|
|
|
67
68
|
if file = app.options[:__FILE__]
|
|
68
69
|
full = File.expand_path(file)
|
|
@@ -72,7 +73,7 @@ module Camping
|
|
|
72
73
|
|
|
73
74
|
key = app.name.to_sym
|
|
74
75
|
hash[key] = app
|
|
75
|
-
|
|
76
|
+
|
|
76
77
|
if !old_apps.include?(key)
|
|
77
78
|
@callback.call(app) if @callback
|
|
78
79
|
app.create if app.respond_to?(:create)
|
|
@@ -87,18 +88,22 @@ module Camping
|
|
|
87
88
|
end
|
|
88
89
|
|
|
89
90
|
@mtime = mtime
|
|
90
|
-
|
|
91
|
+
|
|
91
92
|
self
|
|
92
93
|
end
|
|
93
94
|
|
|
95
|
+
# load_file
|
|
96
|
+
#
|
|
97
|
+
# Rack::Builder is mainly used to parse a config.ru file and to
|
|
98
|
+
# build a rack app with middleware from that.
|
|
94
99
|
def load_file
|
|
95
100
|
if @file =~ /\.ru$/
|
|
96
|
-
@app
|
|
101
|
+
@app = Rack::Builder.parse_file(@file)
|
|
97
102
|
else
|
|
98
103
|
load(@file)
|
|
99
104
|
end
|
|
100
105
|
end
|
|
101
|
-
|
|
106
|
+
|
|
102
107
|
# Removes all the apps defined in this script.
|
|
103
108
|
def remove_apps
|
|
104
109
|
@requires.each do |(path, full)|
|
|
@@ -112,7 +117,7 @@ module Camping
|
|
|
112
117
|
ensure
|
|
113
118
|
@apps.clear
|
|
114
119
|
end
|
|
115
|
-
|
|
120
|
+
|
|
116
121
|
# Reloads the file if needed. No harm is done by calling this multiple
|
|
117
122
|
# times, so feel free call just to be sure.
|
|
118
123
|
def reload
|
|
@@ -123,7 +128,7 @@ module Camping
|
|
|
123
128
|
def reload!
|
|
124
129
|
load_apps(remove_apps)
|
|
125
130
|
end
|
|
126
|
-
|
|
131
|
+
|
|
127
132
|
# Checks if both scripts watches the same file.
|
|
128
133
|
def ==(other)
|
|
129
134
|
@file == other.file
|
|
@@ -136,20 +141,20 @@ module Camping
|
|
|
136
141
|
@apps
|
|
137
142
|
end
|
|
138
143
|
end
|
|
139
|
-
|
|
144
|
+
|
|
140
145
|
private
|
|
141
|
-
|
|
146
|
+
|
|
142
147
|
def mtime
|
|
143
148
|
@requires.map do |(path, full)|
|
|
144
149
|
File.mtime(full)
|
|
145
150
|
end.reject {|t| t > Time.now }.max || Time.now
|
|
146
151
|
end
|
|
147
|
-
|
|
148
|
-
# Figures out the full path of a required file.
|
|
152
|
+
|
|
153
|
+
# Figures out the full path of a required file.
|
|
149
154
|
def full_path(req)
|
|
150
|
-
return req if File.
|
|
151
|
-
dir = $LOAD_PATH.detect { |l| File.
|
|
152
|
-
if dir
|
|
155
|
+
return req if File.exist?(req)
|
|
156
|
+
dir = $LOAD_PATH.detect { |l| File.exist?(File.join(l, req)) }
|
|
157
|
+
if dir
|
|
153
158
|
File.expand_path(req, File.expand_path(dir))
|
|
154
159
|
else
|
|
155
160
|
req
|
data/lib/camping/server.rb
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
require 'irb'
|
|
2
2
|
require 'erb'
|
|
3
3
|
require 'rack'
|
|
4
|
+
require 'rackup'
|
|
5
|
+
require 'camping/version'
|
|
4
6
|
require 'camping/reloader'
|
|
7
|
+
require 'camping/commands'
|
|
5
8
|
|
|
6
9
|
# == The Camping Server (for development)
|
|
7
10
|
#
|
|
8
11
|
# Camping includes a pretty nifty server which is built for development.
|
|
9
12
|
# It follows these rules:
|
|
10
|
-
#
|
|
11
|
-
# * Load all Camping apps in a
|
|
12
|
-
# * Load new apps that appear in that directory or that file.
|
|
13
|
+
#
|
|
14
|
+
# * Load all Camping apps in a file.
|
|
13
15
|
# * Mount those apps according to their name. (e.g. Blog is mounted at /blog.)
|
|
14
16
|
# * Run each app's <tt>create</tt> method upon startup.
|
|
15
17
|
# * Reload the app if its modification time changes.
|
|
@@ -19,109 +21,88 @@ require 'camping/reloader'
|
|
|
19
21
|
#
|
|
20
22
|
# Run it like this:
|
|
21
23
|
#
|
|
22
|
-
# camping examples/ # Mounts all apps in that directory
|
|
23
24
|
# camping blog.rb # Mounts Blog at /
|
|
24
25
|
#
|
|
25
26
|
# And visit http://localhost:3301/ in your browser.
|
|
26
27
|
module Camping
|
|
27
|
-
class Server <
|
|
28
|
+
class Server < Rackup::Server
|
|
28
29
|
class Options
|
|
29
|
-
|
|
30
|
-
DB = File.join(home, '.camping.db')
|
|
31
|
-
RC = File.join(home, '.campingrc')
|
|
32
|
-
elsif home = ENV['APPDATA'] # MSWIN
|
|
33
|
-
DB = File.join(home, 'Camping.db')
|
|
34
|
-
RC = File.join(home, 'Campingrc')
|
|
35
|
-
else
|
|
36
|
-
DB = nil
|
|
37
|
-
RC = nil
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
HOME = File.expand_path(home) + '/'
|
|
41
|
-
|
|
30
|
+
|
|
42
31
|
def parse!(args)
|
|
43
32
|
args = args.dup
|
|
44
33
|
options = {}
|
|
45
|
-
|
|
46
34
|
opt_parser = OptionParser.new("", 24, ' ') do |opts|
|
|
47
|
-
opts.banner = "Usage: camping my-camping-app.rb"
|
|
48
|
-
|
|
35
|
+
opts.banner = "Usage: camping Or: camping my-camping-app.rb"
|
|
36
|
+
|
|
37
|
+
# opts.define_head "#{File.basename($0)}, the microframework ON-button for ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
|
|
38
|
+
|
|
49
39
|
opts.separator ""
|
|
50
40
|
opts.separator "Specific options:"
|
|
51
|
-
|
|
41
|
+
|
|
52
42
|
opts.on("-h", "--host HOSTNAME",
|
|
53
43
|
"Host for web server to bind to (default is all IPs)") { |v| options[:Host] = v }
|
|
54
|
-
|
|
44
|
+
|
|
55
45
|
opts.on("-p", "--port NUM",
|
|
56
46
|
"Port for web server (defaults to 3301)") { |v| options[:Port] = v }
|
|
57
|
-
|
|
58
|
-
|
|
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",
|
|
47
|
+
|
|
48
|
+
opts.on("-c", "--console",
|
|
63
49
|
"Run in console mode with IRB") { options[:server] = "console" }
|
|
64
|
-
|
|
65
|
-
|
|
50
|
+
|
|
51
|
+
opts.on("-e", "--env ENVIRONMENT",
|
|
52
|
+
"Sets the environment. (defaults: development)") { |v| options[:environment] = ENV['environment'] = v }
|
|
53
|
+
|
|
54
|
+
server_list = ["thin", "webrick", "console", "puma", "tipi", "falcon"]
|
|
66
55
|
opts.on("-s", "--server NAME",
|
|
67
56
|
"Server to force (#{server_list.join(', ')})") { |v| options[:server] = v }
|
|
68
57
|
|
|
69
58
|
opts.separator ""
|
|
70
59
|
opts.separator "Common options:"
|
|
71
|
-
|
|
60
|
+
|
|
72
61
|
# No argument, shows at tail. This will print an options summary.
|
|
73
62
|
# Try it and see!
|
|
74
|
-
opts.
|
|
63
|
+
opts.on("-?", "--help", "Show this message") do
|
|
75
64
|
puts opts
|
|
76
65
|
exit
|
|
77
66
|
end
|
|
78
67
|
|
|
79
68
|
# Another typical switch to print the version.
|
|
80
|
-
opts.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
69
|
+
opts.on("-v", "--version", "Show version") { options[:version] = true }
|
|
70
|
+
|
|
71
|
+
# Show Routes
|
|
72
|
+
opts.on("-r", "--routes", "Show Routes") { options[:routes] = true }
|
|
73
|
+
|
|
84
74
|
end
|
|
85
|
-
|
|
75
|
+
|
|
86
76
|
opt_parser.parse!(args)
|
|
87
|
-
|
|
77
|
+
|
|
88
78
|
if args.empty?
|
|
89
|
-
|
|
90
|
-
exit
|
|
79
|
+
args << "camp.rb"
|
|
91
80
|
end
|
|
92
|
-
|
|
81
|
+
|
|
93
82
|
options[:script] = args.shift
|
|
94
83
|
options
|
|
95
84
|
end
|
|
96
85
|
end
|
|
97
|
-
|
|
86
|
+
|
|
98
87
|
def initialize(*)
|
|
99
88
|
super
|
|
100
89
|
@reloader = Camping::Reloader.new(options[:script]) do |app|
|
|
101
90
|
if !app.options.has_key?(:dynamic_templates)
|
|
102
91
|
app.options[:dynamic_templates] = true
|
|
103
92
|
end
|
|
104
|
-
|
|
105
|
-
if !Camping::Models.autoload?(:Base) && options[:database]
|
|
106
|
-
Camping::Models::Base.establish_connection(
|
|
107
|
-
:adapter => 'sqlite3',
|
|
108
|
-
:database => options[:database]
|
|
109
|
-
)
|
|
110
|
-
end
|
|
111
93
|
end
|
|
112
94
|
end
|
|
113
|
-
|
|
95
|
+
|
|
114
96
|
def opt_parser
|
|
115
97
|
Options.new
|
|
116
98
|
end
|
|
117
99
|
|
|
118
100
|
def default_options
|
|
119
101
|
super.merge({
|
|
120
|
-
:Port => 3301
|
|
121
|
-
:database => Options::DB
|
|
102
|
+
:Port => 3301
|
|
122
103
|
})
|
|
123
104
|
end
|
|
124
|
-
|
|
105
|
+
|
|
125
106
|
def middleware
|
|
126
107
|
h = super
|
|
127
108
|
h["development"] << [XSendfile]
|
|
@@ -129,6 +110,33 @@ module Camping
|
|
|
129
110
|
end
|
|
130
111
|
|
|
131
112
|
def start
|
|
113
|
+
|
|
114
|
+
commands = []
|
|
115
|
+
ARGV.each do |cmd|
|
|
116
|
+
commands << cmd
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Parse commands
|
|
120
|
+
case commands[0]
|
|
121
|
+
when "new"
|
|
122
|
+
Camping::Commands.new_cmd(commands[1])
|
|
123
|
+
exit
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
if options[:version] == true
|
|
127
|
+
puts "Camping v#{Camping::VERSION}"
|
|
128
|
+
exit
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
if options[:routes] == true
|
|
132
|
+
@reloader.reload!
|
|
133
|
+
r = @reloader
|
|
134
|
+
eval("self", TOPLEVEL_BINDING).meta_def(:reload!) { r.reload!; nil }
|
|
135
|
+
ARGV.clear
|
|
136
|
+
Camping::Commands.routes
|
|
137
|
+
exit
|
|
138
|
+
end
|
|
139
|
+
|
|
132
140
|
if options[:server] == "console"
|
|
133
141
|
puts "** Starting console"
|
|
134
142
|
@reloader.reload!
|
|
@@ -138,52 +146,119 @@ module Camping
|
|
|
138
146
|
IRB.start
|
|
139
147
|
exit
|
|
140
148
|
else
|
|
149
|
+
@reloader.reload!
|
|
150
|
+
r = @reloader
|
|
151
|
+
Camping.make_camp
|
|
141
152
|
name = server.name[/\w+$/]
|
|
142
153
|
puts "** Starting #{name} on #{options[:Host]}:#{options[:Port]}"
|
|
143
154
|
super
|
|
144
155
|
end
|
|
145
156
|
end
|
|
146
157
|
|
|
158
|
+
# defines the public directory to be /public
|
|
147
159
|
def public_dir
|
|
148
160
|
File.expand_path('../public', @reloader.file)
|
|
149
161
|
end
|
|
150
|
-
|
|
162
|
+
|
|
163
|
+
# add the public directory as a Rack app serving files first, then the
|
|
164
|
+
# current value of self, which is our camping apps, as an app.
|
|
151
165
|
def app
|
|
152
|
-
Rack::Cascade.new([Rack::
|
|
166
|
+
Rack::Cascade.new([Rack::Files.new(public_dir), self], [405, 404, 403])
|
|
153
167
|
end
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
168
|
+
|
|
169
|
+
# path_matches?
|
|
170
|
+
# accepts a regular expression string
|
|
171
|
+
# in our case our apps and controllers
|
|
172
|
+
def path_matches?(path, *reg)
|
|
173
|
+
p = T.(path)
|
|
174
|
+
reg.each do |r|
|
|
175
|
+
return true if Regexp.new(T.(r)).match?(p) && p == T.(r)
|
|
161
176
|
end
|
|
177
|
+
false
|
|
162
178
|
end
|
|
163
179
|
|
|
180
|
+
# Ensure trailing slash lambda
|
|
181
|
+
T ||= -> (u) {
|
|
182
|
+
u << "/" if u[-1] != "/"; u
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
# call(env) res
|
|
186
|
+
# == How routing works
|
|
187
|
+
#
|
|
188
|
+
# The first app added using Camping.goes is set at the root, we walk through
|
|
189
|
+
# the defined routes of the first app to see if there is a match.
|
|
190
|
+
# With no match we then walk through every other defined app.
|
|
191
|
+
# When we reach a matching route we call that app and Camping's router
|
|
192
|
+
# handles the rest.
|
|
193
|
+
#
|
|
194
|
+
# Mounting apps at different directories is now explicit by setting the
|
|
195
|
+
# url_prefix option:
|
|
196
|
+
#
|
|
197
|
+
# camping.goes :Nuts # Mounts Nuts at /
|
|
198
|
+
# module Auth
|
|
199
|
+
# set :url_prefix, "auth/"
|
|
200
|
+
# end
|
|
201
|
+
# camping.goes :Auth # Mounts Auth at /auth/
|
|
202
|
+
# camping.goes :Blog # Mounts Blog at /
|
|
203
|
+
#
|
|
204
|
+
# Note that routes that you set explicitly with R are not prefixed. This
|
|
205
|
+
# us explicit control over routes:
|
|
206
|
+
#
|
|
207
|
+
# module Auth::Controllers
|
|
208
|
+
# class Whatever < R '/thing/' # Mounts at /thing/
|
|
209
|
+
# def get
|
|
210
|
+
# render :some_view
|
|
211
|
+
# end
|
|
212
|
+
# end
|
|
213
|
+
# end
|
|
214
|
+
#
|
|
164
215
|
def call(env)
|
|
165
|
-
|
|
166
|
-
|
|
216
|
+
if ENV['environment'] == 'development'
|
|
217
|
+
@reloader.reload
|
|
218
|
+
Camping.make_camp
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# our switch statement iterates through possible app outcomes, no apps
|
|
222
|
+
# loaded, one app loaded, or multiple apps loaded.
|
|
223
|
+
case @reloader.apps.length
|
|
224
|
+
when 0
|
|
225
|
+
[200, {'content-type' => 'text/html'}, ["I'm sorry but no apps were found."]]
|
|
226
|
+
when 1
|
|
227
|
+
@reloader.apps.values.first.call(env) # When we have one
|
|
228
|
+
else
|
|
229
|
+
# 2 and up get special treatment
|
|
230
|
+
@reloader.apps.each do |name, app|
|
|
231
|
+
app.routes.each do |r|
|
|
232
|
+
if (path_matches?(env['PATH_INFO'], r))
|
|
233
|
+
return app.call(env)
|
|
234
|
+
next
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# Just return the first app if we didn't find a match.
|
|
240
|
+
@reloader.apps.values.first.call(env)
|
|
241
|
+
end
|
|
167
242
|
end
|
|
168
|
-
|
|
243
|
+
|
|
169
244
|
class XSendfile
|
|
170
245
|
def initialize(app)
|
|
171
246
|
@app = app
|
|
172
247
|
end
|
|
173
|
-
|
|
248
|
+
|
|
174
249
|
def call(env)
|
|
175
250
|
status, headers, body = @app.call(env)
|
|
176
|
-
|
|
251
|
+
|
|
177
252
|
if key = headers.keys.grep(/X-Sendfile/i).first
|
|
178
253
|
filename = headers[key]
|
|
179
254
|
content = open(filename,'rb') { | io | io.read}
|
|
180
255
|
headers['Content-Length'] = size(content).to_s
|
|
181
256
|
body = [content]
|
|
182
257
|
end
|
|
183
|
-
|
|
258
|
+
|
|
184
259
|
return status, headers, body
|
|
185
260
|
end
|
|
186
|
-
|
|
261
|
+
|
|
187
262
|
if "".respond_to?(:bytesize)
|
|
188
263
|
def size(str)
|
|
189
264
|
str.bytesize
|
data/lib/camping/session.rb
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
require 'rack/session'
|
|
2
|
+
class InsecureSecret < Exception #:nodoc: all
|
|
3
|
+
end
|
|
1
4
|
module Camping
|
|
2
5
|
# == Getting Started
|
|
3
6
|
#
|
|
@@ -9,7 +12,7 @@ module Camping
|
|
|
9
12
|
# to store your application's data.
|
|
10
13
|
#
|
|
11
14
|
# require 'camping/session' # 1
|
|
12
|
-
#
|
|
15
|
+
#
|
|
13
16
|
# module Nuts
|
|
14
17
|
# set :secret, "Oh yeah!" # 2
|
|
15
18
|
# include Camping::Session # 3
|
|
@@ -27,9 +30,9 @@ module Camping
|
|
|
27
30
|
module Session
|
|
28
31
|
def self.included(app)
|
|
29
32
|
key = "#{app}.state".downcase
|
|
30
|
-
secret = app.options[:secret] || [__FILE__, File.mtime(
|
|
31
|
-
|
|
32
|
-
app.use Rack::Session::Cookie, :key => key, :
|
|
33
|
+
secret = app.options[:secret] || ['camping-secret',__FILE__, File.mtime('Rakefile')].join(":")*2
|
|
34
|
+
raise InsecureSecret, "You're Session Secret is too short. Minimum length is 64." if secret.length < 64
|
|
35
|
+
app.use Rack::Session::Cookie, :key => key, :secrets => secret
|
|
33
36
|
end
|
|
34
37
|
end
|
|
35
|
-
end
|
|
38
|
+
end
|
data/lib/camping/template.rb
CHANGED
|
@@ -8,10 +8,9 @@ end
|
|
|
8
8
|
|
|
9
9
|
$TILT_CODE = %{
|
|
10
10
|
Template = Tilt
|
|
11
|
-
include Tilt::CompileSite unless self.options[:dynamic_templates]
|
|
12
11
|
}
|
|
13
12
|
|
|
14
|
-
Camping::S.sub!
|
|
13
|
+
Camping::S.sub!(/autoload\s*:Template\s*,\s*['"]camping\/template['"]/, $TILT_CODE)
|
|
15
14
|
Camping::Apps.each do |c|
|
|
16
15
|
c.module_eval $TILT_CODE
|
|
17
16
|
end
|