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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/lib/padrino-core/application/authenticity_token.rb +25 -0
  3. data/lib/padrino-core/application/rendering/extensions/erubis.rb +11 -7
  4. data/lib/padrino-core/application/rendering/extensions/haml.rb +3 -2
  5. data/lib/padrino-core/application/rendering/extensions/slim.rb +10 -3
  6. data/lib/padrino-core/application/rendering.rb +13 -5
  7. data/lib/padrino-core/application/routing.rb +62 -19
  8. data/lib/padrino-core/application.rb +136 -50
  9. data/lib/padrino-core/cli/base.rb +32 -1
  10. data/lib/padrino-core/loader.rb +68 -68
  11. data/lib/padrino-core/logger.rb +6 -6
  12. data/lib/padrino-core/mounter.rb +9 -4
  13. data/lib/padrino-core/reloader/rack.rb +26 -0
  14. data/lib/padrino-core/reloader/storage.rb +55 -0
  15. data/lib/padrino-core/reloader.rb +192 -287
  16. data/lib/padrino-core/router.rb +11 -12
  17. data/lib/padrino-core/server.rb +5 -0
  18. data/lib/padrino-core/support_lite.rb +31 -32
  19. data/lib/padrino-core/version.rb +1 -1
  20. data/lib/padrino-core.rb +22 -30
  21. data/padrino-core.gemspec +2 -1
  22. data/test/fixtures/apps/helpers/system_helpers.rb +8 -0
  23. data/test/fixtures/apps/kiq.rb +3 -0
  24. data/test/fixtures/apps/lib/myklass/mysubklass.rb +4 -0
  25. data/test/fixtures/apps/lib/myklass.rb +2 -0
  26. data/test/fixtures/apps/models/child.rb +2 -0
  27. data/test/fixtures/apps/models/parent.rb +5 -0
  28. data/test/fixtures/apps/render.rb +13 -0
  29. data/test/fixtures/apps/system.rb +13 -0
  30. data/test/fixtures/apps/views/blog/post.erb +1 -0
  31. data/test/helper.rb +1 -1
  32. data/test/mini_shoulda.rb +4 -4
  33. data/test/test_application.rb +5 -13
  34. data/test/test_core.rb +2 -6
  35. data/test/test_csrf_protection.rb +67 -1
  36. data/test/test_dependencies.rb +14 -1
  37. data/test/test_mounter.rb +50 -6
  38. data/test/test_reloader_complex.rb +2 -2
  39. data/test/test_reloader_simple.rb +1 -1
  40. data/test/test_reloader_system.rb +52 -0
  41. data/test/test_rendering.rb +54 -0
  42. data/test/test_router.rb +121 -2
  43. data/test/test_routing.rb +84 -15
  44. 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
- class << self
24
- ##
25
- # Specified folders can be excluded from the code reload detection process.
26
- # Default excluded directories at Padrino.root are: test, spec, features, tmp, config, db and public
27
- #
28
- def exclude
29
- @_exclude ||= %w(test spec tmp features config public db).map { |path| Padrino.root(path) }
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
- $LOADED_FEATURES.delete(file) if files.include?(file)
135
- Padrino::Utils.silence_output
136
- loaded = false
137
- require(file)
138
- loaded = true
139
- update_modification_time(file)
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
- # Returns true if the file is defined in our padrino root.
158
- #
159
- def figure_path(file)
160
- return file if Pathname.new(file).absolute?
161
- $:.each do |path|
162
- found = File.join(path, file)
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
- # Removes the specified class and constant.
170
- #
171
- def remove_constant(const)
172
- return if exclude_constants.any? { |c| const._orig_klass_name.index(c) == 0 } &&
173
- !include_constants.any? { |c| const._orig_klass_name.index(c) == 0 }
174
- begin
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
- private
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
- # Clear instance variables that keep track of # loaded features/files/mtimes.
187
- #
188
- def clear_modification_times
189
- MTIMES.clear
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
- def clear_loaded_classes
193
- LOADED_CLASSES.each do |file, klasses|
194
- klasses.each { |klass| remove_constant(klass) }
195
- LOADED_CLASSES.delete(file)
196
- end
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
- def clear_loaded_files_and_features
200
- LOADED_FILES.each do |file, dependencies|
201
- dependencies.each { |dependency| $LOADED_FEATURES.delete(dependency) }
202
- $LOADED_FEATURES.delete(file)
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
- # Macro for mtime query.
208
- #
209
- def modification_time(file)
210
- MTIMES[file]
211
- end
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
- # Macro for mtime update.
215
- #
216
- def update_modification_time(file)
217
- MTIMES[file] = File.mtime(file)
218
- end
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
- # Tracks loaded file features/classes/constants:
222
- #
223
- def process_loaded_file(*args)
224
- options = args.extract_options!
225
- new_constants = options[:constants]
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
- # Store the file details
230
- LOADED_CLASSES[file] = new_constants
231
- LOADED_FILES[file] = Set.new($LOADED_FEATURES) - files - [file]
133
+ private
232
134
 
