padrino-core 0.9.10 → 0.9.11

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,13 +4,8 @@ module Padrino
4
4
  class << self
5
5
  # Start for the given options a rackup handler
6
6
  def start(options)
7
-
8
- puts "=> Padrino/#{Padrino.version} has taken the stage #{options.environment} on port #{options.port}"
9
-
10
7
  if options.daemonize?
11
-
12
8
  stop # Need to stop a process if it exists
13
-
14
9
  fork do
15
10
  Process.setsid
16
11
  exit if fork
@@ -27,11 +22,11 @@ module Padrino
27
22
  File.open(pid, 'w'){ |f| f.write("#{Process.pid}") }
28
23
  end
29
24
 
30
- Padrino.run!(options.host, options.port, options.adapter)
25
+ Padrino.run!(options.symbolize_keys)
31
26
  exit
32
27
  end
33
28
  else
34
- Padrino.run!(options.host, options.port, options.adapter)
29
+ Padrino.run!(options.symbolize_keys)
35
30
  end
36
31
  end
37
32
 
@@ -1,3 +1,4 @@
1
+ require 'rubygems'
1
2
  require 'thor'
2
3
 
3
4
  module Padrino
@@ -19,7 +20,7 @@ module Padrino
19
20
  def start
20
21
  prepare :start
21
22
  require File.expand_path(File.dirname(__FILE__) + "/adapter")
22
- require 'config/boot'
23
+ require File.expand_path('config/boot.rb')
23
24
  Padrino::Cli::Adapter.start(options)
24
25
  end
25
26
 
@@ -45,7 +46,7 @@ module Padrino
45
46
  puts "=> Executing Rake #{ARGV.join(' ')} ..."
46
47
  ENV['PADRINO_LOG_LEVEL'] ||= "test"
47
48
  require File.expand_path(File.dirname(__FILE__) + '/rake')
48
- silence(:stdout) { require 'config/boot' }
49
+ silence(:stdout) { require File.expand_path('config/boot.rb') }
49
50
  PadrinoTasks.init
50
51
  end
51
52
 
@@ -57,7 +58,7 @@ module Padrino
57
58
  puts "=> Loading #{options.environment} console (Padrino v.#{Padrino.version})"
58
59
  require 'irb'
59
60
  require "irb/completion"
60
- require 'config/boot'
61
+ require File.expand_path('config/boot.rb')
61
62
  require File.expand_path(File.dirname(__FILE__) + '/console')
62
63
  IRB.start
63
64
  end
@@ -76,6 +77,7 @@ module Padrino
76
77
  raise SystemExit
77
78
  end
78
79
  ENV["PADRINO_ENV"] ||= options.environment.to_s
80
+ ENV["RACK_ENV"] = ENV["PADRINO_ENV"] # Also set this for middleware
79
81
  chdir(options.chdir)
80
82
  unless File.exist?('config/boot.rb')
81
83
  puts "=> Could not find boot file in: #{options.chdir}/config/boot.rb !!!"
@@ -16,6 +16,5 @@ end
16
16
  # Load apps
17
17
  Padrino.mounted_apps.each do |app|
18
18
  puts "=> Loading Application #{app.app_object}"
19
- Padrino.require_dependency(app.app_file)
20
19
  app.app_object.setup_application!
21
20
  end
@@ -23,7 +23,6 @@ Dir["lib/tasks/**/*.rake"].
23
23
  # setting up the required environment for Padrino
24
24
  task :environment do
25
25
  Padrino.mounted_apps.each do |app|
26
- Padrino.require_dependency(app.app_file)
27
26
  app.app_object.setup_application!
28
27
  end
29
28
  end
@@ -32,14 +31,14 @@ desc "Displays a listing of the named routes within a project"
32
31
  task :routes, :query, :needs => :environment do |t, args|
33
32
  Padrino.mounted_apps.each do |app|
34
33
  app_routes = app.app_object.router.routes
