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.
- data/README.rdoc +2 -2
- data/Rakefile +4 -56
- data/bin/padrino +9 -12
- data/lib/padrino-core.rb +7 -6
- data/lib/padrino-core/application.rb +44 -19
- data/lib/padrino-core/application/mounter.rb +19 -5
- data/lib/padrino-core/application/rendering.rb +123 -101
- data/lib/padrino-core/application/routing.rb +238 -159
- data/lib/padrino-core/cli/adapter.rb +2 -7
- data/lib/padrino-core/cli/base.rb +5 -3
- data/lib/padrino-core/cli/console.rb +0 -1
- data/lib/padrino-core/cli/rake.rb +3 -4
- data/lib/padrino-core/command.rb +27 -0
- data/lib/padrino-core/loader.rb +27 -9
- data/lib/padrino-core/locale/cz.yml +30 -0
- data/lib/padrino-core/locale/de.yml +8 -8
- data/lib/padrino-core/locale/es.yml +30 -0
- data/lib/padrino-core/locale/tr.yml +30 -0
- data/lib/padrino-core/locale/uk.yml +30 -0
- data/lib/padrino-core/reloader.rb +166 -30
- data/lib/padrino-core/router.rb +5 -3
- data/lib/padrino-core/server.rb +10 -4
- data/lib/padrino-core/support_lite.rb +86 -70
- data/lib/padrino-core/version.rb +3 -2
- data/padrino-core.gemspec +17 -120
- data/test/fixtures/apps/complex.rb +2 -2
- data/test/fixtures/apps/simple.rb +11 -2
- data/test/helper.rb +1 -1
- data/test/test_application.rb +25 -8
- data/test/test_mounter.rb +14 -4
- data/test/test_reloader_simple.rb +26 -1
- data/test/test_rendering.rb +53 -8
- data/test/test_router.rb +4 -4
- data/test/test_routing.rb +369 -12
- data/test/test_server.rb +2 -2
- metadata +77 -74
- data/test/fixtures/apps/.components +0 -6
- data/test/fixtures/apps/.gitignore +0 -7
@@ -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.
|
25
|
+
Padrino.run!(options.symbolize_keys)
|
31
26
|
exit
|
32
27
|
end
|
33
28
|
else
|
34
|
-
Padrino.run!(options.
|
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 !!!"
|
@@ -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?
|
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.
|
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
|
data/lib/padrino-core/loader.rb
CHANGED
@@ -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) #
|
11
|
-
|
12
|
-
|
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
|
-
|
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
|
-
|
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
|
5
|
-
# Wenn keine Formate angegeben
|
6
|
-
# Du kannst
|
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: [
|
12
|
-
abbr_day_names: [
|
13
|
-
month_names: [~, Januar, Februar,
|
14
|
-
abbr_month_names: [~, Jan, Feb,
|
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
|
5
|
+
# High performance source code reloader middleware
|
6
6
|
#
|
7
7
|
module Reloader
|
8
8
|
##
|
9
|
-
# This class acts as Rack middleware.
|
10
|
-
#
|
11
|
-
#
|
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
|
-
#
|
38
|
-
#
|
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
|
-
#
|
46
|
-
#
|
47
|
-
#
|
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
|
-
|
50
|
-
|
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
|
-
|
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 >
|
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
|
-
#
|
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
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
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
|
-
#
|
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|
|
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
|
105
|
-
# the +file+ might reside in. Returns the full path and File::Stat for
|
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]
|