padrino-core 0.10.7 → 0.11.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/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
|