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,5 +1,5 @@
1
1
  module Padrino
2
- class << self
2
+ module Loader
3
3
  ##
4
4
  # Hooks to be called before a load/reload.
5
5
  #
@@ -40,17 +40,6 @@ module Padrino
40
40
  @_after_load
41
41
  end
42
42
 
43
- ##
44
- # The used +$LOAD_PATHS+ from Padrino.
45
- #
46
- # @return [Array<String>]
47
- # The load paths used by Padrino.
48
- #
49
- def load_paths
50
- @_load_paths_was = %w(lib models shared).map { |path| Padrino.root(path) }
51
- @_load_paths ||= @_load_paths_was
52
- end
53
-
54
43
  ##
55
44
  # Requires necessary dependencies as well as application files from root
56
45
  # lib and models.
@@ -60,21 +49,18 @@ module Padrino
60
49
  #
61
50
  def load!
62
51
  return false if loaded?
63
- t = Time.now
64
-
52
+ began_at = Time.now
65
53
  @_called_from = first_caller
66
- Padrino.set_encoding
67
- Padrino.set_load_paths(*load_paths)
68
- Padrino::Logger.setup! # Initialize our logger
69
- Padrino.require_dependencies("#{root}/config/database.rb", :nodeps => true) # Be sure to don't remove constants from dbs.
70
- Padrino::Reloader.lock! # Now we can remove constant from here to down
71
- Padrino.before_load.each(&:call) # Run before hooks
72
- Padrino.dependency_paths.each { |path| Padrino.require_dependencies(path) }
73
- Padrino.after_load.each(&:call) # Run after hooks
74
- Padrino::Reloader.run!
54
+ set_encoding
55
+ set_load_paths(*load_paths)
56
+ Logger.setup!
57
+ require_dependencies("#{root}/config/database.rb")
58
+ Reloader.lock!
59
+ before_load.each(&:call)
60
+ require_dependencies(*dependency_paths)
61
+ after_load.each(&:call)
62
+ logger.devel "Loaded Padrino in #{Time.now - began_at} seconds"
75
63
  Thread.current[:padrino_loaded] = true
76
-
77
- Padrino.logger.devel "Loaded Padrino in #{Time.now - t} seconds"
78
64
  end
79
65
 
80
66
  ##
@@ -83,14 +69,14 @@ module Padrino
83
69
  # @return [NilClass]
84
70
  #
85
71
  def clear!
86
- Padrino.clear_middleware!
87
- Padrino.mounted_apps.clear
72
+ clear_middleware!
73
+ mounted_apps.clear
88
74
  @_load_paths = nil
89
75
  @_dependency_paths = nil
90
76
  @_global_configuration = nil
91
- Padrino.before_load.clear
92
- Padrino.after_load.clear
93
- Padrino::Reloader.clear!
77
+ before_load.clear
78
+ after_load.clear
79
+ Reloader.clear!
94
80
  Thread.current[:padrino_loaded] = nil
95
81
  end
96
82
 
@@ -98,10 +84,10 @@ module Padrino
98
84
  # Method for reloading required applications and their files.
99
85
  #
100
86
  def reload!
101
- return unless Padrino::Reloader.changed?
102
- Padrino.before_load.each(&:call) # Run before hooks
103
- Padrino::Reloader.reload! # detects the modified files
104
- Padrino.after_load.each(&:call) # Run after hooks
87
+ return unless Reloader.changed?
88
+ before_load.each(&:call)
89
+ Reloader.reload!
90
+ after_load.each(&:call)
105
91
  end
106
92
 
107
93
  ##
@@ -147,38 +133,55 @@ module Padrino
147
133
  # require_dependencies("#{Padrino.root}/lib/**/*.rb")
148
134
  #
149
135
  def require_dependencies(*paths)
150
- options = paths.extract_options!
151
-
152
- # Extract all files to load
153
- files = paths.flatten.map { |path| Dir[path] }.flatten.uniq.sort
136
+ options = paths.extract_options!.merge( :cyclic => true )
137
+ files = paths.flatten.map{|path| Dir[path].sort_by{|v| v.count('/') }}.flatten.uniq
154
138
 
155
139
  while files.present?
156
- errors, failed = [], []
140
+ error, fatal, loaded = nil, nil, nil
157
141
 
158
- size_at_start = files.size
159
-
160
- # Now we try to require our dependencies, we dup files
161
- # so we don't perform delete on the original array during
162
- # iteration, this prevent problems with Rubinus
163
142
  files.dup.each do |file|
164
143
  begin
165
- Padrino::Reloader.safe_load(file, options.dup)
144
+ Reloader.safe_load(file, options)
166
145
  files.delete(file)
146
+ loaded = true
167
147
  rescue NameError, LoadError => e
168
- Padrino.logger.devel "Problem while loading #{file}: #{e}"
169
- errors << e
170
- failed << file
148
+ logger.devel "Cyclic dependency reload for #{e.class}: #{e.message}"
149
+ error = e
171
150
  rescue Exception => e
172
- raise e
151
+ fatal = e
152
+ break
173
153
  end