233
- # Track only features in our Padrino.root
234
- LOADED_FILES[file].delete_if { |feature| !in_root?(feature) }
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
- # Unloads all constants in new_constants.
239
- #
240
- def unload_constants(new_constants)
241
- new_constants.each { |klass| remove_constant(klass) }
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
- # Safe load dependencies of a file.
246
- #
247
- def reload_deps_of_file(file)
248
- if features = LOADED_FILES.delete(file)
249
- features.each { |feature| safe_load(feature, :force => true) }
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
- # Check if file was changed or if force a reload.
255
- #
256
- def should_reload?(file)
257
- MTIMES[file] && File.mtime(file) > MTIMES[file]
258
- end
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
- # Removes all classes declared in the specified file.
262
- #
263
- def remove_loaded_file_classes(file)
264
- if klasses = LOADED_CLASSES.delete(file)
265
- klasses.each { |klass| remove_constant(klass) }
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
- # Remove all loaded fatures with our file.
271
- #
272
- def remove_loaded_file_features(file)
273
- if features = LOADED_FILES[file]
274
- features.each { |feature| $LOADED_FEATURES.delete(feature) }
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
- # Return the mounted_apps providing the app location.
280
- # Can be an array because in one app.rb we can define multiple Padrino::Application.
281
- #
282
- def mounted_apps_of(file)
283
- file = figure_path(file)
284
- Padrino.mounted_apps.find_all { |app| File.identical?(file, app.app_file) }
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
- # Returns true if file is in our Padrino.root.
289
- #
290
- def in_root?(file)
291
- # This is better but slow:
292
- # Pathname.new(Padrino.root).find { |f| File.identical?(Padrino.root(f), figure_path(file)) }
293
- figure_path(file).index(Padrino.root) == 0
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
- # Searches Ruby files in your +Padrino.load_paths+ , Padrino::Application.load_paths
298
- # and monitors them for any changes.
299
- #
300
- def rotation
301
- files_for_rotation.uniq.map do |file|
302
- file = File.expand_path(file)
303
- next if Padrino::Reloader.exclude.any? { |base| file.index(base) == 0 } || !File.exist?(file)
304
- yield file, File.mtime(file)
305
- end.compact
306
- end
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
- # Creates an array of paths for use in #rotation.
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
- # This class acts as a Rack middleware to be added to the application stack.
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
- class Rack
325
- def initialize(app, cooldown=1)
326
- @app = app
327
- @cooldown = cooldown
328
- @last = (Time.now - cooldown)
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
@@ -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
- return app.call(
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
- def sort!
92
- @mapping = @mapping.sort_by { |h, p, m, a| -p.size }
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
@@ -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
- def self.colors
203
- @_colors ||= {
204
- :clear => 0,
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
- colors.each do |color, value|
218
- define_method(color) do
219
- ["\e[", value.to_s, "m", self, "\e[", self.class.colors[:clear], "m"] * ''
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
@@ -6,7 +6,7 @@
6
6
  #
7
7
  module Padrino
8
8
  # The version constant for the current version of Padrino.
9
- VERSION = '0.11.4' unless defined?(Padrino::VERSION)
9
+ VERSION = '0.12.0.rc1' unless defined?(Padrino::VERSION)
10
10
 
11
11
  #
12
12
  # The current Padrino version.