35
- app_routes.reject! { |r| r.named.blank? || r.conditions[:request_method] == 'HEAD' }
34
+ app_routes.reject! { |r| r.named.blank? || r.as_options[:conditions][:request_method].try(:first) == 'HEAD' }
36
35
  app_routes.reject! { |r| r.named.to_s !~ /#{args.query}/ } if args.query.present?
37
36
  next if app_routes.empty?
38
37
  shell.say "\nApplication: #{app.name}", :yellow
39
38
  app_routes.map! do |route|
40
39
  name_string = "(#{route.named.to_s.split("_").map { |piece| %Q[:#{piece}] }.join(", ")})"
41
- request_method = route.conditions[:request_method]
42
- [request_method, name_string, route.original_path]
40
+ request_method = route.as_options[:conditions][:request_method].try(:first)
41
+ [request_method, name_string, route.path]
43
42
  end
44
43
  app_routes.unshift(["REQUEST", "URL", "PATH"])
45
44
  max_col_1 = app_routes.max { |a, b| a[0].size <=> b[0].size }[0].size
@@ -0,0 +1,27 @@
1
+ require 'rbconfig'
2
+
3
+ module Padrino
4
+ ##
5
+ # This method return the correct location of padrino bin or
6
+ # exec it using Kernel#system with the given args
7
+ #
8
+ def self.bin(*args)
9
+ @_padrino_bin ||= [self.ruby_command, File.expand_path("../../../bin/padrino", __FILE__)]
10
+ args.empty? ? @_padrino_bin : system(args.unshift(@_padrino_bin).join(" "))
11
+ end
12
+
13
+ ##
14
+ # Return the path to the ruby interpreter taking into account multiple
15
+ # installations and windows extensions.
16
+ #
17
+ def self.ruby_command
18
+ @ruby_command ||= begin
19
+ ruby = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
20
+ ruby << Config::CONFIG['EXEEXT']
21
+
22
+ # escape string in case path to ruby executable contain spaces.
23
+ ruby.sub!(/.*\s.*/m, '"\&"')
24
+ ruby
25
+ end
26
+ end
27
+ end # Padrino
@@ -7,11 +7,9 @@ module Padrino
7
7
  return false if loaded?
8
8
  @_called_from = first_caller
9
9
  set_encoding
10
- set_load_paths(*load_paths) # we set the padrino load paths
11
- require_dependencies("#{root}/lib/**/*.rb", "#{root}/shared/lib/**/*.rb") # load root libs
12
- require_dependencies("#{root}/models/**/*.rb", "#{root}/shared/models/**/*.rb") # load root models
13
- require_dependencies("#{root}/config/database.rb", "#{root}/config/apps.rb") # load configuration
14
- Reloader::Stat.reload! # We need to fill our Stat::CACHE but we do that only for development
10
+ set_load_paths(*load_paths) # We set the padrino load paths
11
+ dependency_paths.each { |path| require_dependency(path) }
12
+ Reloader::Stat.run! # We need to fill our Stat::CACHE
15
13
  Padrino.logger # Initialize our logger
16
14
  Thread.current[:padrino_loaded] = true
17
15
  end
@@ -20,9 +18,7 @@ module Padrino
20
18
  # Method for reloading required applications and their files
21
19
  #
22
20
  def reload!
23
- return unless Reloader::Stat.changed?
24
21
  Reloader::Stat.reload! # detects the modified files
25
- Padrino.mounted_apps.each { |m| m.app_object.reload! } # finally we reload all files for each app
26
22
  end
27
23
 
28
24
  ##
@@ -74,7 +70,7 @@ module Padrino
74
70
  # Now we try to require our dependencies
75
71
  files.each do |file|
76
72
  begin
77
- require file
73
+ Reloader::Stat.safe_load(file)
78
74
  files.delete(file)
79
75
  rescue Exception => e
80
76
  errors << e
@@ -89,13 +85,35 @@ module Padrino
89
85
  end
90
86
  alias :require_dependency :require_dependencies
91
87
 
88
+ ##
89
+ # Returns default list of path globs to load as dependencies
90
+ #
91
+ def dependency_paths
92
+ # Load db adapter, libs, root models, app configuration
93
+ @dependency_paths ||= [
94
+ "#{root}/config/database.rb", "#{root}/lib/**/*.rb", "#{root}/shared/lib/**/*.rb",
95
+ "#{root}/models/**/*.rb", "#{root}/shared/models/**/*.rb", @custom_dependencies,
96
+ "#{root}/config/apps.rb"
97
+ ].flatten.compact
98
+ end
99
+
100
+ ##
101
+ # Appends custom dependency patterns to the be loaded for Padrino
102
+ # ==== Examples
103
+ # Padrino.custom_dependencies("#{Padrino.root}/foo/bar/*.rb")
104
+ #
105
+ def custom_dependencies(*globs)
106
+ @custom_dependencies ||= []
107
+ @custom_dependencies.concat(globs)
108
+ end
109
+
92
110
  ##
93
111
  # Attempts to load all dependency libs that we need.
94
112
  # If you use this method we can perform correctly a Padrino.reload!
95
113
  #
96
114
  def load_dependencies(*paths)
97
115
  paths.each do |path|
98
- Dir[path].each { |file| load(file) }
116
+ FileSet.glob(path) { |file| load(file) }
99
117
  end
100
118
  end
101
119
  alias :load_dependency :load_dependencies
@@ -0,0 +1,30 @@
1
+ cz:
2
+ date:
3
+ formats:
4
+ # Use the strftime parameters for formats.
5
+ # When no format has been given, it uses default.
6
+ # You can provide other formats here if you like!
7
+ default: "%d. %m. %Y"
8
+ short: "%d %b"
9
+ long: "%d. %B %Y"
10
+
11
+ day_names: [Neděle, Pondělí, Úterý, Středa, Čtvrtek, Pátek, Sobota]
12
+ abbr_day_names: [Ne, Po, Út, St, Čt, Pá, So]
13
+ month_names: [~, Leden, Únor, Březen, Duben, Květen, Červen, Červenec, Srpen, Září, Říjen, Listopad, Prosinec]
14
+ abbr_month_names: [~, Led, Úno, Bře, Dub, Kvě, Čvn, Čvc, Srp, Zář, Říj, Lis, Pro]
15
+ order: [:day, :month, :year]
16
+
17
+ time:
18
+ formats:
19
+ default: "%a %d. %B %Y %H:%M %z"
20
+ short: "%d. %m. %H:%M"
21
+ long: "%A %d. %B %Y %H:%M"
22
+ am: "dop"
23
+ pm: "odp"
24
+
25
+ # Used in array.to_sentence.
26
+ support:
27
+ array:
28
+ words_connector: ", "
29
+ two_words_connector: " a "
30
+ last_word_connector: " a "
@@ -1,18 +1,18 @@
1
1
  de:
2
2
  date:
3
3
  formats:
4
- # Benutze die strftime Parameter f¸r die Formatierung
5
- # Wenn keine Formate angegeben wurde, wird "default" benutzt.
6
- # Du kannst andere Formate hier bieten, wenn Du willst.
4
+ # Benutze die strftime Parameter für die Formatierung
5
+ # Wenn keine Formate angegeben werden, wird "default" benutzt.
6
+ # Du kannst auch weitere Formate hinzufügen, wenn Du möchtest.
7
7
  default: "&d.&m.%Y"
8
8
  short: "%b %d"
9
9
  long: "%B %d, %Y"
10
10
 
11
- day_names: [Sontag, Montag, Dienstag, Mittwoch, Donnerstag, Freitag, Samstag]
12
- abbr_day_names: [Son, Mon, Die, Mit, Don, Fre, Sam]
13
- month_names: [~, Januar, Februar, Marz, April, Mai, Juni, Juli, August, September, Oktober, November, Dezember]
14
- abbr_month_names: [~, Jan, Feb, Mar, Apr, Mai, Jun, Jul, Aug, Sep, Okt, Nov, Dez]
15
- order: [ day, :month, :year ]
11
+ day_names: [Sonntag, Montag, Dienstag, Mittwoch, Donnerstag, Freitag, Samstag]
12
+ abbr_day_names: [So, Mo, Di, Mi, Do, Fr, Sa]
13
+ month_names: [~, Januar, Februar, März, April, Mai, Juni, Juli, August, September, Oktober, November, Dezember]
14
+ abbr_month_names: [~, Jan, Feb, Mär, Apr, Mai, Jun, Jul, Aug, Sep, Okt, Nov, Dez]
15
+ order: [ :day, :month, :year ]
16
16
 
17
17
  time:
18
18
  formats:
@@ -0,0 +1,30 @@
1
+ es:
2
+ date:
3
+ formats:
4
+ # Use the strftime parameters for formats.
5
+ # When no format has been given, it uses default.
6
+ # You can provide other formats here if you like!
7
+ default: "%d-%m-%Y"
8
+ short: "%b %d"
9
+ long: "%B %d, %Y"
10
+
11
+ day_names: [Domingo, Lunes, Martes, Miércoles, Jueves, Viernes, Sábado]
12
+ abbr_day_names: [Dom, Lun, Mar, Mie, Jue, Vie, Sab]
13
+ month_names: [~, Enero, Febrero, Marzo, Abril, Mayo, Junio, Julio, Agosto, Septiembre, Octubre, Noviembre, Diciembre]
14
+ abbr_month_names: [~, Ene, Feb, Mar, Abr, May, Jun, Jul, Ago, Sep, Oct, Nov, Dic]
15
+ order: [ :day, :month, :year]
16
+
17
+ time:
18
+ formats:
19
+ default: "%a, %d %b %Y %H:%M:%S %z"
20
+ short: "%d %b %H:%M"
21
+ long: "%B %d, %Y %H:%M"
22
+ am: "am"
23
+ pm: "pm"
24
+
25
+ # Used in array.to_sentence.
26
+ support:
27
+ array:
28
+ words_connector: ", "
29
+ two_words_connector: " y "
30
+ last_word_connector: ", y "
@@ -0,0 +1,30 @@
1
+ tr:
2
+ date:
3
+ formats:
4
+ # Use the strftime parameters for formats.
5
+ # When no format has been given, it uses default.
6
+ # You can provide other formats here if you like!
7
+ default: "%d/%m/%Y"
8
+ short: "%d %b"
9
+ long: "%d %B %Y"
10
+
11
+ day_names: [Pazar, Pazartesi, Salı, Çarşamba, Perşembe, Cuma, Cumartesi]
12
+ abbr_day_names: [Paz, Pts, Sal, Çar, Per, Cum, Cts]
13
+ month_names: [~, Ocak, Şubat, Mart, Nisan, Mayıs, Haziran, Temmuz, Ağustos, Eylül, Ekim, Kasım, Aralık]
14
+ abbr_month_names: [~, Oca, Şub, Mar, Nis, May, Haz, Tem, Ağu, Eyl, Eki, Kas, Ara]
15
+ order: [ :day, :month, :year ]
16
+
17
+ time:
18
+ formats:
19
+ default: "%a, %b %b %Y %H:%M:%S %z"
20
+ short: "%b %d %H:%M"
21
+ long: "%d %B, %Y %H:%M"
22
+ am: "öö"
23
+ pm: "ös"
24
+
25
+ # Used in array.to_sentence.
26
+ support:
27
+ array:
28
+ words_connector: ", "
29
+ two_words_connector: " ve "
30
+ last_word_connector: " ve "
@@ -0,0 +1,30 @@
1
+ uk:
2
+ date:
3
+ formats:
4
+ # Use the strftime parameters for formats.
5
+ # When no format has been given, it uses default.
6
+ # You can provide other formats here if you like!
7
+ default: "%d.%m.%Y"
8
+ short: "%d %b"
9
+ long: "%e %B, %Y"
10
+
11
+ day_names: [Неділя, Понеділок, Вівторок, Середа, Четвер, Пятница, Субота]
12
+ abbr_day_names: [Нд, Пн, Вт, Ср, Чт, Пт, Сб]
13
+ month_names: [~, Січено, Лютий, Березень, Квітень, Травень, Червень, Липень, Серпень, Вересень, Жовтень, Листопад, Грудень]
14
+ abbr_month_names: [~, Січ, Лют, Бер, Кві, Тра, Чер, Лип, Сер, Вер, Жов, Лис, Гру]
15
+ order: [ :year, :month, :day ]
16
+
17
+ time:
18
+ formats:
19
+ default: "%a, %d %b %Y %H:%M:%S %z"
20
+ short: "%d %b %H:%M"
21
+ long: "%e %B, %Y %H:%M"
22
+ am: "д.п."
23
+ pm: "п.п"
24
+
25
+ # Used in array.to_sentence.
26
+ support:
27
+ array:
28
+ words_connector: ", "
29
+ two_words_connector: " і "
30
+ last_word_connector: ", і "
@@ -2,14 +2,13 @@ require 'pathname'
2
2
 
3
3
  module Padrino
4
4
  ##
5
- # High performant source reloader
5
+ # High performance source code reloader middleware
6
6
  #
7
7
  module Reloader
8
8
  ##
9
- # This class acts as Rack middleware.
10
- #
11
- # It is performing a check/reload cycle at the start of every request, but
12
- # also respects a cool down time, during which nothing will be done.
9
+ # This class acts as a Rack middleware to be added to the application stack. This middleware performs a
10
+ # check and reload for source files at the start of each request, but also respects a specified cool down time
11
+ # during which no further action will be taken.
13
12
  #
14
13
  class Rack
15
14
  def initialize(app, cooldown = 1)
@@ -34,63 +33,201 @@ module Padrino
34
33
  end
35
34
 
36
35
  ##
37
- # You can exclude some folders from reload its contents.
38
- # Defaults excluded directories of Padrino.root are: test, spec, features, tmp, config, lib, db and public
36
+ # Specified folders can be excluded from the code reload detection process.
37
+ # Default excluded directories at Padrino.root are: test, spec, features, tmp, config, lib, db and public
39
38
  #
40
39
  def self.exclude
41
40
  @_exclude ||= %w(test spec tmp features config lib public db).map { |path| Padrino.root(path) }
42
41
  end
43
42
 
44
43
  ##
45
- # What makes it especially suited for use in a any environment is that
46
- # any file will only be checked once and there will only be made one system
47
- # call stat(2).
44
+ # Specified constants can be excluded from the code unloading process.
45
+ # Default excluded constants are: Padrino, Sinatra
46
+ #
47
+ def self.exclude_constants
48
+ @_exclude_constants ||= %w(Padrino::Application Sinatra::Application Sinatra::Base)
49
+ end
50
+
51
+ ##
52
+ # Specified constants can be configured to be reloaded on every request.
53
+ # Default included constants are: [none]
48
54
  #
49
- # Please note that this will not reload files in the background, it does so
50
- # only when actively called.
55
+ def self.include_constants
56
+ @_include_constants ||= []
57
+ end
58
+
59
+ ##
60
+ # This reloader is suited for use in a many environments because each file
61
+ # will only be checked once and only one system call to stat(2) is made.
62
+ #
63
+ # Please note that this will not reload files in the background, and does so
64
+ # only when explicitly invoked.
51
65
  #
52
66
  module Stat
53
67
  class << self
54
- CACHE = {}
55
- MTIMES = {}
68
+ CACHE = {}
69
+ MTIMES = {}
70
+ FILES_LOADED = {}
71
+ LOADED_CLASSES = {}
56
72
 
73
+ ##
74
+ # Reload all files with changes detected.
75
+ #
57
76
  def reload!
58
77
  rotation do |file, mtime|
78
+ # Retrive the last modified time
79
+ new_file = MTIMES[file].nil?
59
80
  previous_mtime = MTIMES[file] ||= mtime
60
- safe_load(file, mtime) if mtime > previous_mtime
81
+ logger.debug "Detected a new file #{file}" if new_file
82
+ # We skip to next file if it is not new and not modified
83
+ next unless new_file || mtime > previous_mtime
84
+ # If the file is related to their app (i.e. a controller/mailer/helper)
85
+ if app = Padrino.mounted_apps.find { |a| file =~ /^#{File.dirname(a.app_file)}/ }
86
+ # We need to reload their own app
87
+ app.app_object.reload!
88
+ # App reloading will also perform safe_load of itself so we can go next
89
+ if File.identical?(app.app_file, file)
90
+ MTIMES[file] = mtime # This prevent a loop
91
+ next
92
+ end
93
+ end
94
+ # Now we can reload our file
95
+ safe_load(file, mtime)
61
96
  end
62
97
  end
63
98
 
99
+ ##
100
+ # Returns true if any file changes are detected and populates the MTIMES cache
101
+ #
64
102
  def changed?
65
103
  changed = false
66
104
  rotation do |file, mtime|
105
+ new_file = MTIMES[file].nil?
67
106
  previous_mtime = MTIMES[file] ||= mtime
68
- changed = true if mtime > MTIMES[file]
107
+ changed = true if new_file || mtime > previous_mtime
69
108
  end
70
109
  changed
71
110
  end
111
+ alias :run! :changed?
112
+
113
+ ##
114
+ # A safe Kernel::load which issues the necessary hooks depending on results
115
+ #
116
+ def safe_load(file, mtime=nil)
117
+ reload = mtime && mtime > MTIMES[file]
118
+
119
+ logger.debug "Reloading #{file}" if reload
120
+
121
+ # Removes all classes declared in the specified file
122
+ if klasses = LOADED_CLASSES.delete(file)
123
+ klasses.each { |klass| remove_constant(klass) }
124
+ end
125
+
126
+ # Keeps track of which constants were loaded and the files
127
+ # that have been added so that the constants can be removed
128
+ # and the files can be removed from $LOADED_FEAUTRES
129
+ if FILES_LOADED[file]
130
+ FILES_LOADED[file].each do |fl|
131
+ next if fl == file
132
+ $LOADED_FEATURES.delete(fl)
133
+ end
134
+ end
135
+
136
+ # Now reload the file ignoring any syntax errors
137
+ $LOADED_FEATURES.delete(file)
138
+
139
+ # Duplicate objects and loaded features in the file
140
+ klasses = ObjectSpace.classes.dup
141
+ files_loaded = $LOADED_FEATURES.dup
142
+
143
+ # Start to re-require old dependencies
144
+ #
145
+ # Why we need to reload the dependencies i.e. of a model?
146
+ #
147
+ # In some circumstances (i.e. with MongoMapper) reloading a model require:
148
+ #
149
+ # 1) Clean objectspace
150
+ # 2) Reload model dependencies
151
+ #
152
+ # We need to clean objectspace because for example we don't need to apply two times validations keys etc...
153
+ #
154
+ # We need to reload MongoMapper dependencies for re-initialize them.
155
+ #
156
+ # In other cases i.e. in a controller (specially with dependencies that uses autoload) reload stuff like sass
157
+ # is not really necessary... but how to distinguish when it is (necessary) since it is not?
158
+ #
159
+ if FILES_LOADED[file]
160
+ FILES_LOADED[file].each do |fl|
161
+ next if fl == file
162
+ # Swich off for a while warnings expecially "already initialized constant" stuff
163
+ begin
164
+ verbosity = $-v
165
+ $-v = nil
166
+ require(fl)
167
+ ensure
168
+ $-v = verbosity
169
+ end
170
+ end
171
+ end
172
+
173
+ # And finally reload the specified file
174
+ begin
175
+ require(file)
176
+ rescue SyntaxError => ex
177
+ logger.error "Cannot require #{file} because of syntax error: #{ex.message}"
178
+ ensure
179
+ MTIMES[file] = mtime if mtime
180
+ end
181
+
182
+ # Store the file details after successful loading
183
+ LOADED_CLASSES[file] = ObjectSpace.classes - klasses
184
+ FILES_LOADED[file] = $LOADED_FEATURES - files_loaded
185
+
186
+ nil
187
+ end
72
188
 
73
189
  ##
74
- # A safe Kernel::load, issuing the hooks depending on the results
190
+ # Removes the specified class and constant.
191
+ #
192
+ # Additionally this removes the specified class from the subclass list of every superclass that
193
+ # tracks it's subclasses in an array returned by _subclasses_list. Classes that wish to use this
194
+ # functionality are required to alias the reader for their list of subclasses
195
+ # to _subclasses_list. Plugins for ORMs and other libraries should keep this in mind.
75
196
  #
76
- def safe_load(file, mtime)
77
- logger.debug "Reloading #{file}"
78
- load(file)
79
- file
80
- rescue LoadError, SyntaxError => ex
81
- logger.error ex
82
- ensure
83
- MTIMES[file] = mtime
197
+ def remove_constant(const)
198
+ return if Padrino::Reloader.exclude_constants.any? { |base| (const.to_s =~ /^#{base}/ || const.superclass.to_s =~ /^#{base}/) } &&
199
+ !Padrino::Reloader.include_constants.any? { |base| (const.to_s =~ /^#{base}/ || const.superclass.to_s =~ /^#{base}/) }
200
+
201
+ superklass = const
202
+ until (superklass = superklass.superclass).nil?
203
+ if superklass.respond_to?(:_subclasses_list)
204
+ superklass.send(:_subclasses_list).delete(klass)
205
+ superklass.send(:_subclasses_list).delete(klass.to_s)
206
+ end
207
+ end
208
+
209
+ parts = const.to_s.split("::")
210
+ base = parts.size == 1 ? Object : Object.full_const_get(parts[0..-2].join("::"))
211
+ object = parts[-1].to_s
212
+ begin
213
+ base.send(:remove_const, object)
214
+ rescue NameError
215
+ end
216
+
217
+ nil
84
218
  end
85
219
 
86
220
  ##
87
- # Search Ruby files in your +Padrino.root+ and monitor them for changes.
221
+ # Searches Ruby files in your +Padrino.root+ and monitors them for any changes.
88
222
  #
89
223
  def rotation
90
- paths = Dir[Padrino.root("*")].reject { |path| Padrino::Reloader.exclude.include?(path) || !File.directory?(path) }
224
+ paths = Dir[Padrino.root("*")].unshift(Padrino.root).reject { |path| !File.directory?(path) }
225
+
91
226
  files = paths.map { |path| Dir["#{path}/**/*.rb"] }.flatten
92
227
 
93
228
  files.map{ |file|
229
+ next if Padrino::Reloader.exclude.any? { |base| file =~ /^#{base}/ }
230
+
94
231
  found, stat = figure_path(file, paths)
95
232
  next unless found && stat && mtime = stat.mtime
96
233
 
@@ -101,9 +238,8 @@ module Padrino
101
238
  end
102
239
 
103
240
  ##
104
- # Takes a relative or absolute +file+ name, a couple possible +paths+ that
105
- # the +file+ might reside in. Returns the full path and File::Stat for the
106
- # path.
241
+ # Takes a relative or absolute +file+ name and a couple possible +paths+ that
242
+ # the +file+ might reside in. Returns the full path and File::Stat for that path.
107
243
  #
108
244
  def figure_path(file, paths)
109
245
  found = CACHE[file]