174
154
  end
175
155
 
176
- # Stop processing if nothing loads or if everything has loaded
177
- raise errors.last if files.size == size_at_start && files.present?
178
- break if files.empty?
156
+ if fatal || !loaded
157
+ e = fatal || error
158
+ logger.error "#{e.class}: #{e.message}; #{e.backtrace.first}"
159
+ raise e
160
+ end
179
161
  end
180
162
  end
181
163
 
164
+ ##
165
+ # Concat to +$LOAD_PATH+ the given paths.
166
+ #
167
+ # @param [Array<String>] paths
168
+ # The paths to concat.
169
+ #
170
+ def set_load_paths(*paths)
171
+ load_paths.concat(paths).uniq!
172
+ $LOAD_PATH.concat(paths).uniq!
173
+ end
174
+
175
+ ##
176
+ # The used +$LOAD_PATHS+ from Padrino.
177
+ #
178
+ # @return [Array<String>]
179
+ # The load paths used by Padrino.
180
+ #
181
+ def load_paths
182
+ @_load_paths ||= load_paths_was.dup
183
+ end
184
+
182
185
  ##
183
186
  # Returns default list of path globs to load as dependencies.
184
187
  # Appends custom dependency patterns to the be loaded for Padrino.
@@ -190,35 +193,32 @@ module Padrino
190
193
  # Padrino.dependency_paths << "#{Padrino.root}/uploaders/*.rb"
191
194
  #
192
195
  def dependency_paths
193
- @_dependency_paths ||= (dependency_paths_was + Array(module_paths))
194
- end
195
-
196
- ##
197
- # Concat to +$LOAD_PATH+ the given paths.
198
- #
199
- # @param [Array<String>] paths
200
- # The paths to concat.
201
- #
202
- def set_load_paths(*paths)
203
- $:.concat(paths); load_paths.concat(paths)
204
- $:.uniq!; load_paths.uniq!
196
+ @_dependency_paths ||= dependency_paths_was + module_paths
205
197
  end
206
198
 
207
199
  private
208
200
 
209
201
  def module_paths
210
- Padrino.modules.map(&:dependency_paths).flatten!
202
+ modules.map(&:dependency_paths).flatten
203
+ end
204
+
205
+ def load_paths_was
206
+ @_load_paths_was ||= [
207
+ "#{root}/lib",
208
+ "#{root}/models",
209
+ "#{root}/shared",
210
+ ].freeze
211
211
  end
212
212
 
213
213
  def dependency_paths_was
214
- [
214
+ @_dependency_paths_was ||= [
215
215
  "#{root}/config/database.rb",
216
216
  "#{root}/lib/**/*.rb",
217
- "#{root}/shared/lib/**/*.rb",
218
217
  "#{root}/models/**/*.rb",
218
+ "#{root}/shared/lib/**/*.rb",
219
219
  "#{root}/shared/models/**/*.rb",
220
220
  "#{root}/config/apps.rb"
221
- ]
221
+ ].freeze
222
222
  end
223
223
  end
224
224
  end
@@ -52,10 +52,10 @@ module Padrino
52
52
  # :devel:: Development-related information that is unnecessary in debug mode
53
53
  #
54
54
  Levels = {
55
- :fatal => 7,
56
- :error => 6,
57
- :warn => 4,
58
- :info => 3,
55
+ :fatal => 4,
56
+ :error => 3,
57
+ :warn => 2,
58
+ :info => 1,
59
59
  :debug => 0,
60
60
  :devel => -1,
61
61
  } unless defined?(Levels)
@@ -194,13 +194,13 @@ module Padrino
194
194
  #
195
195
  def colorize(string, *colors)
196
196
  colors.each do |c|
197
- string = string.send(c)
197
+ string = string.colorize(c)
198
198
  end
199
199
  string
200
200
  end
201
201
 
202
202
  def stylized_level(level)
203
- style = ColoredLevels[level].map { |c| "\e[%dm" % String.colors[c] } * ''
203
+ style = ColoredLevels[level].map { |c| "\e[%dm" % String::Colorizer.colors[c] } * ''
204
204
  [style, super, "\e[0m"] * ''
205
205
  end
206
206
  end
@@ -8,10 +8,11 @@ module Padrino
8
8
  # Mounter.new("blog_app", :app_file => "/path/to/blog/app.rb").to("/blog")
9
9
  #
10
10
  class Mounter
11
+ DEFAULT_CASCADE = [404, 405]
11
12
  class MounterException < RuntimeError
12
13
  end
13
14
 
14
- attr_accessor :name, :uri_root, :app_file, :app_class, :app_root, :app_obj, :app_host
15
+ attr_accessor :name, :uri_root, :app_file, :app_class, :app_root, :app_obj, :app_host, :cascade
15
16
 
16
17
  ##
17
18
  # @param [String, Padrino::Application] name
@@ -31,8 +32,9 @@ module Padrino
31
32
  @app_file = options[:app_file] || locate_app_file
32
33
  @app_obj = options[:app_obj] || app_constant || locate_app_object
