padrino-core 0.11.4 → 0.12.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/padrino-core/application/authenticity_token.rb +25 -0
- data/lib/padrino-core/application/rendering/extensions/erubis.rb +11 -7
- data/lib/padrino-core/application/rendering/extensions/haml.rb +3 -2
- data/lib/padrino-core/application/rendering/extensions/slim.rb +10 -3
- data/lib/padrino-core/application/rendering.rb +13 -5
- data/lib/padrino-core/application/routing.rb +62 -19
- data/lib/padrino-core/application.rb +136 -50
- data/lib/padrino-core/cli/base.rb +32 -1
- data/lib/padrino-core/loader.rb +68 -68
- data/lib/padrino-core/logger.rb +6 -6
- data/lib/padrino-core/mounter.rb +9 -4
- data/lib/padrino-core/reloader/rack.rb +26 -0
- data/lib/padrino-core/reloader/storage.rb +55 -0
- data/lib/padrino-core/reloader.rb +192 -287
- data/lib/padrino-core/router.rb +11 -12
- data/lib/padrino-core/server.rb +5 -0
- data/lib/padrino-core/support_lite.rb +31 -32
- data/lib/padrino-core/version.rb +1 -1
- data/lib/padrino-core.rb +22 -30
- data/padrino-core.gemspec +2 -1
- data/test/fixtures/apps/helpers/system_helpers.rb +8 -0
- data/test/fixtures/apps/kiq.rb +3 -0
- data/test/fixtures/apps/lib/myklass/mysubklass.rb +4 -0
- data/test/fixtures/apps/lib/myklass.rb +2 -0
- data/test/fixtures/apps/models/child.rb +2 -0
- data/test/fixtures/apps/models/parent.rb +5 -0
- data/test/fixtures/apps/render.rb +13 -0
- data/test/fixtures/apps/system.rb +13 -0
- data/test/fixtures/apps/views/blog/post.erb +1 -0
- data/test/helper.rb +1 -1
- data/test/mini_shoulda.rb +4 -4
- data/test/test_application.rb +5 -13
- data/test/test_core.rb +2 -6
- data/test/test_csrf_protection.rb +67 -1
- data/test/test_dependencies.rb +14 -1
- data/test/test_mounter.rb +50 -6
- data/test/test_reloader_complex.rb +2 -2
- data/test/test_reloader_simple.rb +1 -1
- data/test/test_reloader_system.rb +52 -0
- data/test/test_rendering.rb +54 -0
- data/test/test_router.rb +121 -2
- data/test/test_routing.rb +84 -15
- metadata +29 -11
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'pathname'
|
2
|
+
require 'padrino-core/reloader/rack'
|
3
|
+
require 'padrino-core/reloader/storage'
|
2
4
|
|
3
5
|
module Padrino
|
4
6
|
##
|
@@ -12,330 +14,233 @@ module Padrino
|
|
12
14
|
# Please note that this will not reload files in the background, and does so
|
13
15
|
# only when explicitly invoked.
|
14
16
|
#
|
17
|
+
extend self
|
15
18
|
|
16
19
|
# The modification times for every file in a project.
|
17
|
-
MTIMES
|
18
|
-
# The list of files loaded as part of a project.
|
19
|
-
LOADED_FILES = {}
|
20
|
-
# The list of object constants and classes loaded as part of the project.
|
21
|
-
LOADED_CLASSES = {}
|
20
|
+
MTIMES = {}
|
22
21
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
end
|
31
|
-
|
32
|
-
##
|
33
|
-
# Specified constants can be excluded from the code unloading process.
|
34
|
-
#
|
35
|
-
def exclude_constants
|
36
|
-
@_exclude_constants ||= Set.new
|
37
|
-
end
|
38
|
-
|
39
|
-
##
|
40
|
-
# Specified constants can be configured to be reloaded on every request.
|
41
|
-
# Default included constants are: [none]
|
42
|
-
#
|
43
|
-
def include_constants
|
44
|
-
@_include_constants ||= Set.new
|
45
|
-
end
|
46
|
-
|
47
|
-
##
|
48
|
-
# Reload all files with changes detected.
|
49
|
-
#
|
50
|
-
def reload!
|
51
|
-
# Detect changed files
|
52
|
-
rotation do |file, mtime|
|
53
|
-
# Retrive the last modified time
|
54
|
-
new_file = MTIMES[file].nil?
|
55
|
-
previous_mtime = MTIMES[file] ||= mtime
|
56
|
-
logger.devel "Detected a new file #{file}" if new_file
|
57
|
-
# We skip to next file if it is not new and not modified
|
58
|
-
next unless new_file || mtime > previous_mtime
|
59
|
-
# Now we can reload our file
|
60
|
-
apps = mounted_apps_of(file)
|
61
|
-
if apps.present?
|
62
|
-
apps.each { |app| app.app_obj.reload! }
|
63
|
-
else
|
64
|
-
safe_load(file, :force => new_file)
|
65
|
-
# Reload also apps
|
66
|
-
Padrino.mounted_apps.each do |app|
|
67
|
-
app.app_obj.reload! if app.app_obj.dependencies.include?(file)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
##
|
74
|
-
# Remove files and classes loaded with stat
|
75
|
-
#
|
76
|
-
def clear!
|
77
|
-
clear_modification_times
|
78
|
-
clear_loaded_classes
|
79
|
-
clear_loaded_files_and_features
|
80
|
-
end
|
81
|
-
|
82
|
-
##
|
83
|
-
# Returns true if any file changes are detected and populates the MTIMES cache
|
84
|
-
#
|
85
|
-
def changed?
|
86
|
-
changed = false
|
87
|
-
rotation do |file, mtime|
|
88
|
-
new_file = MTIMES[file].nil?
|
89
|
-
previous_mtime = MTIMES[file]
|
90
|
-
changed = true if new_file || mtime > previous_mtime
|
91
|
-
end
|
92
|
-
changed
|
93
|
-
end
|
94
|
-
alias :run! :changed?
|
95
|
-
|
96
|
-
##
|
97
|
-
# We lock dependencies sets to prevent reloading of protected constants
|
98
|
-
#
|
99
|
-
def lock!
|
100
|
-
klasses = ObjectSpace.classes do |klass|
|
101
|
-
klass._orig_klass_name.split('::')[0]
|
102
|
-
end
|
103
|
-
|
104
|
-
klasses = klasses | Padrino.mounted_apps.map { |app| app.app_class }
|
105
|
-
Padrino::Reloader.exclude_constants.merge(klasses)
|
106
|
-
end
|
107
|
-
|
108
|
-
##
|
109
|
-
# A safe Kernel::require which issues the necessary hooks depending on results
|
110
|
-
#
|
111
|
-
def safe_load(file, options={})
|
112
|
-
began_at = Time.now
|
113
|
-
force = options[:force]
|
114
|
-
file = figure_path(file)
|
115
|
-
reload = should_reload?(file)
|
116
|
-
m_time = modification_time(file)
|
117
|
-
|
118
|
-
return if !force && m_time && !reload
|
119
|
-
|
120
|
-
remove_loaded_file_classes(file)
|
121
|
-
remove_loaded_file_features(file)
|
122
|
-
|
123
|
-
# Duplicate objects and loaded features before load file
|
124
|
-
klasses = ObjectSpace.classes
|
125
|
-
files = Set.new($LOADED_FEATURES.dup)
|
126
|
-
|
127
|
-
reload_deps_of_file(file)
|
128
|
-
|
129
|
-
# And finally load the specified file
|
130
|
-
begin
|
131
|
-
logger.devel :loading, began_at, file if !reload
|
132
|
-
logger.debug :reload, began_at, file if reload
|
22
|
+
##
|
23
|
+
# Specified folders can be excluded from the code reload detection process.
|
24
|
+
# Default excluded directories at Padrino.root are: test, spec, features, tmp, config, db and public
|
25
|
+
#
|
26
|
+
def exclude
|
27
|
+
@_exclude ||= Set.new %w(test spec tmp features config public db).map{ |path| Padrino.root(path) }
|
28
|
+
end
|
133
29
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
rescue SyntaxError => e
|
141
|
-
logger.error "Cannot require #{file} due to a syntax error: #{e.message}"
|
142
|
-
ensure
|
143
|
-
Padrino::Utils.unsilence_output
|
144
|
-
new_constants = ObjectSpace.new_classes(klasses)
|
145
|
-
if loaded
|
146
|
-
process_loaded_file(:file => file,
|
147
|
-
:constants => new_constants,
|
148
|
-
:files => files)
|
149
|
-
else
|
150
|
-
logger.devel "Failed to load #{file}; removing partially defined constants"
|
151
|
-
unload_constants(new_constants)
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|
30
|
+
##
|
31
|
+
# Specified constants can be excluded from the code unloading process.
|
32
|
+
#
|
33
|
+
def exclude_constants
|
34
|
+
@_exclude_constants ||= Set.new
|
35
|
+
end
|
155
36
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
return File.expand_path(found) if File.exist?(found)
|
164
|
-
end
|
165
|
-
file
|
166
|
-
end
|
37
|
+
##
|
38
|
+
# Specified constants can be configured to be reloaded on every request.
|
39
|
+
# Default included constants are: [none]
|
40
|
+
#
|
41
|
+
def include_constants
|
42
|
+
@_include_constants ||= Set.new
|
43
|
+
end
|
167
44
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
parts = const.to_s.sub(/^::(Object)?/, 'Object::').split('::')
|
176
|
-
object = parts.pop
|
177
|
-
base = parts.empty? ? Object : Inflector.constantize(parts * '::')
|
178
|
-
base.send :remove_const, object
|
179
|
-
logger.devel "Removed constant: #{const} from #{base}"
|
180
|
-
rescue NameError; end
|
45
|
+
##
|
46
|
+
# Reload apps and files with changes detected.
|
47
|
+
#
|
48
|
+
def reload!
|
49
|
+
rotation do |file|
|
50
|
+
next unless file_changed?(file)
|
51
|
+
reload_special(file) || reload_regular(file)
|
181
52
|
end
|
53
|
+
end
|
182
54
|
|
183
|
-
|
55
|
+
##
|
56
|
+
# Remove files and classes loaded with stat
|
57
|
+
#
|
58
|
+
def clear!
|
59
|
+
MTIMES.clear
|
60
|
+
Storage.clear!
|
61
|
+
end
|
184
62
|
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
63
|
+
##
|
64
|
+
# Returns true if any file changes are detected.
|
65
|
+
#
|
66
|
+
def changed?
|
67
|
+
rotation do |file|
|
68
|
+
break true if file_changed?(file)
|
190
69
|
end
|
70
|
+
end
|
191
71
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
72
|
+
##
|
73
|
+
# We lock dependencies sets to prevent reloading of protected constants
|
74
|
+
#
|
75
|
+
def lock!
|
76
|
+
klasses = ObjectSpace.classes do |klass|
|
77
|
+
klass._orig_klass_name.split('::').first
|
197
78
|
end
|
79
|
+
klasses |= Padrino.mounted_apps.map(&:app_class)
|
80
|
+
exclude_constants.merge(klasses)
|
81
|
+
end
|
198
82
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
83
|
+
##
|
84
|
+
# A safe Kernel::require which issues the necessary hooks depending on results
|
85
|
+
#
|
86
|
+
def safe_load(file, options={})
|
87
|
+
began_at = Time.now
|
88
|
+
file = figure_path(file)
|
89
|
+
return unless options[:force] || file_changed?(file)
|
90
|
+
|
91
|
+
Storage.prepare(file) # might call #safe_load recursively
|
92
|
+
logger.debug(file_new?(file) ? :loading : :reload, began_at, file)
|
93
|
+
begin
|
94
|
+
with_silence{ require(file) }
|
95
|
+
Storage.commit(file)
|
96
|
+
update_modification_time(file)
|
97
|
+
rescue Exception => e
|
98
|
+
unless options[:cyclic]
|
99
|
+
logger.error "#{e.class}: #{e.message}; #{e.backtrace.first}"
|
100
|
+
logger.error "Failed to load #{file}; removing partially defined constants"
|
203
101
|
end
|
102
|
+
Storage.rollback(file)
|
103
|
+
raise e
|
204
104
|
end
|
105
|
+
end
|
205
106
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
107
|
+
##
|
108
|
+
# Removes the specified class and constant.
|
109
|
+
#
|
110
|
+
def remove_constant(const)
|
111
|
+
return if constant_excluded?(const)
|
112
|
+
base, _, object = const.to_s.rpartition('::')
|
113
|
+
base = base.empty? ? Object : base.constantize
|
114
|
+
base.send :remove_const, object
|
115
|
+
logger.devel "Removed constant #{const} from #{base}"
|
116
|
+
rescue NameError
|
117
|
+
end
|
212
118
|
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
119
|
+
##
|
120
|
+
# Returns the list of special tracked files for Reloader.
|
121
|
+
#
|
122
|
+
def special_files
|
123
|
+
@special_files ||= Set.new
|
124
|
+
end
|
219
125
|
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
files = options[:files]
|
227
|
-
file = options[:file]
|
126
|
+
##
|
127
|
+
# Sets the list of special tracked files for Reloader.
|
128
|
+
#
|
129
|
+
def special_files=(files)
|
130
|
+
@special_files = Set.new(files)
|
131
|
+
end
|
228
132
|
|
229
|
-
|
230
|
-
LOADED_CLASSES[file] = new_constants
|
231
|
-
LOADED_FILES[file] = Set.new($LOADED_FEATURES) - files - [file]
|
133
|
+
private
|
232
134
|
|
233
|
-
|
234
|
-
|
135
|
+
##
|
136
|
+
# Returns absolute path of the file.
|
137
|
+
#
|
138
|
+
def figure_path(file)
|
139
|
+
return file if Pathname.new(file).absolute?
|
140
|
+
$LOAD_PATH.each do |path|
|
141
|
+
found = File.join(path, file)
|
142
|
+
return File.expand_path(found) if File.file?(found)
|
235
143
|
end
|
144
|
+
file
|
145
|
+
end
|
236
146
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
147
|
+
##
|
148
|
+
# Reloads the file if it's special. For now it's only I18n locale files.
|
149
|
+
#
|
150
|
+
def reload_special(file)
|
151
|
+
return unless special_files.any?{ |f| File.identical?(f, file) }
|
152
|
+
if defined?(I18n)
|
153
|
+
began_at = Time.now
|
154
|
+
I18n.reload!
|
155
|
+
update_modification_time(file)
|
156
|
+
logger.debug :reload, began_at, file
|
242
157
|
end
|
158
|
+
true
|
159
|
+
end
|
243
160
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
161
|
+
##
|
162
|
+
# Reloads ruby file and applications dependent on it.
|
163
|
+
#
|
164
|
+
def reload_regular(file)
|
165
|
+
apps = mounted_apps_of(file)
|
166
|
+
if apps.present?
|
167
|
+
apps.each { |app| app.app_obj.reload! }
|
168
|
+
update_modification_time(file)
|
169
|
+
else
|
170
|
+
safe_load(file)
|
171
|
+
Padrino.mounted_apps.each do |app|
|
172
|
+
app.app_obj.reload! if app.app_obj.dependencies.include?(file)
|
250
173
|
end
|
251
174
|
end
|
175
|
+
end
|
252
176
|
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
177
|
+
###
|
178
|
+
# Macro for mtime update.
|
179
|
+
#
|
180
|
+
def update_modification_time(file)
|
181
|
+
MTIMES[file] = File.mtime(file)
|
182
|
+
end
|
259
183
|
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
end
|
267
|
-
end
|
184
|
+
###
|
185
|
+
# Returns true if the file is new or it's modification time changed.
|
186
|
+
#
|
187
|
+
def file_changed?(file)
|
188
|
+
file_new?(file) || File.mtime(file) > MTIMES[file]
|
189
|
+
end
|
268
190
|
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
end
|
276
|
-
end
|
191
|
+
###
|
192
|
+
# Returns true if the file is new.
|
193
|
+
#
|
194
|
+
def file_new?(file)
|
195
|
+
MTIMES[file].nil?
|
196
|
+
end
|
277
197
|
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
end
|
198
|
+
##
|
199
|
+
# Return the mounted_apps providing the app location.
|
200
|
+
# Can be an array because in one app.rb we can define multiple Padrino::Application.
|
201
|
+
#
|
202
|
+
def mounted_apps_of(file)
|
203
|
+
Padrino.mounted_apps.select { |app| File.identical?(file, app.app_file) }
|
204
|
+
end
|
286
205
|
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
206
|
+
##
|
207
|
+
# Searches Ruby files in your +Padrino.load_paths+ , Padrino::Application.load_paths
|
208
|
+
# and monitors them for any changes.
|
209
|
+
#
|
210
|
+
def rotation
|
211
|
+
files_for_rotation.each do |file|
|
212
|
+
file = File.expand_path(file)
|
213
|
+
next if Reloader.exclude.any? { |base| file.start_with?(base) } || !File.file?(file)
|
214
|
+
yield file
|
294
215
|
end
|
216
|
+
nil
|
217
|
+
end
|
295
218
|
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
219
|
+
##
|
220
|
+
# Creates an array of paths for use in #rotation.
|
221
|
+
#
|
222
|
+
def files_for_rotation
|
223
|
+
files = Set.new
|
224
|
+
Padrino.load_paths.each{ |path| files += Dir.glob("#{path}/**/*.rb") }
|
225
|
+
Padrino.mounted_apps.each do |app|
|
226
|
+
files << app.app_file
|
227
|
+
files += app.app_obj.dependencies
|
228
|
+
end
|
229
|
+
files + special_files
|
230
|
+
end
|
307
231
|
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
def files_for_rotation
|
312
|
-
files = Padrino.load_paths.map { |path| Dir["#{path}/**/*.rb"] }.flatten
|
313
|
-
files = files | Padrino.mounted_apps.map { |app| app.app_file }
|
314
|
-
files = files | Padrino.mounted_apps.map { |app| app.app_obj.dependencies }.flatten
|
315
|
-
end
|
316
|
-
end # self
|
232
|
+
def constant_excluded?(const)
|
233
|
+
(exclude_constants - include_constants).any?{ |c| const._orig_klass_name.start_with?(c) }
|
234
|
+
end
|
317
235
|
|
318
236
|
##
|
319
|
-
#
|
320
|
-
# This middleware performs a check and reload for source files at the start
|
321
|
-
# of each request, but also respects a specified cool down time
|
322
|
-
# during which no further action will be taken.
|
237
|
+
# Disables output, yields block, switches output back.
|
323
238
|
#
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
end
|
330
|
-
|
331
|
-
# Invoked in order to perform the reload as part of the request stack.
|
332
|
-
def call(env)
|
333
|
-
if @cooldown && Time.now > @last + @cooldown
|
334
|
-
Thread.list.size > 1 ? Thread.exclusive { Padrino.reload! } : Padrino.reload!
|
335
|
-
@last = Time.now
|
336
|
-
end
|
337
|
-
@app.call(env)
|
338
|
-
end
|
239
|
+
def with_silence
|
240
|
+
verbosity_level, $-v = $-v, nil
|
241
|
+
yield
|
242
|
+
ensure
|
243
|
+
$-v = verbosity_level
|
339
244
|
end
|
340
245
|
end
|
341
246
|
end
|
data/lib/padrino-core/router.rb
CHANGED
@@ -62,14 +62,15 @@ module Padrino
|
|
62
62
|
host = Regexp.new("^#{Regexp.quote(host)}$", true, 'n') unless host.nil? || host.is_a?(Regexp)
|
63
63
|
|
64
64
|
@mapping << [host, path, match, app]
|
65
|
-
sort!
|
66
65
|
end
|
67
66
|
|
68
67
|
# The call handler setup to route a request given the mappings specified.
|
69
68
|
def call(env)
|
69
|
+
began_at = Time.now
|
70
70
|
path_info = env["PATH_INFO"].to_s
|
71
71
|
script_name = env['SCRIPT_NAME']
|
72
72
|
http_host = env['HTTP_HOST']
|
73
|
+
last_result = nil
|
73
74
|
|
74
75
|
@mapping.each do |host, path, match, app|
|
75
76
|
next unless host.nil? || http_host =~ host
|
@@ -78,18 +79,16 @@ module Padrino
|
|
78
79
|
|
79
80
|
rest = "/" if rest.empty?
|
80
81
|
|
81
|
-
|
82
|
-
env.merge(
|
83
|
-
'SCRIPT_NAME' => (script_name + path),
|
84
|
-
'PATH_INFO' => rest))
|
85
|
-
end
|
86
|
-
[404, {"Content-Type" => "text/plain", "X-Cascade" => "pass"}, ["Not Found: #{path_info}"]]
|
87
|
-
end
|
88
|
-
|
89
|
-
private
|
82
|
+
last_result = app.call(env.merge('SCRIPT_NAME' => script_name + path, 'PATH_INFO' => rest))
|
90
83
|
|
91
|
-
|
92
|
-
|
84
|
+
cascade_setting = app.respond_to?(:cascade) ? app.cascade : true
|
85
|
+
cascade_statuses = cascade_setting.respond_to?(:include?) ? cascade_setting : Mounter::DEFAULT_CASCADE
|
86
|
+
break unless cascade_setting && cascade_statuses.include?(last_result[0])
|
87
|
+
end
|
88
|
+
last_result || begin
|
89
|
+
Padrino::Logger::Rack.new(nil,'/').send(:log, env, 404, {}, began_at) if logger.debug?
|
90
|
+
[404, {"Content-Type" => "text/plain", "X-Cascade" => "pass"}, ["Not Found: #{path_info}"]]
|
91
|
+
end
|
93
92
|
end
|
94
93
|
end
|
95
94
|
end
|
data/lib/padrino-core/server.rb
CHANGED
@@ -31,6 +31,11 @@ module Padrino
|
|
31
31
|
FileUtils.mkdir_p(File.dirname(options[:pid]))
|
32
32
|
end
|
33
33
|
options[:server] = detect_rack_handler if options[:server].blank?
|
34
|
+
if options[:options].is_a?(Array)
|
35
|
+
parsed_server_options = options.delete(:options).map { |opt| opt.split('=', 2) }.flatten
|
36
|
+
server_options = Hash[*parsed_server_options].symbolize_keys!
|
37
|
+
options.merge!(server_options)
|
38
|
+
end
|
34
39
|
new(options, app).start
|
35
40
|
end
|
36
41
|
|
@@ -4,6 +4,7 @@
|
|
4
4
|
require 'active_support/core_ext/module/aliasing' # alias_method_chain
|
5
5
|
require 'active_support/core_ext/hash/keys' # symbolize_keys
|
6
6
|
require 'active_support/core_ext/hash/reverse_merge' # reverse_merge
|
7
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
7
8
|
require 'active_support/core_ext/hash/slice' # slice
|
8
9
|
require 'active_support/core_ext/object/blank' # present?
|
9
10
|
require 'active_support/core_ext/array/extract_options' # extract_options
|
@@ -199,27 +200,41 @@ end
|
|
199
200
|
# puts help.red.bold
|
200
201
|
#
|
201
202
|
class String
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
:bold => 1,
|
206
|
-
:black => 30,
|
207
|
-
:red => 31,
|
208
|
-
:green => 32,
|
209
|
-
:yellow => 33,
|
210
|
-
:blue => 34,
|
211
|
-
:magenta => 35,
|
212
|
-
:cyan => 36,
|
213
|
-
:white => 37
|
214
|
-
}
|
203
|
+
# colorize(:red)
|
204
|
+
def colorize(color)
|
205
|
+
Colorizer.send(color, self)
|
215
206
|
end
|
216
207
|
|
217
|
-
|
218
|
-
|
219
|
-
|
208
|
+
# Used to colorize strings for the shell
|
209
|
+
class Colorizer
|
210
|
+
# Returns colors integer mapping
|
211
|
+
def self.colors
|
212
|
+
@_colors ||= {
|
213
|
+
:clear => 0,
|
214
|
+
:bold => 1,
|
215
|
+
:black => 30,
|
216
|
+
:red => 31,
|
217
|
+
:green => 32,
|
218
|
+
:yellow => 33,
|
219
|
+
:blue => 34,
|
220
|
+
:magenta => 35,
|
221
|
+
:cyan => 36,
|
222
|
+
:white => 37
|
223
|
+
}
|
224
|
+
end
|
225
|
+
|
226
|
+
# Defines class level color methods
|
227
|
+
# i.e Colorizer.red("hello")
|
228
|
+
class << self
|
229
|
+
Colorizer.colors.each do |color, value|
|
230
|
+
define_method(color) do |target|
|
231
|
+
"\e[#{value}m" << target << "\e[0m"
|
232
|
+
end
|
233
|
+
end
|
220
234
|
end
|
221
235
|
end
|
222
236
|
|
237
|
+
# Strip unnecessary indentation of the front of a string
|
223
238
|
def undent
|
224
239
|
gsub(/^.{#{slice(/^ +/).size}}/, '')
|
225
240
|
end
|
@@ -242,19 +257,3 @@ I18n.load_path += Dir["#{File.dirname(__FILE__)}/locale/*.yml"] if defined?(I18n
|
|
242
257
|
# Used to determine if this file has already been required
|
243
258
|
#
|
244
259
|
module SupportLite; end
|
245
|
-
|
246
|
-
module Padrino
|
247
|
-
class Utils
|
248
|
-
###
|
249
|
-
# Silences output verbosity level so load
|
250
|
-
# errors are not visible when safe_load(file)
|
251
|
-
#
|
252
|
-
def self.silence_output
|
253
|
-
@verbosity_level, $-v = $-v, nil
|
254
|
-
end
|
255
|
-
|
256
|
-
def self.unsilence_output
|
257
|
-
$-v = @verbosity_level
|
258
|
-
end
|
259
|
-
end
|
260
|
-
end
|
data/lib/padrino-core/version.rb
CHANGED