padrino-core 0.10.7 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/padrino-core.rb +58 -4
- data/lib/padrino-core/application.rb +38 -16
- data/lib/padrino-core/application/flash.rb +229 -0
- data/lib/padrino-core/application/rendering.rb +39 -11
- data/lib/padrino-core/application/rendering/extensions/erubis.rb +55 -0
- data/lib/padrino-core/application/rendering/extensions/haml.rb +26 -0
- data/lib/padrino-core/application/rendering/extensions/slim.rb +14 -0
- data/lib/padrino-core/application/routing.rb +106 -35
- data/lib/padrino-core/cli/base.rb +41 -38
- data/lib/padrino-core/cli/rake.rb +30 -9
- data/lib/padrino-core/cli/rake_tasks.rb +9 -14
- data/lib/padrino-core/loader.rb +23 -9
- data/lib/padrino-core/locale/fr.yml +1 -1
- data/lib/padrino-core/locale/ru.yml +1 -1
- data/lib/padrino-core/logger.rb +48 -32
- data/lib/padrino-core/module.rb +58 -0
- data/lib/padrino-core/mounter.rb +15 -5
- data/lib/padrino-core/reloader.rb +14 -12
- data/lib/padrino-core/server.rb +4 -4
- data/lib/padrino-core/support_lite.rb +43 -6
- data/lib/padrino-core/version.rb +1 -1
- data/padrino-core.gemspec +9 -4
- data/test/fixtures/app_gem/Gemfile +4 -0
- data/test/fixtures/app_gem/app/app.rb +3 -0
- data/test/fixtures/app_gem/app_gem.gemspec +17 -0
- data/test/fixtures/app_gem/lib/app_gem.rb +7 -0
- data/test/fixtures/app_gem/lib/app_gem/version.rb +3 -0
- data/test/mini_shoulda.rb +1 -1
- data/test/test_application.rb +38 -21
- data/test/test_csrf_protection.rb +80 -0
- data/test/test_filters.rb +70 -0
- data/test/test_flash.rb +168 -0
- data/test/test_logger.rb +27 -0
- data/test/test_mounter.rb +24 -2
- data/test/test_reloader_simple.rb +4 -4
- data/test/test_rendering.rb +75 -4
- data/test/test_routing.rb +164 -35
- data/test/test_support_lite.rb +56 -0
- metadata +52 -29
@@ -33,7 +33,7 @@ module Padrino
|
|
33
33
|
# Specified constants can be excluded from the code unloading process.
|
34
34
|
#
|
35
35
|
def exclude_constants
|
36
|
-
@_exclude_constants ||=
|
36
|
+
@_exclude_constants ||= Set.new
|
37
37
|
end
|
38
38
|
|
39
39
|
##
|
@@ -41,7 +41,7 @@ module Padrino
|
|
41
41
|
# Default included constants are: [none]
|
42
42
|
#
|
43
43
|
def include_constants
|
44
|
-
@_include_constants ||=
|
44
|
+
@_include_constants ||= Set.new
|
45
45
|
end
|
46
46
|
|
47
47
|
##
|
@@ -103,9 +103,12 @@ module Padrino
|
|
103
103
|
# We lock dependencies sets to prevent reloading of protected constants
|
104
104
|
#
|
105
105
|
def lock!
|
106
|
-
klasses = ObjectSpace.classes
|
106
|
+
klasses = ObjectSpace.classes do |klass|
|
107
|
+
klass._orig_klass_name.split('::')[0]
|
108
|
+
end
|
109
|
+
|
107
110
|
klasses = klasses | Padrino.mounted_apps.map { |app| app.app_class }
|
108
|
-
Padrino::Reloader.exclude_constants.
|
111
|
+
Padrino::Reloader.exclude_constants.merge(klasses)
|
109
112
|
end
|
110
113
|
|
111
114
|
##
|
@@ -130,8 +133,8 @@ module Padrino
|
|
130
133
|
end
|
131
134
|
|
132
135
|
# Duplicate objects and loaded features before load file
|
133
|
-
klasses = ObjectSpace.classes
|
134
|
-
files = $LOADED_FEATURES.dup
|
136
|
+
klasses = ObjectSpace.classes
|
137
|
+
files = Set.new($LOADED_FEATURES.dup)
|
135
138
|
|
136
139
|
# Now we can reload dependencies of our file
|
137
140
|
if features = LOADED_FILES.delete(file)
|
@@ -142,7 +145,7 @@ module Padrino
|
|
142
145
|
begin
|
143
146
|
logger.devel :loading, began_at, file if !reload
|
144
147
|
logger.debug :reload, began_at, file if reload
|
145
|
-
$LOADED_FEATURES.delete(file)
|
148
|
+
$LOADED_FEATURES.delete(file) if files.include?(file)
|
146
149
|
verbosity_was, $-v = $-v, nil
|
147
150
|
loaded = false
|
148
151
|
require(file)
|
@@ -152,18 +155,17 @@ module Padrino
|
|
152
155
|
logger.error "Cannot require #{file} due to a syntax error: #{e.message}"
|
153
156
|
ensure
|
154
157
|
$-v = verbosity_was
|
155
|
-
new_constants =
|
158
|
+
new_constants = ObjectSpace.new_classes(klasses)
|
156
159
|
if loaded
|
157
160
|
# Store the file details
|
158
161
|
LOADED_CLASSES[file] = new_constants
|
159
|
-
LOADED_FILES[file] = ($LOADED_FEATURES - files - [file]
|
162
|
+
LOADED_FILES[file] = Set.new($LOADED_FEATURES) - files - [file]
|
160
163
|
# Track only features in our Padrino.root
|
161
164
|
LOADED_FILES[file].delete_if { |feature| !in_root?(feature) }
|
162
165
|
else
|
163
166
|
logger.devel "Failed to load #{file}; removing partially defined constants"
|
164
167
|
new_constants.each { |klass| remove_constant(klass) }
|
165
168
|
end
|
166
|
-
|
167
169
|
end
|
168
170
|
end
|
169
171
|
|
@@ -183,8 +185,8 @@ module Padrino
|
|
183
185
|
# Removes the specified class and constant.
|
184
186
|
#
|
185
187
|
def remove_constant(const)
|
186
|
-
return if exclude_constants.
|
187
|
-
!include_constants.
|
188
|
+
return if exclude_constants.any? { |c| const._orig_klass_name.index(c) == 0 } &&
|
189
|
+
!include_constants.any? { |c| const._orig_klass_name.index(c) == 0 }
|
188
190
|
begin
|
189
191
|
parts = const.to_s.sub(/^::(Object)?/, 'Object::').split('::')
|
190
192
|
object = parts.pop
|
data/lib/padrino-core/server.rb
CHANGED
@@ -4,8 +4,8 @@ module Padrino
|
|
4
4
|
# thin, mongrel, or webrick in that order.
|
5
5
|
#
|
6
6
|
# @example
|
7
|
-
# Padrino.run! # with these defaults => host: "
|
8
|
-
# Padrino.run!("
|
7
|
+
# Padrino.run! # with these defaults => host: "127.0.0.1", port: "3000", adapter: the first found
|
8
|
+
# Padrino.run!("0.0.0.0", "4000", "mongrel") # use => host: "0.0.0.0", port: "4000", adapter: "mongrel"
|
9
9
|
#
|
10
10
|
def self.run!(options={})
|
11
11
|
Padrino.load!
|
@@ -17,13 +17,13 @@ module Padrino
|
|
17
17
|
#
|
18
18
|
class Server < Rack::Server
|
19
19
|
# Server Handlers
|
20
|
-
Handlers = [:thin, :mongrel, :trinidad, :webrick]
|
20
|
+
Handlers = [:thin, :puma, :mongrel, :trinidad, :webrick]
|
21
21
|
|
22
22
|
# Starts the application on the available server with specified options.
|
23
23
|
def self.start(app, opts={})
|
24
24
|
options = {}.merge(opts) # We use a standard hash instead of Thor::CoreExt::HashWithIndifferentAccess
|
25
25
|
options.symbolize_keys!
|
26
|
-
options[:Host] = options.delete(:host) || '
|
26
|
+
options[:Host] = options.delete(:host) || '127.0.0.1'
|
27
27
|
options[:Port] = options.delete(:port) || 3000
|
28
28
|
options[:AccessLog] = []
|
29
29
|
if options[:daemonize]
|
@@ -9,6 +9,7 @@ require 'active_support/core_ext/object/blank' # present?
|
|
9
9
|
require 'active_support/core_ext/array/extract_options' # extract_options
|
10
10
|
require 'active_support/inflector/methods' # constantize
|
11
11
|
require 'active_support/inflector/inflections' # pluralize
|
12
|
+
require 'active_support/core_ext/string/output_safety' # SafeBuffer and html_safe
|
12
13
|
require 'active_support/inflections' # load default inflections
|
13
14
|
require 'yaml' unless defined?(YAML) # load yaml for i18n
|
14
15
|
require 'win32console' if RUBY_PLATFORM =~ /(win|m)32/ # ruby color support for win
|
@@ -110,13 +111,49 @@ end
|
|
110
111
|
|
111
112
|
module ObjectSpace
|
112
113
|
class << self
|
114
|
+
##
|
113
115
|
# Returns all the classes in the object space.
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
116
|
+
# Optionally, a block can be passed, for example the following code
|
117
|
+
# would return the classes that start with the character "A":
|
118
|
+
#
|
119
|
+
# ObjectSpace.classes do |klass|
|
120
|
+
# if klass.to_s[0] == "A"
|
121
|
+
# klass
|
122
|
+
# end
|
123
|
+
# end
|
124
|
+
#
|
125
|
+
def classes(&block)
|
126
|
+
rs = Set.new
|
127
|
+
|
128
|
+
ObjectSpace.each_object(Class).each do |klass|
|
129
|
+
if block
|
130
|
+
if r = block.call(klass)
|
131
|
+
# add the returned value if the block returns something
|
132
|
+
rs << r
|
133
|
+
end
|
134
|
+
else
|
135
|
+
rs << klass
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
rs
|
140
|
+
end
|
141
|
+
|
142
|
+
##
|
143
|
+
# Returns a list of existing classes that are not included in "snapshot"
|
144
|
+
# This method is useful to get the list of new classes that were loaded
|
145
|
+
# after an event like requiring a file.
|
146
|
+
# Usage:
|
147
|
+
#
|
148
|
+
# snapshot = ObjectSpace.classes
|
149
|
+
# # require a file
|
150
|
+
# ObjectSpace.new_classes(snapshot)
|
151
|
+
#
|
152
|
+
def new_classes(snapshot)
|
153
|
+
self.classes do |klass|
|
154
|
+
if !snapshot.include?(klass)
|
155
|
+
klass
|
156
|
+
end
|
120
157
|
end
|
121
158
|
end
|
122
159
|
end
|
data/lib/padrino-core/version.rb
CHANGED
data/padrino-core.gemspec
CHANGED
@@ -31,8 +31,13 @@ Gem::Specification.new do |s|
|
|
31
31
|
# s.post_install_message << "\n\e[32m" + ("*" * 20) + "\n\e[0m"
|
32
32
|
|
33
33
|
s.add_dependency("tilt", "~> 1.3.0")
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
34
|
+
if ENV["SINATRA_EDGE"]
|
35
|
+
s.add_dependency("sinatra")
|
36
|
+
else
|
37
|
+
s.add_dependency("sinatra", "~> 1.4.2")
|
38
|
+
end
|
39
|
+
s.add_dependency("http_router", "~> 0.11.0")
|
40
|
+
s.add_dependency("thor", "~> 0.17.0")
|
41
|
+
s.add_dependency("activesupport", ">= 3.1.0")
|
42
|
+
s.add_dependency("rack-protection", ">= 1.5.0")
|
38
43
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/app_gem/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Florian Gilcher"]
|
6
|
+
gem.email = ["florian.gilcher@asquera.de"]
|
7
|
+
gem.description = %q{TODO: Write a gem description}
|
8
|
+
gem.summary = %q{TODO: Write a gem summary}
|
9
|
+
gem.homepage = ""
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "app_gem"
|
15
|
+
gem.require_paths = ["app", "lib"]
|
16
|
+
gem.version = AppGem::VERSION
|
17
|
+
end
|
data/test/mini_shoulda.rb
CHANGED
data/test/test_application.rb
CHANGED
@@ -1,17 +1,13 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/helper')
|
2
|
+
require 'haml'
|
2
3
|
|
3
4
|
class PadrinoPristine < Padrino::Application; end
|
4
|
-
class PadrinoTestApp
|
5
|
+
class PadrinoTestApp < Padrino::Application; end
|
5
6
|
class PadrinoTestApp2 < Padrino::Application; end
|
6
7
|
|
7
8
|
describe "Application" do
|
8
|
-
|
9
|
-
|
10
|
-
end
|
11
|
-
|
12
|
-
def teardown
|
13
|
-
remove_views
|
14
|
-
end
|
9
|
+
before { Padrino.clear! }
|
10
|
+
after { remove_views }
|
15
11
|
|
16
12
|
context 'for application functionality' do
|
17
13
|
|
@@ -19,24 +15,36 @@ describe "Application" do
|
|
19
15
|
assert File.identical?(__FILE__, PadrinoPristine.app_file)
|
20
16
|
assert_equal :padrino_pristine, PadrinoPristine.app_name
|
21
17
|
assert_equal :test, PadrinoPristine.environment
|
22
|
-
assert_equal Padrino.root(
|
23
|
-
assert
|
18
|
+
assert_equal Padrino.root('views'), PadrinoPristine.views
|
19
|
+
assert PadrinoPristine.raise_errors
|
24
20
|
assert !PadrinoPristine.logging
|
25
21
|
assert !PadrinoPristine.sessions
|
26
22
|
assert !PadrinoPristine.dump_errors
|
27
23
|
assert !PadrinoPristine.show_exceptions
|
28
|
-
assert
|
24
|
+
assert PadrinoPristine.raise_errors
|
29
25
|
assert !Padrino.configure_apps
|
30
26
|
end
|
31
27
|
|
28
|
+
should 'check haml options on production' do
|
29
|
+
assert defined?(Haml), 'Haml not defined'
|
30
|
+
assert_equal :test, PadrinoPristine.environment
|
31
|
+
assert !PadrinoPristine.haml[:ugly]
|
32
|
+
Padrino.stub :env, :production do
|
33
|
+
PadrinoPristine.send :default_configuration!
|
34
|
+
assert_equal :production, Padrino.env
|
35
|
+
assert_equal :production, PadrinoPristine.environment
|
36
|
+
assert PadrinoPristine.haml[:ugly]
|
37
|
+
PadrinoPristine.environment = :test
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
32
41
|
should 'check padrino specific options' do
|
33
42
|
assert !PadrinoPristine.instance_variable_get(:@_configured)
|
34
43
|
PadrinoPristine.send(:setup_application!)
|
35
44
|
assert_equal :padrino_pristine, PadrinoPristine.app_name
|
36
45
|
assert_equal 'StandardFormBuilder', PadrinoPristine.default_builder
|
37
|
-
assert
|
46
|
+
assert PadrinoPristine.instance_variable_get(:@_configured)
|
38
47
|
assert !PadrinoPristine.reload?
|
39
|
-
assert !PadrinoPristine.flash
|
40
48
|
end
|
41
49
|
|
42
50
|
should 'set global project settings' do
|
@@ -48,18 +56,27 @@ describe "Application" do
|
|
48
56
|
assert_equal PadrinoTestApp.session_secret, PadrinoTestApp2.session_secret
|
49
57
|
end
|
50
58
|
|
59
|
+
should 'be able to configure_apps multiple times' do
|
60
|
+
Padrino.configure_apps { set :foo1, "bar" }
|
61
|
+
Padrino.configure_apps { set :foo1, "bam" }
|
62
|
+
Padrino.configure_apps { set :foo2, "baz" }
|
63
|
+
PadrinoTestApp.send(:default_configuration!)
|
64
|
+
assert_equal "bam", PadrinoTestApp.settings.foo1, "should have foo1 assigned to bam"
|
65
|
+
assert_equal "baz", PadrinoTestApp.settings.foo2, "should have foo2 assigned to baz"
|
66
|
+
end
|
67
|
+
|
51
68
|
should "have shared sessions accessible in project" do
|
52
69
|
Padrino.configure_apps { enable :sessions; set :session_secret, 'secret' }
|
53
70
|
Padrino.mount("PadrinoTestApp").to("/write")
|
54
71
|
Padrino.mount("PadrinoTestApp2").to("/read")
|
55
|
-
PadrinoTestApp.
|
56
|
-
|
57
|
-
PadrinoTestApp2.
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
assert_equal 'shared',
|
72
|
+
PadrinoTestApp.send :default_configuration!
|
73
|
+
PadrinoTestApp.get('/') { session[:foo] = "shared" }
|
74
|
+
PadrinoTestApp2.send(:default_configuration!)
|
75
|
+
PadrinoTestApp2.get('/') { session[:foo] }
|
76
|
+
@app = Padrino.application
|
77
|
+
get '/write'
|
78
|
+
get '/read'
|
79
|
+
assert_equal 'shared', body
|
63
80
|
end
|
64
81
|
|
65
82
|
# compare to: test_routing: allow global provides
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/helper')
|
2
|
+
|
3
|
+
describe "Application" do
|
4
|
+
before { Padrino.clear! }
|
5
|
+
after { remove_views }
|
6
|
+
|
7
|
+
context 'CSRF protection' do
|
8
|
+
context "with CSRF protection on" do
|
9
|
+
before do
|
10
|
+
mock_app do
|
11
|
+
enable :sessions
|
12
|
+
enable :protect_from_csrf
|
13
|
+
post('/'){ 'HI' }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
should "not allow requests without tokens" do
|
18
|
+
post "/"
|
19
|
+
assert_equal 403, status
|
20
|
+
end
|
21
|
+
|
22
|
+
should "allow requests with correct tokens" do
|
23
|
+
post "/", {"authenticity_token" => "a"}, 'rack.session' => {:csrf => "a"}
|
24
|
+
assert_equal 200, status
|
25
|
+
end
|
26
|
+
|
27
|
+
should "not allow requests with incorrect tokens" do
|
28
|
+
post "/", {"authenticity_token" => "a"}, 'rack.session' => {:csrf => "b"}
|
29
|
+
assert_equal 403, status
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "without CSRF protection on" do
|
34
|
+
before do
|
35
|
+
mock_app do
|
36
|
+
enable :sessions
|
37
|
+
disable :protect_from_csrf
|
38
|
+
post('/'){ 'HI' }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
should "allows requests without tokens" do
|
43
|
+
post "/"
|
44
|
+
assert_equal 200, status
|
45
|
+
end
|
46
|
+
|
47
|
+
should "allow requests with correct tokens" do
|
48
|
+
post "/", {"authenticity_token" => "a"}, 'rack.session' => {:csrf => "a"}
|
49
|
+
assert_equal 200, status
|
50
|
+
end
|
51
|
+
|
52
|
+
should "allow requests with incorrect tokens" do
|
53
|
+
post "/", {"authenticity_token" => "a"}, 'rack.session' => {:csrf => "b"}
|
54
|
+
assert_equal 200, status
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "with optional CSRF protection" do
|
59
|
+
before do
|
60
|
+
mock_app do
|
61
|
+
enable :sessions
|
62
|
+
enable :protect_from_csrf
|
63
|
+
set :allow_disabled_csrf, true
|
64
|
+
post('/on') { 'HI' }
|
65
|
+
post('/off', :csrf_protection => false) { 'HI' }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
should "allow access to routes with csrf_protection off" do
|
70
|
+
post "/off"
|
71
|
+
assert_equal 200, status
|
72
|
+
end
|
73
|
+
|
74
|
+
should "not allow access to routes with csrf_protection on" do
|
75
|
+
post "/on"
|
76
|
+
assert_equal 403, status
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/test/test_filters.rb
CHANGED
@@ -275,4 +275,74 @@ describe "Filters" do
|
|
275
275
|
get '/foo'
|
276
276
|
assert_equal 'before', test
|
277
277
|
end
|
278
|
+
|
279
|
+
should "call before filters only once" do
|
280
|
+
once = ''
|
281
|
+
mock_app do
|
282
|
+
error 500 do
|
283
|
+
'error 500'
|
284
|
+
end
|
285
|
+
before do
|
286
|
+
once += 'before'
|
287
|
+
end
|
288
|
+
get :index do
|
289
|
+
raise Exception, 'Oops'
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
get '/'
|
294
|
+
assert_equal 'before', once
|
295
|
+
end
|
296
|
+
|
297
|
+
should 'catch exceptions in before filters' do
|
298
|
+
doodle = nil
|
299
|
+
mock_app do
|
300
|
+
after do
|
301
|
+
doodle = 'Been after'
|
302
|
+
end
|
303
|
+
before do
|
304
|
+
raise StandardError, "before"
|
305
|
+
end
|
306
|
+
get :index do
|
307
|
+
doodle = 'Been now'
|
308
|
+
end
|
309
|
+
error 500 do
|
310
|
+
"We broke #{env['sinatra.error'].message}"
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
get '/'
|
315
|
+
assert_equal 'We broke before', body
|
316
|
+
assert_equal nil, doodle
|
317
|
+
end
|
318
|
+
|
319
|
+
should 'catch exceptions in after filters if no exceptions caught before' do
|
320
|
+
doodle = ''
|
321
|
+
mock_app do
|
322
|
+
after do
|
323
|
+
doodle += ' and after'
|
324
|
+
raise StandardError, "after"
|
325
|
+
end
|
326
|
+
get :foo do
|
327
|
+
doodle = 'Been now'
|
328
|
+
raise StandardError, "now"
|
329
|
+
end
|
330
|
+
get :index do
|
331
|
+
doodle = 'Been now'
|
332
|
+
end
|
333
|
+
error 500 do
|
334
|
+
"We broke #{env['sinatra.error'].message}"
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
get '/foo'
|
339
|
+
assert_equal 'We broke now', body
|
340
|
+
assert_equal 'Been now', doodle
|
341
|
+
|
342
|
+
doodle = ''
|
343
|
+
get '/'
|
344
|
+
assert_equal 'We broke after', body
|
345
|
+
assert_equal 'Been now and after', doodle
|
346
|
+
end
|
347
|
+
|
278
348
|
end
|