33
34
  ensure_app_file! || ensure_app_object!
34
- @app_root = options[:app_root] || File.dirname(@app_file)
35
+ @app_root = options[:app_root] || (@app_obj.respond_to?(:root) && @app_obj.root || File.dirname(@app_file))
35
36
  @uri_root = "/"
37
+ @cascade = options[:cascade] ? true == options[:cascade] ? DEFAULT_CASCADE.dup : Array(options[:cascade]) : []
36
38
  Padrino::Reloader.exclude_constants << @app_class
37
39
  end
38
40
 
@@ -82,11 +84,12 @@ module Padrino
82
84
  def map_onto(router)
83
85
  app_data, app_obj = self, @app_obj
84
86
  app_obj.set :uri_root, app_data.uri_root
85
- app_obj.set :app_name, app_data.name
87
+ app_obj.set :app_name, app_data.app_obj.app_name.to_s
86
88
  app_obj.set :app_file, app_data.app_file unless ::File.exist?(app_obj.app_file)
87
89
  app_obj.set :root, app_data.app_root unless app_data.app_root.blank?
88
90
  app_obj.set :public_folder, Padrino.root('public', app_data.uri_root) unless File.exists?(app_obj.public_folder)
89
91
  app_obj.set :static, File.exist?(app_obj.public_folder) if app_obj.nil?
92
+ app_obj.set :cascade, app_data.cascade
90
93
  app_obj.setup_application! # Initializes the app here with above settings.
91
94
  router.map(:to => app_obj, :path => app_data.uri_root, :host => app_data.app_host)
92
95
  end
@@ -106,7 +109,9 @@ module Padrino
106
109
  #
107
110
  def named_routes
108
111
  app_obj.routes.map { |route|
109
- name_array = "(#{route.name.to_s.split("_").map { |piece| %Q[:#{piece}] }.join(", ")})"
112
+ route_name = route.name.to_s
113
+ route_name.sub!(/^#{route.controller} /, "") if route.controller
114
+ name_array = "(#{route.controller ? %Q[:#{route.controller}] + ", " : ""}:#{route_name})"
110
115
  request_method = route.request_methods.first
111
116
  next if route.name.blank? || request_method == 'HEAD'
112
117
  original_path = route.original_path.is_a?(Regexp) ? route.original_path.inspect : route.original_path
@@ -0,0 +1,26 @@
1
+ module Padrino
2
+ module Reloader
3
+ ##
4
+ # This class acts as a Rack middleware to be added to the application stack.
5
+ # This middleware performs a check and reload for source files at the start
6
+ # of each request, but also respects a specified cool down time
7
+ # during which no further action will be taken.
8
+ #
9
+ class Rack
10
+ def initialize(app, cooldown=1)
11
+ @app = app
12
+ @cooldown = cooldown
13
+ @last = (Time.now - cooldown)
14
+ end
15
+
16
+ # Invoked in order to perform the reload as part of the request stack.
17
+ def call(env)
18
+ if @cooldown && Time.now > @last + @cooldown
19
+ Thread.list.size > 1 ? Thread.exclusive { Padrino.reload! } : Padrino.reload!
20
+ @last = Time.now
21
+ end
22
+ @app.call(env)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,55 @@
1
+ module Padrino
2
+ module Reloader
3
+ module Storage
4
+ extend self
5
+
6
+ def clear!
7
+ files.each_key do |file|
8
+ remove(file)
9
+ $LOADED_FEATURES.delete(file)
10
+ end
11
+ @files = {}
12
+ end
13
+
14
+ def remove(name)
15
+ file = files[name] || return
16
+ file[:constants].each{ |constant| Reloader.remove_constant(constant) }
17
+ file[:features].each{ |feature| $LOADED_FEATURES.delete(feature) }
18
+ files.delete(name)
19
+ end
20
+
21
+ def prepare(name)
22
+ file = remove(name)
23
+ @old_entries ||= {}
24
+ @old_entries[name] = {
25
+ :constants => ObjectSpace.classes,
26
+ :features => old_features = Set.new($LOADED_FEATURES.dup)
27
+ }
28
+ features = file && file[:features] || []
29
+ features.each{ |feature| Reloader.safe_load(feature, :force => true) }
30
+ $LOADED_FEATURES.delete(name) if old_features.include?(name)
31
+ end
32
+
33
+ def commit(name)
34
+ entry = {
35
+ :constants => ObjectSpace.new_classes(@old_entries[name][:constants]),
36
+ :features => Set.new($LOADED_FEATURES) - @old_entries[name][:features] - [name]
37
+ }
38
+ files[name] = entry
39
+ @old_entries.delete(name)
40
+ end
41
+
42
+ def rollback(name)
43
+ new_constants = ObjectSpace.new_classes(@old_entries[name][:constants])
44
+ new_constants.each{ |klass| Reloader.remove_constant(klass) }
45
+ @old_entries.delete(name)
46
+ end
47
+
48
+ private
49
+
50
+ def files
51
+ @files ||= {}
52
+ end
53
+ end
54
+ end
55
+ end