gin 1.0.4 → 1.1.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.
- data/History.rdoc +16 -0
- data/Manifest.txt +12 -2
- data/README.rdoc +33 -0
- data/Rakefile +5 -0
- data/TODO.rdoc +7 -0
- data/bin/gin +158 -0
- data/lib/gin.rb +19 -2
- data/lib/gin/app.rb +420 -173
- data/lib/gin/cache.rb +65 -0
- data/lib/gin/config.rb +174 -18
- data/lib/gin/constants.rb +4 -0
- data/lib/gin/controller.rb +219 -11
- data/lib/gin/core_ext/gin_class.rb +16 -0
- data/lib/gin/errorable.rb +16 -2
- data/lib/gin/filterable.rb +29 -19
- data/lib/gin/reloadable.rb +1 -1
- data/lib/gin/request.rb +11 -0
- data/lib/gin/router.rb +185 -61
- data/lib/gin/rw_lock.rb +109 -0
- data/lib/gin/test.rb +702 -0
- data/public/gin.css +15 -3
- data/test/app/layouts/bar.erb +9 -0
- data/test/app/layouts/foo.erb +9 -0
- data/test/app/views/bar.erb +1 -0
- data/test/mock_app.rb +94 -0
- data/test/mock_config/invalid.yml +2 -0
- data/test/test_app.rb +160 -45
- data/test/test_cache.rb +57 -0
- data/test/test_config.rb +108 -13
- data/test/test_controller.rb +201 -11
- data/test/test_errorable.rb +1 -1
- data/test/test_gin.rb +9 -0
- data/test/test_helper.rb +3 -1
- data/test/test_router.rb +33 -0
- data/test/test_rw_lock.rb +65 -0
- data/test/test_test.rb +627 -0
- metadata +86 -6
- data/.autotest +0 -23
- data/.gitignore +0 -7
data/History.rdoc
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
=== 1.1.0 / 2013-07-10
|
2
|
+
|
3
|
+
* Major Enhancements
|
4
|
+
* Test helper methods and assertions
|
5
|
+
* Template/View support with tilt
|
6
|
+
* New config implementation with auto-(re)loading
|
7
|
+
* App instances are individually configurable
|
8
|
+
* Gin command for easy new app generation
|
9
|
+
|
10
|
+
* Minor Enhancements
|
11
|
+
* Routes now support multiple embedded params between slashes
|
12
|
+
|
13
|
+
* Bugfixes
|
14
|
+
* Lots of thread-safety fixes
|
15
|
+
* Fixes to rare reloader path issue
|
16
|
+
|
1
17
|
=== 1.0.4 / 2013-03-19
|
2
18
|
|
3
19
|
* Minor Enhancements
|
data/Manifest.txt
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
.autotest
|
2
|
-
.gitignore
|
3
1
|
History.rdoc
|
4
2
|
Manifest.txt
|
5
3
|
README.rdoc
|
6
4
|
Rakefile
|
5
|
+
bin/gin
|
7
6
|
lib/gin.rb
|
8
7
|
lib/gin/app.rb
|
8
|
+
lib/gin/cache.rb
|
9
9
|
lib/gin/config.rb
|
10
10
|
lib/gin/constants.rb
|
11
11
|
lib/gin/controller.rb
|
@@ -18,7 +18,9 @@ lib/gin/reloadable.rb
|
|
18
18
|
lib/gin/request.rb
|
19
19
|
lib/gin/response.rb
|
20
20
|
lib/gin/router.rb
|
21
|
+
lib/gin/rw_lock.rb
|
21
22
|
lib/gin/stream.rb
|
23
|
+
lib/gin/test.rb
|
22
24
|
public/400.html
|
23
25
|
public/404.html
|
24
26
|
public/500.html
|
@@ -29,10 +31,16 @@ public/gin_sm.png
|
|
29
31
|
test/app/app_foo.rb
|
30
32
|
test/app/controllers/app_controller.rb
|
31
33
|
test/app/controllers/foo_controller.rb
|
34
|
+
test/app/layouts/bar.erb
|
35
|
+
test/app/layouts/foo.erb
|
36
|
+
test/app/views/bar.erb
|
32
37
|
test/mock_config/backend.yml
|
38
|
+
test/mock_config/invalid.yml
|
33
39
|
test/mock_config/memcache.yml
|
34
40
|
test/mock_config/not_a_config.txt
|
41
|
+
test/mock_app.rb
|
35
42
|
test/test_app.rb
|
43
|
+
test/test_cache.rb
|
36
44
|
test/test_config.rb
|
37
45
|
test/test_controller.rb
|
38
46
|
test/test_errorable.rb
|
@@ -42,3 +50,5 @@ test/test_helper.rb
|
|
42
50
|
test/test_request.rb
|
43
51
|
test/test_response.rb
|
44
52
|
test/test_router.rb
|
53
|
+
test/test_rw_lock.rb
|
54
|
+
test/test_test.rb
|
data/README.rdoc
CHANGED
@@ -7,8 +7,15 @@
|
|
7
7
|
Gin is a small Ruby web framework, built on Rack, which borrows from
|
8
8
|
Sinatra expressiveness, and targets larger applications.
|
9
9
|
|
10
|
+
== Getting Started
|
11
|
+
|
12
|
+
Gin provides a simple command for setting up a new application.
|
13
|
+
$ gin path/to/appname
|
14
|
+
|
10
15
|
== Hello World
|
11
16
|
|
17
|
+
A Hello World application requires at minimum a Gin::App instance and a Gin::Controller.
|
18
|
+
|
12
19
|
# config.ru
|
13
20
|
require 'gin'
|
14
21
|
|
@@ -22,10 +29,36 @@ Sinatra expressiveness, and targets larger applications.
|
|
22
29
|
|
23
30
|
run MyApp.new
|
24
31
|
|
32
|
+
== Why Gin?
|
33
|
+
|
34
|
+
Gin tries to fill a need for a framework that's simple and lightweight, yet scalable for large applications. Gin is comparable to Sinatra in performance and memory consumption, but supports multiple controllers and filters.
|
35
|
+
|
36
|
+
=== Features
|
37
|
+
|
38
|
+
* Easily mount multiple apps as Rack middleware
|
39
|
+
* Simple and fast RESTful routing
|
40
|
+
* Controller classes and action arguments
|
41
|
+
* Inheritable Before/After filter chains
|
42
|
+
* Response streaming
|
43
|
+
* Asset urls with cache-busting and CDN support
|
44
|
+
* Configs for multiple environments
|
45
|
+
* Periodic config reloading
|
46
|
+
* Inheritable error handling
|
47
|
+
* In-app middleware
|
48
|
+
* Rack-Session and Rack-Protection
|
49
|
+
* Dev-mode auto-reloading
|
50
|
+
* Templates (layouts/views) with Tilt
|
51
|
+
|
52
|
+
=== What's Not Included
|
53
|
+
|
54
|
+
* Helpers - those are called Modules in Ruby
|
55
|
+
* ORM - choose your own library
|
56
|
+
|
25
57
|
== Requirements
|
26
58
|
|
27
59
|
* rack
|
28
60
|
* rack-protection
|
61
|
+
* tilt
|
29
62
|
|
30
63
|
== Install
|
31
64
|
|
data/Rakefile
CHANGED
@@ -11,6 +11,11 @@ Hoe.spec 'gin' do
|
|
11
11
|
|
12
12
|
self.extra_deps << ['rack', '~>1.1']
|
13
13
|
self.extra_deps << ['rack-protection', '~>1.0']
|
14
|
+
self.extra_deps << ['tilt', '~>1.4']
|
15
|
+
|
16
|
+
self.extra_dev_deps << ['nokogiri', '~>1.5.9']
|
17
|
+
self.extra_dev_deps << ['plist', '~>3.1.0']
|
18
|
+
self.extra_dev_deps << ['bson', '~>1.9.0']
|
14
19
|
end
|
15
20
|
|
16
21
|
# vim: syntax=ruby
|
data/TODO.rdoc
ADDED
data/bin/gin
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$: << File.join(File.dirname(__FILE__), "../lib") if $0 == "bin/gin"
|
4
|
+
require 'gin'
|
5
|
+
|
6
|
+
class Gin::Cmd
|
7
|
+
def self.run argv=ARGV
|
8
|
+
show_help if argv.empty? || argv.delete("-h") || argv.delete("--help")
|
9
|
+
|
10
|
+
bare = !!argv.delete("--bare")
|
11
|
+
|
12
|
+
path = argv.pop
|
13
|
+
error("Missing app_name argument. Use gin -h for help.") if !path
|
14
|
+
|
15
|
+
error("Invalid options #{argv.join(", ")}.") unless argv.empty?
|
16
|
+
|
17
|
+
name = File.basename(path)
|
18
|
+
dir = File.expand_path(path)
|
19
|
+
parent_ru_path = File.expand_path(File.join(dir, "..", "*.ru"))
|
20
|
+
standalone = Dir[parent_ru_path].empty?
|
21
|
+
|
22
|
+
make_dirs(dir, bare)
|
23
|
+
|
24
|
+
app_class_name = Gin.camelize(name)
|
25
|
+
make_config_ru(app_class_name, name, dir) if standalone
|
26
|
+
make_console(name, dir) if standalone
|
27
|
+
make_app_rb(app_class_name, name, dir, standalone)
|
28
|
+
make_home_ctrl_rb(app_class_name, name, dir)
|
29
|
+
|
30
|
+
puts "You're all set! Your new app is waiting at #{dir}.\n\n"
|
31
|
+
exit 0
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
def self.error msg
|
36
|
+
$stderr.puts "#{msg}\n\n"
|
37
|
+
exit 1
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
def self.show_help
|
42
|
+
puts <<-STR
|
43
|
+
|
44
|
+
gin #{Gin::VERSION}
|
45
|
+
|
46
|
+
Create a new Gin application.
|
47
|
+
|
48
|
+
gin <path/to/app_name>
|
49
|
+
|
50
|
+
Examples:
|
51
|
+
|
52
|
+
$ gin my_website
|
53
|
+
$ gin my_api --bare
|
54
|
+
|
55
|
+
Options:
|
56
|
+
--bare Don't create view-related files/dirs
|
57
|
+
-h --help Show this screen
|
58
|
+
|
59
|
+
Gin applications created in a directory containing
|
60
|
+
a *.ru file will not generate its own config.ru.
|
61
|
+
|
62
|
+
STR
|
63
|
+
true
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def self.make_dirs dir, bare=false
|
68
|
+
error("Can't create directory: #{dir} already exists") if File.directory?(dir)
|
69
|
+
|
70
|
+
Dir.mkdir(dir)
|
71
|
+
|
72
|
+
unless bare
|
73
|
+
Dir.mkdir(File.join(dir, "views"))
|
74
|
+
Dir.mkdir(File.join(dir, "layouts"))
|
75
|
+
Dir.mkdir(File.join(dir, "public"))
|
76
|
+
Dir.mkdir(File.join(dir, "public", "js"))
|
77
|
+
Dir.mkdir(File.join(dir, "public", "css"))
|
78
|
+
Dir.mkdir(File.join(dir, "public", "img"))
|
79
|
+
end
|
80
|
+
|
81
|
+
Dir.mkdir(File.join(dir, "lib"))
|
82
|
+
Dir.mkdir(File.join(dir, "config"))
|
83
|
+
Dir.mkdir(File.join(dir, "controllers"))
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
def self.make_home_ctrl_rb app_class_name, name, dir
|
88
|
+
contents = <<-STR
|
89
|
+
class #{app_class_name}::HomeController < Gin::Controller
|
90
|
+
def index
|
91
|
+
<<-HTML
|
92
|
+
<!DOCTYPE html>
|
93
|
+
<html>
|
94
|
+
<head>
|
95
|
+
<title>Welcome to Gin</title>
|
96
|
+
<link rel="stylesheet" type="text/css" href="/gin.css"/>
|
97
|
+
</head>
|
98
|
+
<body style="">
|
99
|
+
<div class="canvas">
|
100
|
+
<img src="/gin_sm.png" class="logo"/>
|
101
|
+
<h1>Welcome to Gin</h1>
|
102
|
+
<p>Gin is a lightweight framework geared towards API and Website development.</br>
|
103
|
+
Start building your app by editing your app's root <strong>.rb</strong> file and
|
104
|
+
your <strong>HomeController</strong> at <strong>controllers/home_controller.rb</strong>.</p>
|
105
|
+
<p><center>The <a href="http://yaks.me/gin/wiki" target="_blank">WIKI</a> is available for help and documentation.
|
106
|
+
</center></p>
|
107
|
+
</div>
|
108
|
+
</body>
|
109
|
+
</html>
|
110
|
+
HTML
|
111
|
+
end
|
112
|
+
end
|
113
|
+
STR
|
114
|
+
|
115
|
+
File.write(File.join(dir, "controllers/home_controller.rb"), contents)
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
def self.make_app_rb app_class_name, name, dir, standalone
|
120
|
+
contents = <<-STR
|
121
|
+
require 'gin'
|
122
|
+
|
123
|
+
$:.unshift( File.expand_path('../lib', __FILE__) )
|
124
|
+
#{"$:.unshift( File.expand_path('../controllers', __FILE__) )\n" if standalone}
|
125
|
+
|
126
|
+
class #{app_class_name} < Gin::App
|
127
|
+
require '#{"#{name}/controllers/" if !standalone}home_controller'
|
128
|
+
mount #{app_class_name}::HomeController, "/"
|
129
|
+
end
|
130
|
+
STR
|
131
|
+
|
132
|
+
File.write(File.join(dir, "#{name}.rb"), contents)
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
def self.make_config_ru app_class_name, name, dir
|
137
|
+
contents = <<-STR
|
138
|
+
$:.unshift File.expand_path("..", __FILE__)
|
139
|
+
require '#{name}'
|
140
|
+
run #{app_class_name}.new
|
141
|
+
STR
|
142
|
+
|
143
|
+
filepath = File.join(dir, 'config.ru')
|
144
|
+
File.write(filepath, contents)
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
def self.make_console name, dir
|
149
|
+
filepath = File.join(dir, 'console')
|
150
|
+
bash = "irb -I \"$( dirname \"${BASH_SOURCE[0]}\" )\" -r #{name}\n"
|
151
|
+
File.write(filepath, bash)
|
152
|
+
require 'fileutils'
|
153
|
+
FileUtils.chmod "u=wrx,go=rx", filepath
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
|
158
|
+
Gin::Cmd.run
|
data/lib/gin.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
require 'rack'
|
2
|
+
require 'tilt'
|
2
3
|
|
3
4
|
class Gin
|
4
|
-
VERSION = '1.0
|
5
|
+
VERSION = '1.1.0'
|
5
6
|
|
6
7
|
LIB_DIR = File.expand_path("..", __FILE__) #:nodoc:
|
7
8
|
PUBLIC_DIR = File.expand_path("../../public/", __FILE__) #:nodoc:
|
@@ -16,6 +17,10 @@ class Gin
|
|
16
17
|
def http_status; 404; end
|
17
18
|
end
|
18
19
|
|
20
|
+
class MissingConfig < Error; end
|
21
|
+
|
22
|
+
class TemplateMissing < Error; end
|
23
|
+
|
19
24
|
|
20
25
|
##
|
21
26
|
# Change string to underscored version.
|
@@ -29,6 +34,16 @@ class Gin
|
|
29
34
|
end
|
30
35
|
|
31
36
|
|
37
|
+
##
|
38
|
+
# Change string to camel-case version.
|
39
|
+
|
40
|
+
def self.camelize str
|
41
|
+
str = str.dup
|
42
|
+
str.gsub!(/\/([^_:])/){|m| "::#{$1.upcase}" }
|
43
|
+
str.gsub!(/(^|_+)([^_:])/){|m| $2.upcase }
|
44
|
+
end
|
45
|
+
|
46
|
+
|
32
47
|
##
|
33
48
|
# Create a URI query from a Hash.
|
34
49
|
|
@@ -110,13 +125,15 @@ class Gin
|
|
110
125
|
require 'gin/core_ext/rack_commonlogger'
|
111
126
|
|
112
127
|
require 'gin/constants'
|
113
|
-
require 'gin/
|
128
|
+
require 'gin/cache'
|
114
129
|
require 'gin/router'
|
115
130
|
require 'gin/config'
|
116
131
|
require 'gin/request'
|
117
132
|
require 'gin/response'
|
133
|
+
require 'gin/rw_lock'
|
118
134
|
require 'gin/stream'
|
119
135
|
require 'gin/errorable'
|
120
136
|
require 'gin/filterable'
|
121
137
|
require 'gin/controller'
|
138
|
+
require 'gin/app'
|
122
139
|
end
|
data/lib/gin/app.rb
CHANGED
@@ -18,10 +18,10 @@ class Gin::App
|
|
18
18
|
class RouterError < Gin::Error; end
|
19
19
|
|
20
20
|
CALLERS_TO_IGNORE = [ # :nodoc:
|
21
|
-
|
22
|
-
/lib\/tilt.*\.rb
|
21
|
+
/lib\/gin(\/(.*?))?\.rb/, # all gin code
|
22
|
+
/lib\/tilt.*\.rb/, # all tilt code
|
23
23
|
/^\(.*\)$/, # generated code
|
24
|
-
/rubygems\/custom_require\.rb
|
24
|
+
/rubygems\/custom_require\.rb/, # rubygems require hacks
|
25
25
|
/active_support/, # active_support require hacks
|
26
26
|
/bundler(\/runtime)?\.rb/, # bundler require hacks
|
27
27
|
/<internal:/, # internal in ruby >= 1.9.2
|
@@ -30,17 +30,41 @@ class Gin::App
|
|
30
30
|
|
31
31
|
|
32
32
|
def self.inherited subclass #:nodoc:
|
33
|
+
subclass.setup
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
def self.setup # :nodoc:
|
33
38
|
caller_line = caller.find{|line| !CALLERS_TO_IGNORE.any?{|m| line =~ m} }
|
34
39
|
filepath = File.expand_path(caller_line.split(/:\d+:in `/).first)
|
35
|
-
dir
|
36
|
-
|
37
|
-
|
38
|
-
|
40
|
+
dir = File.dirname(filepath)
|
41
|
+
|
42
|
+
@source_file = filepath
|
43
|
+
@source_class = self.to_s
|
44
|
+
@templates = Gin::Cache.new
|
45
|
+
@md5s = Gin::Cache.new
|
46
|
+
@reload_mutex = Mutex.new
|
47
|
+
@autoreload = nil
|
48
|
+
@instance = nil
|
49
|
+
|
50
|
+
@options = {}
|
51
|
+
@options[:root_dir] = dir
|
52
|
+
@options[:environment] = ENV['RACK_ENV'] || ENV_DEV
|
53
|
+
@options[:error_delegate] = Gin::Controller
|
54
|
+
@options[:middleware] = []
|
55
|
+
@options[:logger] = $stdout
|
56
|
+
@options[:router] = Gin::Router.new
|
57
|
+
@options[:session_secret] = SESSION_SECRET
|
58
|
+
@options[:protection] = false
|
59
|
+
@options[:sessions] = false
|
60
|
+
@options[:config_reload] = false
|
61
|
+
@options[:layout] = :layout
|
62
|
+
@options[:template_engines] = Tilt.mappings.merge(nil => [Tilt::ERBTemplate])
|
39
63
|
end
|
40
64
|
|
41
65
|
|
42
66
|
##
|
43
|
-
# Create a new
|
67
|
+
# Create a new instance of the app and call it.
|
44
68
|
|
45
69
|
def self.call env
|
46
70
|
@instance ||= self.new
|
@@ -48,6 +72,35 @@ class Gin::App
|
|
48
72
|
end
|
49
73
|
|
50
74
|
|
75
|
+
##
|
76
|
+
# Hash of the full Gin::App configuration.
|
77
|
+
# Result of using class-level setter methods such as Gin::App.environment.
|
78
|
+
|
79
|
+
def self.options
|
80
|
+
@options
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
##
|
85
|
+
# Returns the source file of the current app.
|
86
|
+
|
87
|
+
def self.source_file
|
88
|
+
@source_file
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
def self.namespace #:nodoc:
|
93
|
+
# Parent namespace of the App class. Used for reloading purposes.
|
94
|
+
Gin.const_find(@source_class.split("::")[0..-2]) if @source_class
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
def self.source_class #:nodoc:
|
99
|
+
# Lookup the class from its name. Used for reloading purposes.
|
100
|
+
Gin.const_find(@source_class) if @source_class
|
101
|
+
end
|
102
|
+
|
103
|
+
|
51
104
|
##
|
52
105
|
# Enable or disable auto-app reloading.
|
53
106
|
# On by default in development mode.
|
@@ -55,21 +108,36 @@ class Gin::App
|
|
55
108
|
# In order for an app to be reloadable, the libs and controllers must be
|
56
109
|
# required from the Gin::App class context, or use MyApp.require("lib").
|
57
110
|
#
|
58
|
-
#
|
111
|
+
# Gin::App class reloading is not supported for applications defined in
|
112
|
+
# the config.ru file. Dependencies will, however, still be reloaded.
|
113
|
+
#
|
114
|
+
# Autoreload must be enabled before any calls to Gin::App.require for
|
115
|
+
# those files to be reloaded.
|
116
|
+
#
|
117
|
+
# class MyApp < Gin::App
|
118
|
+
#
|
119
|
+
# # Only reloaded in development mode
|
120
|
+
# require 'nokogiri'
|
121
|
+
#
|
122
|
+
# autoreload false
|
123
|
+
# # Never reloaded
|
124
|
+
# require 'static_thing'
|
125
|
+
#
|
126
|
+
# autoreload true
|
127
|
+
# # Reloaded every request
|
128
|
+
# require 'my_app/home_controller'
|
129
|
+
# end
|
59
130
|
|
60
131
|
def self.autoreload val=nil
|
61
132
|
@autoreload = val unless val.nil?
|
133
|
+
reload = @autoreload.nil? ? self.environment == ENV_DEV : @autoreload
|
62
134
|
|
63
|
-
if
|
64
|
-
|
135
|
+
if reload
|
136
|
+
Object.send :require, 'gin/reloadable' unless defined?(Gin::Reloadable)
|
137
|
+
include Gin::Reloadable unless self < Gin::Reloadable
|
65
138
|
end
|
66
139
|
|
67
|
-
|
68
|
-
Object.send :require, 'gin/reloadable'
|
69
|
-
include Gin::Reloadable
|
70
|
-
end
|
71
|
-
|
72
|
-
@autoreload
|
140
|
+
reload
|
73
141
|
end
|
74
142
|
|
75
143
|
|
@@ -121,38 +189,47 @@ class Gin::App
|
|
121
189
|
|
122
190
|
|
123
191
|
##
|
124
|
-
#
|
125
|
-
|
126
|
-
def self.source_file
|
127
|
-
@source_file
|
128
|
-
end
|
129
|
-
|
192
|
+
# Get or set the CDN asset host (and path).
|
193
|
+
# If block is given, evaluates the block on every read.
|
130
194
|
|
131
|
-
def self.
|
132
|
-
|
133
|
-
|
195
|
+
def self.asset_host host=nil, &block
|
196
|
+
@options[:asset_host] = host if host
|
197
|
+
@options[:asset_host] = block if block_given?
|
198
|
+
@options[:asset_host]
|
134
199
|
end
|
135
200
|
|
136
201
|
|
137
|
-
def self.
|
138
|
-
|
139
|
-
|
202
|
+
def self.make_config opts={} # :nodoc:
|
203
|
+
Gin::Config.new opts[:environment] || self.environment,
|
204
|
+
dir: opts[:config_dir] || self.config_dir,
|
205
|
+
logger: opts[:logger] || self.logger,
|
206
|
+
ttl: opts[:config_reload] || self.config_reload
|
140
207
|
end
|
141
208
|
|
142
209
|
|
143
210
|
##
|
144
|
-
#
|
145
|
-
#
|
211
|
+
# Access the config for your application, loaded from the config_dir.
|
212
|
+
# # config/memcache.yml
|
213
|
+
# default: &default
|
214
|
+
# host: example.com
|
215
|
+
# connections: 1
|
216
|
+
# development: *default
|
217
|
+
# host: localhost
|
218
|
+
#
|
219
|
+
# # access from App class
|
220
|
+
# CACHE = Memcache.new( MyApp.config['memcache.host'] )
|
221
|
+
#
|
222
|
+
# The config object is shared across all instances of the App and has
|
223
|
+
# thread-safety built-in.
|
146
224
|
|
147
|
-
def self.
|
148
|
-
@
|
149
|
-
@root_dir
|
225
|
+
def self.config
|
226
|
+
@options[:config] ||= make_config
|
150
227
|
end
|
151
228
|
|
152
229
|
|
153
230
|
##
|
154
231
|
# Get or set the path to the config directory.
|
155
|
-
# Defaults to root_dir
|
232
|
+
# Defaults to "<root_dir>/config"
|
156
233
|
#
|
157
234
|
# Configs are expected to be YAML files following this pattern:
|
158
235
|
# default: &default
|
@@ -168,108 +245,129 @@ class Gin::App
|
|
168
245
|
# the current environment will be accessible.
|
169
246
|
|
170
247
|
def self.config_dir dir=nil
|
171
|
-
|
172
|
-
|
248
|
+
if String === dir
|
249
|
+
@options[:config_dir] = dir
|
250
|
+
@options[:config].dir = dir if @options[:config]
|
251
|
+
end
|
252
|
+
|
253
|
+
@options[:config_dir] || File.join(self.root_dir, "config")
|
173
254
|
end
|
174
255
|
|
175
256
|
|
176
257
|
##
|
177
|
-
#
|
178
|
-
#
|
179
|
-
#
|
180
|
-
#
|
181
|
-
#
|
182
|
-
#
|
183
|
-
# host: localhost
|
258
|
+
# Get or set the config max age for auto-reloading in seconds.
|
259
|
+
# Turns config reloading off if set to false. Defaults to false.
|
260
|
+
# Config only gets reloaded on demand.
|
261
|
+
#
|
262
|
+
# # Set to 10 minutes
|
263
|
+
# config_reload 600
|
184
264
|
#
|
185
|
-
# #
|
186
|
-
#
|
265
|
+
# # Set to never reload
|
266
|
+
# config_reload false
|
187
267
|
|
188
|
-
def self.
|
189
|
-
|
268
|
+
def self.config_reload ttl=nil
|
269
|
+
unless ttl.nil?
|
270
|
+
@options[:config_reload] = ttl
|
271
|
+
@options[:config].ttl = ttl if @options[:config]
|
272
|
+
end
|
273
|
+
@options[:config_reload]
|
190
274
|
end
|
191
275
|
|
192
276
|
|
193
277
|
##
|
194
|
-
#
|
278
|
+
# Set the default templating engine to use for various
|
279
|
+
# file extensions, or by default:
|
280
|
+
# # Default for .markdown and .md files
|
281
|
+
# default_template Tilt::MarukuTemplate, 'markdown', 'md'
|
282
|
+
#
|
283
|
+
# # Default for files without preset default
|
284
|
+
# default_template Tilt::BlueClothTemplate
|
195
285
|
|
196
|
-
def self.
|
197
|
-
|
198
|
-
|
199
|
-
|
286
|
+
def self.default_template klass, *extensions
|
287
|
+
extensions = [nil] if extensions.empty?
|
288
|
+
extensions.each{|ext|
|
289
|
+
(@options[:template_engines][ext] ||= []).unshift klass }
|
200
290
|
end
|
201
291
|
|
202
292
|
|
203
293
|
##
|
204
|
-
# Get or set the
|
205
|
-
#
|
294
|
+
# Get or set the current environment name,
|
295
|
+
# by default ENV ['RACK_ENV'], or "development".
|
206
296
|
|
207
|
-
def self.
|
208
|
-
|
209
|
-
|
297
|
+
def self.environment env=nil
|
298
|
+
if env
|
299
|
+
@options[:environment] = env
|
300
|
+
@options[:config].environment = env if @options[:config]
|
301
|
+
end
|
302
|
+
@options[:environment]
|
210
303
|
end
|
211
304
|
|
212
305
|
|
213
306
|
##
|
214
|
-
#
|
215
|
-
#
|
307
|
+
# Define a Gin::Controller as a catch-all error rendering controller.
|
308
|
+
# This can be a dedicated controller, or a parent controller
|
309
|
+
# such as AppController. Defaults to Gin::Controller.
|
310
|
+
#
|
311
|
+
# The error delegate should handle the following errors
|
312
|
+
# for creating custom pages for Gin errors:
|
313
|
+
# Gin::NotFound, Gin::BadRequest, ::Exception
|
216
314
|
|
217
|
-
def self.
|
218
|
-
@
|
219
|
-
@
|
220
|
-
host = @asset_host.respond_to?(:call) ? @asset_host.call : @asset_host
|
315
|
+
def self.error_delegate ctrl=nil
|
316
|
+
@options[:error_delegate] = ctrl if ctrl
|
317
|
+
@options[:error_delegate]
|
221
318
|
end
|
222
319
|
|
223
320
|
|
224
321
|
##
|
225
|
-
#
|
226
|
-
#
|
322
|
+
# Get or set the layout name. Layout file location is assumed to be in
|
323
|
+
# the views_dir. If the views dir has a controller wildcard '*', the layout
|
324
|
+
# is assumed to be one level above the controller-specific directory.
|
325
|
+
#
|
326
|
+
# Defaults to :layout.
|
227
327
|
|
228
|
-
def self.
|
229
|
-
|
328
|
+
def self.layout name=nil
|
329
|
+
@options[:layout] = name if name
|
330
|
+
@options[:layout]
|
230
331
|
end
|
231
332
|
|
232
333
|
|
233
334
|
##
|
234
|
-
#
|
235
|
-
#
|
236
|
-
|
237
|
-
def self.asset_version path
|
238
|
-
path = File.expand_path(File.join(public_dir, path))
|
239
|
-
return unless File.file?(path)
|
335
|
+
# Get or set the directory for view layouts.
|
336
|
+
# Defaults to the "<root_dir>/layouts".
|
240
337
|
|
241
|
-
|
242
|
-
@
|
338
|
+
def self.layouts_dir dir=nil
|
339
|
+
@options[:layouts_dir] = dir if dir
|
340
|
+
@options[:layouts_dir] || File.join(root_dir, 'layouts')
|
243
341
|
end
|
244
342
|
|
245
343
|
|
246
|
-
|
344
|
+
##
|
345
|
+
# Get or set the logger for your application. Loggers must respond
|
346
|
+
# to the << method.
|
247
347
|
|
248
|
-
def self.
|
249
|
-
|
348
|
+
def self.logger new_logger=nil
|
349
|
+
if new_logger
|
350
|
+
@options[:logger] = new_logger
|
351
|
+
@options[:config].logger = new_logger if @options[:config]
|
352
|
+
end
|
353
|
+
@options[:logger]
|
250
354
|
end
|
251
355
|
|
252
356
|
|
253
357
|
##
|
254
|
-
#
|
255
|
-
#
|
256
|
-
# such as AppController. Defaults to Gin::Controller.
|
257
|
-
#
|
258
|
-
# The error delegate should handle the following errors
|
259
|
-
# for creating custom pages for Gin errors:
|
260
|
-
# Gin::NotFound, Gin::BadRequest, ::Exception
|
358
|
+
# Cache of file md5s shared across all instances of an App class.
|
359
|
+
# Used for static asset versioning.
|
261
360
|
|
262
|
-
def self.
|
263
|
-
@
|
264
|
-
@error_delegate ||= Gin::Controller
|
361
|
+
def self.md5s
|
362
|
+
@md5s
|
265
363
|
end
|
266
364
|
|
267
365
|
|
268
366
|
##
|
269
|
-
#
|
367
|
+
# List of internal app middleware.
|
270
368
|
|
271
|
-
def self.
|
272
|
-
@
|
369
|
+
def self.middleware
|
370
|
+
@options[:middleware]
|
273
371
|
end
|
274
372
|
|
275
373
|
|
@@ -296,22 +394,45 @@ class Gin::App
|
|
296
394
|
|
297
395
|
|
298
396
|
##
|
299
|
-
#
|
300
|
-
#
|
301
|
-
# handled by the error_delegate.
|
397
|
+
# Use rack-protection or not. Supports assigning
|
398
|
+
# hash for options. Defaults to false.
|
302
399
|
|
303
|
-
def self.
|
304
|
-
|
305
|
-
|
306
|
-
self.middleware << ary
|
400
|
+
def self.protection opts=nil
|
401
|
+
@options[:protection] = opts unless opts.nil?
|
402
|
+
@options[:protection]
|
307
403
|
end
|
308
404
|
|
309
405
|
|
310
406
|
##
|
311
|
-
#
|
407
|
+
# Get or set the path to the public directory.
|
408
|
+
# Defaults to "<root_dir>/public"
|
312
409
|
|
313
|
-
def self.
|
314
|
-
@
|
410
|
+
def self.public_dir dir=nil
|
411
|
+
@options[:public_dir] = dir if dir
|
412
|
+
@options[:public_dir] || File.join(root_dir, "public")
|
413
|
+
end
|
414
|
+
|
415
|
+
|
416
|
+
def self.reload_mutex # :nodoc:
|
417
|
+
@reload_mutex
|
418
|
+
end
|
419
|
+
|
420
|
+
|
421
|
+
##
|
422
|
+
# Get or set the root directory of the application.
|
423
|
+
# Defaults to the app file's directory.
|
424
|
+
|
425
|
+
def self.root_dir dir=nil
|
426
|
+
@options[:root_dir] = File.expand_path(dir) if dir
|
427
|
+
@options[:root_dir]
|
428
|
+
end
|
429
|
+
|
430
|
+
|
431
|
+
##
|
432
|
+
# Router instance that handles mapping Rack-env -> Controller#action.
|
433
|
+
|
434
|
+
def self.router
|
435
|
+
@options[:router]
|
315
436
|
end
|
316
437
|
|
317
438
|
|
@@ -320,9 +441,8 @@ class Gin::App
|
|
320
441
|
# hash for options. Defaults to false.
|
321
442
|
|
322
443
|
def self.sessions opts=nil
|
323
|
-
@
|
324
|
-
@
|
325
|
-
@session
|
444
|
+
@options[:sessions] = opts unless opts.nil?
|
445
|
+
@options[:sessions]
|
326
446
|
end
|
327
447
|
|
328
448
|
|
@@ -331,36 +451,117 @@ class Gin::App
|
|
331
451
|
# Defaults to a new random value on boot.
|
332
452
|
|
333
453
|
def self.session_secret val=nil
|
334
|
-
@session_secret = val if val
|
335
|
-
@session_secret
|
454
|
+
@options[:session_secret] = val if val
|
455
|
+
@options[:session_secret]
|
336
456
|
end
|
337
457
|
|
338
458
|
|
339
459
|
##
|
340
|
-
#
|
341
|
-
#
|
460
|
+
# Cache of precompiled templates, shared across all instances of a
|
461
|
+
# given App class.
|
342
462
|
|
343
|
-
def self.
|
344
|
-
@
|
345
|
-
@protection = false if @protection.nil?
|
346
|
-
@protection
|
463
|
+
def self.templates
|
464
|
+
@templates
|
347
465
|
end
|
348
466
|
|
349
467
|
|
350
468
|
##
|
351
|
-
#
|
352
|
-
#
|
469
|
+
# Add middleware internally to the app.
|
470
|
+
# Middleware statuses and Exceptions will NOT be
|
471
|
+
# handled by the error_delegate.
|
353
472
|
|
354
|
-
def self.
|
355
|
-
|
356
|
-
|
473
|
+
def self.use middleware, *args, &block
|
474
|
+
ary = [middleware, *args]
|
475
|
+
ary << block if block_given?
|
476
|
+
self.middleware << ary
|
477
|
+
end
|
478
|
+
|
479
|
+
|
480
|
+
##
|
481
|
+
# Get or set the path to the views directory.
|
482
|
+
# The wildcard '*' will be replaced by the controller name.
|
483
|
+
#
|
484
|
+
# Defaults to "<root_dir>/views"
|
485
|
+
|
486
|
+
def self.views_dir dir=nil
|
487
|
+
@options[:views_dir] = dir if dir
|
488
|
+
@options[:views_dir] || File.join(root_dir, 'views')
|
489
|
+
end
|
490
|
+
|
491
|
+
|
492
|
+
opt_reader :protection, :sessions, :session_secret, :middleware
|
493
|
+
opt_reader :error_delegate, :router, :logger
|
494
|
+
opt_reader :layout, :layouts_dir, :views_dir, :template_engines
|
495
|
+
opt_reader :root_dir, :public_dir, :environment
|
496
|
+
|
497
|
+
class_proxy :mime_type, :md5s, :templates, :reload_mutex, :autoreload
|
498
|
+
|
499
|
+
# App to fallback on if Gin::App is used as middleware and no route is found.
|
500
|
+
attr_reader :rack_app
|
501
|
+
|
502
|
+
# Options applied to the Gin::App instance. Typically a result of
|
503
|
+
# class-level configuration methods, such as Gin::App.environment.
|
504
|
+
attr_reader :options
|
505
|
+
|
506
|
+
# Internal Rack stack.
|
507
|
+
attr_reader :stack
|
508
|
+
|
509
|
+
##
|
510
|
+
# Create a new Rack-mountable Gin::App instance, with an optional
|
511
|
+
# rack_app and options.
|
512
|
+
|
513
|
+
def initialize rack_app=nil, options={}
|
514
|
+
if Hash === rack_app
|
515
|
+
options = rack_app
|
516
|
+
@rack_app = nil
|
517
|
+
else
|
518
|
+
@rack_app = rack_app
|
519
|
+
end
|
520
|
+
|
521
|
+
@options = {
|
522
|
+
config_dir: self.class.config_dir,
|
523
|
+
public_dir: self.class.public_dir,
|
524
|
+
layouts_dir: self.class.layouts_dir,
|
525
|
+
views_dir: self.class.views_dir,
|
526
|
+
config: self.class.config
|
527
|
+
}.merge(self.class.options).merge(options)
|
528
|
+
|
529
|
+
@options[:config] = self.class.make_config(@options) if
|
530
|
+
@options[:environment] != @options[:config].environment ||
|
531
|
+
@options[:config_dir] != @options[:config].dir ||
|
532
|
+
@options[:config_reload] != @options[:config].ttl
|
533
|
+
|
534
|
+
validate_all_controllers!
|
535
|
+
|
536
|
+
@app = self
|
537
|
+
@stack = build_app Rack::Builder.new
|
538
|
+
end
|
539
|
+
|
540
|
+
|
541
|
+
##
|
542
|
+
# Access the config for your application, loaded from the config_dir.
|
543
|
+
# # config/memcache.yml
|
544
|
+
# default: &default
|
545
|
+
# host: example.com
|
546
|
+
# connections: 1
|
547
|
+
# development: *default
|
548
|
+
# host: localhost
|
549
|
+
#
|
550
|
+
# # access from App instance
|
551
|
+
# @app.config['memcache.host']
|
552
|
+
#
|
553
|
+
# The config object is shared across all instances of the App and has
|
554
|
+
# thread-safety built-in.
|
555
|
+
|
556
|
+
def config
|
557
|
+
@options[:config]
|
357
558
|
end
|
358
559
|
|
359
560
|
|
360
561
|
##
|
361
562
|
# Check if running in development mode.
|
362
563
|
|
363
|
-
def
|
564
|
+
def development?
|
364
565
|
self.environment == ENV_DEV
|
365
566
|
end
|
366
567
|
|
@@ -368,7 +569,7 @@ class Gin::App
|
|
368
569
|
##
|
369
570
|
# Check if running in test mode.
|
370
571
|
|
371
|
-
def
|
572
|
+
def test?
|
372
573
|
self.environment == ENV_TEST
|
373
574
|
end
|
374
575
|
|
@@ -376,7 +577,7 @@ class Gin::App
|
|
376
577
|
##
|
377
578
|
# Check if running in staging mode.
|
378
579
|
|
379
|
-
def
|
580
|
+
def staging?
|
380
581
|
self.environment == ENV_STAGE
|
381
582
|
end
|
382
583
|
|
@@ -384,47 +585,84 @@ class Gin::App
|
|
384
585
|
##
|
385
586
|
# Check if running in production mode.
|
386
587
|
|
387
|
-
def
|
588
|
+
def production?
|
388
589
|
self.environment == ENV_PROD
|
389
590
|
end
|
390
591
|
|
391
592
|
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
593
|
+
##
|
594
|
+
# Returns the asset host for a given asset name. This is useful when assigning
|
595
|
+
# a block for the asset_host. The asset_name argument is passed to the block.
|
596
|
+
|
597
|
+
def asset_host_for asset_name
|
598
|
+
@options[:asset_host].respond_to?(:call) ?
|
599
|
+
@options[:asset_host].call(asset_name) : @options[:asset_host]
|
600
|
+
end
|
398
601
|
|
399
|
-
# Application logger. Defaults to log to $stdout.
|
400
|
-
attr_accessor :logger
|
401
602
|
|
402
|
-
|
403
|
-
|
603
|
+
##
|
604
|
+
# Returns the default asset host.
|
404
605
|
|
405
|
-
|
406
|
-
|
606
|
+
def asset_host
|
607
|
+
asset_host_for(nil)
|
608
|
+
end
|
407
609
|
|
408
610
|
|
409
611
|
##
|
410
|
-
#
|
411
|
-
#
|
612
|
+
# Returns the first 8 bytes of the asset file's md5.
|
613
|
+
# File path is assumed relative to the public_dir.
|
614
|
+
|
615
|
+
def asset_version path
|
616
|
+
path = File.expand_path(File.join(public_dir, path))
|
617
|
+
md5(path)
|
618
|
+
end
|
412
619
|
|
413
|
-
def initialize rack_app=nil, logger=nil
|
414
|
-
load_config
|
415
620
|
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
621
|
+
MD5 = RUBY_PLATFORM =~ /darwin/ ? 'md5 -q' : 'md5sum' #:nodoc:
|
622
|
+
|
623
|
+
##
|
624
|
+
# Returns the first 8 characters of a file's MD5 hash.
|
625
|
+
# Values are cached for future reference.
|
626
|
+
|
627
|
+
def md5 path
|
628
|
+
return unless File.file?(path)
|
629
|
+
self.md5s[path] ||= `#{MD5} #{path}`[0...8]
|
630
|
+
end
|
631
|
+
|
632
|
+
|
633
|
+
##
|
634
|
+
# Returns the tilt template for the given template name.
|
635
|
+
# Returns nil if no template file is found.
|
636
|
+
# template_for 'user/show'
|
637
|
+
# #=> <Tilt::ERBTemplate @file="views/user/show.erb" ...>
|
638
|
+
#
|
639
|
+
# template_for 'user/show.haml'
|
640
|
+
# #=> <Tilt::HamlTemplate @file="views/user/show.haml" ...>
|
641
|
+
#
|
642
|
+
# template_for 'non-existant'
|
643
|
+
# #=> nil
|
644
|
+
|
645
|
+
def template_for path, engine=nil
|
646
|
+
templates.cache([path, engine]) do
|
647
|
+
if file = template_files(path).first
|
648
|
+
ext = File.extname(file)
|
649
|
+
ext = ext.empty? ? nil : ext[1..-1]
|
650
|
+
engine ||= template_engines[ext].first
|
651
|
+
engine.new(file) if engine
|
652
|
+
end
|
422
653
|
end
|
654
|
+
end
|
423
655
|
|
424
|
-
validate_all_controllers!
|
425
656
|
|
426
|
-
|
427
|
-
|
657
|
+
##
|
658
|
+
# Returns an Array of file paths that match a valid path and maps
|
659
|
+
# to a known template engine.
|
660
|
+
# app.template_files 'views/foo'
|
661
|
+
# #=> ['views/foo.erb', 'views/foo.md']
|
662
|
+
|
663
|
+
def template_files path
|
664
|
+
exts = template_engines.keys.map{|e| "." << e if e }.join(",")
|
665
|
+
Dir["#{path}{#{exts}}"]
|
428
666
|
end
|
429
667
|
|
430
668
|
|
@@ -435,16 +673,17 @@ class Gin::App
|
|
435
673
|
# If you use this in production, you're gonna have a bad time.
|
436
674
|
|
437
675
|
def reload!
|
438
|
-
|
676
|
+
reload_mutex.synchronize do
|
677
|
+
self.class.erase_dependencies!
|
439
678
|
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
679
|
+
if File.extname(self.class.source_file) != ".ru"
|
680
|
+
self.class.erase! [self.class.source_file],
|
681
|
+
[self.class.name.split("::").last],
|
682
|
+
self.class.namespace
|
683
|
+
require self.class.source_file
|
684
|
+
end
|
444
685
|
|
445
|
-
self.class.
|
446
|
-
require self.class.source_file
|
447
|
-
@app = self.class.source_class.new @rack_app, @logger
|
686
|
+
@app = self.class.source_class.new @rack_app, @options
|
448
687
|
end
|
449
688
|
end
|
450
689
|
|
@@ -497,8 +736,8 @@ class Gin::App
|
|
497
736
|
|
498
737
|
|
499
738
|
##
|
500
|
-
# Returns a static file Rack response Array from the
|
501
|
-
# env
|
739
|
+
# Returns a static file Rack response Array from the filename set
|
740
|
+
# in env['gin.static'].
|
502
741
|
|
503
742
|
def call_static env
|
504
743
|
with_log_request(env) do
|
@@ -509,7 +748,8 @@ class Gin::App
|
|
509
748
|
|
510
749
|
##
|
511
750
|
# Check if the request is for a static file and set the gin.static env
|
512
|
-
# variable to the filepath.
|
751
|
+
# variable to the filepath. Returns true if request is to a static asset,
|
752
|
+
# otherwise false.
|
513
753
|
|
514
754
|
def static! env
|
515
755
|
filepath = %w{GET HEAD}.include?(env[REQ_METHOD]) &&
|
@@ -526,6 +766,7 @@ class Gin::App
|
|
526
766
|
# Check if the request routes to a controller and action and set
|
527
767
|
# gin.controller, gin.action, gin.path_query_hash,
|
528
768
|
# and gin.http_route env variables.
|
769
|
+
# Returns true if a route is found, otherwise false.
|
529
770
|
|
530
771
|
def route! env
|
531
772
|
http_route = "#{env[REQ_METHOD]} #{env[PATH_INFO]}"
|
@@ -566,7 +807,8 @@ class Gin::App
|
|
566
807
|
"No route exists for: #{env[REQ_METHOD]} #{env[PATH_INFO]}" unless
|
567
808
|
ctrl && action
|
568
809
|
|
569
|
-
ctrl.new(self, env)
|
810
|
+
env[GIN_CTRL] = ctrl.new(self, env)
|
811
|
+
env[GIN_CTRL].call_action action
|
570
812
|
|
571
813
|
rescue ::Exception => err
|
572
814
|
handle_error(err, env)
|
@@ -599,10 +841,12 @@ class Gin::App
|
|
599
841
|
now = Time.now
|
600
842
|
time = now - env[GIN_TIMESTAMP] if env[GIN_TIMESTAMP]
|
601
843
|
|
602
|
-
ctrl, action = env[GIN_CTRL], env[GIN_ACTION]
|
844
|
+
ctrl, action = env[GIN_CTRL].class, env[GIN_ACTION]
|
603
845
|
target = "#{ctrl}##{action}" if ctrl && action
|
846
|
+
target = resp[2].path if !target && resp[2].respond_to?(:path)
|
847
|
+
target = "<stream>" if !target && (!(Array === resp[2]) || !resp[2].empty?)
|
604
848
|
|
605
|
-
|
849
|
+
logger << ( LOG_FORMAT % [
|
606
850
|
env[FWD_FOR] || env[REMOTE_ADDR] || "-",
|
607
851
|
env[REMOTE_USER] || "-",
|
608
852
|
now.strftime(TIME_FORMAT),
|
@@ -640,10 +884,10 @@ class Gin::App
|
|
640
884
|
|
641
885
|
def setup_sessions builder
|
642
886
|
return unless sessions
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
builder.use Rack::Session::Cookie,
|
887
|
+
opts = {}
|
888
|
+
opts[:secret] = session_secret if session_secret
|
889
|
+
opts.merge! sessions.to_hash if sessions.respond_to? :to_hash
|
890
|
+
builder.use Rack::Session::Cookie, opts
|
647
891
|
end
|
648
892
|
|
649
893
|
|
@@ -651,11 +895,11 @@ class Gin::App
|
|
651
895
|
return unless protection
|
652
896
|
require 'rack-protection' unless defined?(Rack::Protection)
|
653
897
|
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
builder.use Rack::Protection,
|
898
|
+
opts = Hash === protection ? protection.dup : {}
|
899
|
+
opts[:except] = Array opts[:except]
|
900
|
+
opts[:except] += [:session_hijacking, :remote_token] unless sessions
|
901
|
+
opts[:reaction] ||= :drop_session
|
902
|
+
builder.use Rack::Protection, opts
|
659
903
|
end
|
660
904
|
|
661
905
|
|
@@ -663,13 +907,13 @@ class Gin::App
|
|
663
907
|
# Make sure all controller actions have a route, or raise a RouterError.
|
664
908
|
|
665
909
|
def validate_all_controllers!
|
666
|
-
|
910
|
+
actions_map = {}
|
667
911
|
|
668
912
|
router.each_route do |route, ctrl, action|
|
669
|
-
(
|
913
|
+
(actions_map[ctrl] ||= []) << action
|
670
914
|
end
|
671
915
|
|
672
|
-
|
916
|
+
actions_map.each do |ctrl, actions|
|
673
917
|
not_mounted = ctrl.actions - actions
|
674
918
|
raise RouterError, "#{ctrl}##{not_mounted[0]} has no route." unless
|
675
919
|
not_mounted.empty?
|
@@ -679,4 +923,7 @@ class Gin::App
|
|
679
923
|
extra_mounted.empty?
|
680
924
|
end
|
681
925
|
end
|
926
|
+
|
927
|
+
|
928
|
+
setup
|
682
929
|
end
|