padrino-core 0.1.5 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.5
1
+ 0.2.0
data/lib/padrino-core.rb CHANGED
@@ -13,17 +13,17 @@ module Padrino
13
13
  # Padrino.root("config", "settings.yml")
14
14
  # # returns PADRINO_ROOT + "/config/setting.yml"
15
15
  def self.root(*args)
16
- File.join(PADRINO_ROOT, *args)
16
+ File.expand_path(File.join(PADRINO_ROOT, *args))
17
17
  end
18
18
 
19
19
  # Helper method that return PADRINO_ENV
20
20
  def self.env
21
- PADRINO_ENV.to_s
21
+ PADRINO_ENV.to_s.downcase.to_sym
22
22
  end
23
23
 
24
24
  # Returns the resulting rack builder mapping each 'mounted' application
25
25
  def self.application
26
- raise ApplicationLoadError.new("At least one application must be mounted onto Padrino!") if self.mounted_apps.none?
26
+ raise ApplicationLoadError.new("At least one app must be mounted!") unless self.mounted_apps && self.mounted_apps.any?
27
27
  builder = Rack::Builder.new
28
28
  self.mounted_apps.each { |app| app.map_onto(builder) }
29
29
  builder
@@ -4,18 +4,12 @@ module Padrino
4
4
  # These subclassed applications can be easily mounted into other Padrino applications as well.
5
5
  class Application < Sinatra::Application
6
6
 
7
- def logger
8
- @log_stream ||= self.class.log_to_file? ? Padrino.root("log/#{PADRINO_ENV.downcase}.log") : $stdout
9
- @logger ||= Logger.new(@log_stream)
10
- end
11
-
12
7
  class << self
13
8
  def inherited(subclass)
14
9
  CALLERS_TO_IGNORE.concat(PADRINO_IGNORE_CALLERS)
15
10
  subclass.default_configuration!
16
11
  super # Loading the subclass
17
12
  subclass.register Padrino::Routing if defined?(Padrino::Routing)
18
- subclass.check_single_app
19
13
  end
20
14
 
21
15
  # Hooks into when a new instance of the application is created
@@ -30,19 +24,21 @@ module Padrino
30
24
  # Makes the routes defined in the block and in the Modules given
31
25
  # in `extensions` available to the application
32
26
  def controllers(*extensions, &block)
33
- self.reset_routes! if reload?
34
27
  instance_eval(&block) if block_given?
35
28
  include(*extensions) if extensions.any?
36
29
  end
37
30
 
38
- # Return true if the bootloader => Padrino.load! it's instatiated in the same
39
- # palace of the app.
40
- # Notice that <tt>signle_apps</tt> was not reloadable!
41
- def single_app?
42
- @_single_app
31
+ # Reloads the application files from all defined load paths
32
+ def reload!
33
+ reset_routes! # remove all existing user-defined application routes
34
+ Padrino.load_dependency(self.app_file) # reload the app file
35
+ load_paths.each { |path| Padrino.load_dependencies(File.join(self.root, path)) }
43
36
  end
44
37
 
45
- protected
38
+ # Resets application routes to only routes not defined by the user
39
+ def reset_routes!
40
+ @routes = Padrino::Application.dupe_routes
41
+ end
46
42
 
47
43
  # Setup the application by registering initializers, load paths and logger
48
44
  # Invoked automatically when an application is first instantiated
@@ -52,33 +48,28 @@ module Padrino
52
48
  self.calculate_paths
53
49
  self.register_initializers
54
50
  self.require_load_paths
55
- self.setup_logger
51
+ self.disable :logging # We need do that as default because Sinatra use commonlogger.
56
52
  @_configured = true
57
53
  end
58
54
 
55
+ protected
56
+
59
57
  # Defines default settings for Padrino application
60
58
  def default_configuration!
61
59
  # Overwriting Sinatra defaults
62
60
  set :app_file, caller_files.first || $0 # Assume app file is first caller
63
61
  set :environment, PADRINO_ENV.to_sym
64
62
  set :raise_errors, true if development?
65
- set :logging, !test?
63
+ set :logging, false#!test?
66
64
  set :sessions, true
67
- set :log_to_file, !development?
68
- set :reload, development?
69
65
  # Padrino specific
66
+ set :reload, development?
70
67
  set :app_name, self.to_s.underscore.to_sym
71
68
  set :default_builder, 'StandardFormBuilder'
72
- enable :flash
69
+ set :flash, defined?(Rack::Flash)
73
70
  # Plugin specific
74
- enable :padrino_mailer
75
- enable :padrino_helpers
76
- end
77
-
78
- def check_single_app
79
- @_single_app = File.identical?(self.app_file, Padrino.called_from.to_s)
80
- single_message = "=> Instantiated #{File.basename(self.app_file)} in single app mode, reload is not available"
81
- puts single_message if @_single_app && logging?
71
+ set :padrino_mailer, defined?(Padrino::Mailer)
72
+ set :padrino_helpers, defined?(Padrino::Helpers)
82
73
  end
83
74
 
84
75
  # Calculates any required paths after app_file and root have been properly configured
@@ -91,32 +82,23 @@ module Padrino
91
82
 
92
83
  # Requires the middleware and initializer modules to configure components
93
84
  def register_initializers
94
- use Rack::Session::Cookie
95
- use Rack::Flash if defined?(Rack::Flash) && flash?
96
- use Padrino::Reloader unless single_app?
97
- register DatabaseSetup if defined?(DatabaseSetup)
85
+ use Padrino::RackLogger
86
+ use Padrino::Reloader if reload?
87
+ use Rack::Flash if flash?
88
+ register DatabaseSetup if defined?(DatabaseSetup)
98
89
  @initializer_path ||= Padrino.root + '/config/initializers/*.rb'
99
90
  Dir[@initializer_path].each { |file| register_initializer(file) }
100
91
  end
101
92
 
102
93
  # Registers all desired padrino extension helpers/routing
103
94
  def register_framework_extensions
104
- register Padrino::Mailer if padrino_mailer? && defined?(Padrino::Mailer)
105
- register Padrino::Helpers if padrino_helpers? && defined?(Padrino::Helpers)
95
+ register Padrino::Mailer if padrino_mailer?
96
+ register Padrino::Helpers if padrino_helpers?
106
97
  end
107
98
 
108
- # Require all files within the application's load paths
99
+ # Requires all files within the application load paths
109
100
  def require_load_paths
110
- load_paths.each { |path| Padrino.load_dependencies(File.join(self.root, path)) }
111
- end
112
-
113
- # Creates the log directory and redirects output to file if needed
114
- def setup_logger
115
- return unless logging? && log_to_file?
116
- FileUtils.mkdir_p("#{Padrino.root}/log") unless File.exists?("#{Padrino.root}/log")
117
- log = File.new("#{Padrino.root}/log/#{PADRINO_ENV.downcase}.log", "a+")
118
- $stdout.reopen(log)
119
- $stderr.reopen(log)
101
+ load_paths.each { |path| Padrino.require_dependencies(File.join(self.root, path)) }
120
102
  end
121
103
 
122
104
  # Returns the load_paths for the application (relative to the application root)
@@ -132,23 +114,15 @@ module Padrino
132
114
  @view_paths.find { |path| Dir[File.join(path, '/**/*')].any? }
133
115
  end
134
116
 
135
- # Resets application routes for use in reloading the application
136
- # This performs a basic routes reload (compatible with sinatra edge)
137
- def reset_routes!
138
- return if single_app? # Don't reset routes for single app
139
- @routes = Padrino::Application.dupe_routes
140
- load(self.app_file)
141
- end
142
-
143
117
  # Registers an initializer with the application
144
118
  # register_initializer('/path/to/initializer')
145
119
  def register_initializer(file_path)
146
- Padrino.load_dependencies(file_path)
120
+ Padrino.require_dependencies(file_path)
147
121
  file_class = File.basename(file_path, '.rb').camelize
148
122
  register "#{file_class}Initializer".constantize
149
123
  rescue NameError => e
150
- puts "The module '#{file_class}Initializer' (#{file_path}) didn't loaded properly!" if logging?
151
- puts " Initializer error was '#{e.message}'" if logging?
124
+ logger.error "The module '#{file_class}Initializer' (#{file_path}) didn't loaded properly!"
125
+ logger.error " Initializer error was '#{e.message}'"
152
126
  end
153
127
  end
154
128
  end
@@ -1,17 +1,23 @@
1
1
  module Padrino
2
2
  PADRINO_IGNORE_CALLERS = [
3
- %r{/padrino-.*$}, # all padrino code
4
- %r{/sinatra}, # all sinatra code
5
- %r{lib/tilt.*\.rb$}, # all tilt code
6
- %r{\(.*\)}, # generated code
7
- %r{shoulda/context\.rb$}, # shoulda hacks
8
- %r{mocha/integration}, # mocha hacks
9
- %r{test/unit}, # test unit hacks
10
- %r{rake_test_loader\.rb}, # rake hacks
11
- %r{custom_require\.rb$}, # rubygems require hacks
12
- %r{active_support}, # active_support require hacks
13
- %r{/thor}, # thor require hacks
14
- ]
3
+ %r{lib/padrino-.*$}, # all padrino code
4
+ %r{/padrino-.*/(lib|bin)}, # all padrino code
5
+ %r{/bin/padrino$}, # all padrino code
6
+ %r{/sinatra}, # all sinatra code
7
+ %r{lib/tilt.*\.rb$}, # all tilt code
8
+ %r{lib/rack.*\.rb$}, # all rack code
9
+ %r{lib/mongrel.*\.rb$}, # all mongrel code
10
+ %r{lib/shotgun.*\.rb$}, # all shotgun lib
11
+ %r{bin/shotgun$}, # shotgun binary
12
+ %r{\(.*\)}, # generated code
13
+ %r{shoulda/context\.rb$}, # shoulda hacks
14
+ %r{mocha/integration}, # mocha hacks
15
+ %r{test/unit}, # test unit hacks
16
+ %r{rake_test_loader\.rb}, # rake hacks
17
+ %r{custom_require\.rb$}, # rubygems require hacks
18
+ %r{active_support}, # active_support require hacks
19
+ %r{/thor}, # thor require hacks
20
+ ] unless defined?(PADRINO_IGNORE_CALLERS)
15
21
 
16
22
  # add rubinius (and hopefully other VM impls) ignore patterns ...
17
23
  PADRINO_IGNORE_CALLERS.concat(RUBY_IGNORE_CALLERS) if defined?(RUBY_IGNORE_CALLERS)
@@ -2,13 +2,20 @@ module Padrino
2
2
  class << self
3
3
  # Requires necessary dependencies as well as application files from root lib and models
4
4
  def load!
5
- return if loaded?
5
+ return false if loaded?
6
6
  @_called_from = first_caller
7
7
  load_required_gems # load bundler gems
8
- load_dependencies("#{root}/config/apps.rb", "#{root}/config/database.rb") # load configuration
9
- load_dependencies("#{root}/lib/**/*.rb", "#{root}/models/*.rb") # load root app dependencies
10
- reload! # We need to fill our Stat::CACHE but we do that only for development
11
- @_loaded = true
8
+ require_dependencies("#{root}/config/apps.rb", "#{root}/config/database.rb") # load configuration
9
+ require_dependencies("#{root}/lib/**/*.rb", "#{root}/models/*.rb") # load root app dependencies
10
+ Stat.reload! # We need to fill our Stat::CACHE but we do that only for development
11
+ Thread.current[:padrino_loaded] = true
12
+ end
13
+
14
+ # Method for reloading required applications and their files
15
+ def reload!
16
+ return unless Stat.changed?
17
+ Stat.reload! # detects the modified files
18
+ Padrino.mounted_apps.each { |m| m.app_object.reload! } # finally we reload all files for each app
12
19
  end
13
20
 
14
21
  # This adds the ablity to instantiate Padrino.load! after Padrino::Application definition.
@@ -18,7 +25,7 @@ module Padrino
18
25
 
19
26
  # Return true if Padrino was loaded with Padrino.load!
20
27
  def loaded?
21
- @_loaded
28
+ Thread.current[:padrino_loaded]
22
29
  end
23
30
 
24
31
  # Attempts to require all dependencies with bundler; if this fails, uses system wide gems
@@ -27,23 +34,33 @@ module Padrino
27
34
  require_vendored_gems
28
35
  end
29
36
 
30
- # Attempts to load/require all dependency libs that we need.
37
+ # Attempts to require all dependency libs that we need.
31
38
  # If you use this method we can perform correctly a Padrino.reload!
32
39
  #
33
- # @param paths [Array] Path where is necessary require or load a dependency
34
- # @example For load all our app libs we need to do:
35
- # load_dependencies("#{Padrino.root}/lib/**/*.rb")
36
- def load_dependencies(*paths)
40
+ # ==== Parameters
41
+ # paths:: Path where is necessary require a dependency
42
+ #
43
+ # Example:
44
+ # # For require all our app libs we need to do:
45
+ # require_dependencies("#{Padrino.root}/lib/**/*.rb")
46
+ def require_dependencies(*paths)
37
47
  paths.each do |path|
38
48
  Dir[path].each { |file| require(file) }
39
49
  end
40
50
  end
41
- alias_method :load_dependency, :load_dependencies
51
+ alias :require_dependency :require_dependencies
42
52
 
43
- # Method for reload required classes
44
- def reload!
45
- Stat::reload!
53
+ # Attempts to load all dependency libs that we need.
54
+ # If you use this method we can perform correctly a Padrino.reload!
55
+ #
56
+ # ==== Parameters
57
+ # paths:: Path where is necessary to load a dependency
58
+ def load_dependencies(*paths)
59
+ paths.each do |path|
60
+ Dir[path].each { |file| load(file) }
61
+ end
46
62
  end
63
+ alias :load_dependency :load_dependencies
47
64
 
48
65
  protected
49
66
 
@@ -57,17 +74,22 @@ module Padrino
57
74
  say " ... Not Found"
58
75
  end
59
76
 
60
- # Loads bundled gems if they exist
77
+ # Require bundled gems if they exist
61
78
  def require_vendored_gems
62
- load_dependencies(root('/../vendor', 'gems', PADRINO_ENV))
63
- say " (Loading bundled gems)\n"
79
+ require_dependencies(root('/../vendor', 'gems', PADRINO_ENV))
80
+ say! " (Loading bundled gems)"
64
81
  rescue LoadError => e
65
- say " (Loading system gems)\n"
82
+ say! " (Loading system gems)"
66
83
  end
67
84
 
68
85
  # Prints out a message to the stdout if not in test environment
69
86
  def say(text)
70
- print text if Padrino.env != 'test'
87
+ print text if Padrino.env != :test
88
+ end
89
+
90
+ # Puts out a message to the stdout if not in test environment
91
+ def say!(text)
92
+ puts text if Padrino.env != :test
71
93
  end
72
94
  end
73
- end
95
+ end
@@ -0,0 +1,204 @@
1
+ module Padrino
2
+
3
+ # Setup a new logger
4
+ def self.setup_logger!
5
+ case Padrino.env
6
+ when :production
7
+ FileUtils.mkdir_p("#{Padrino.root}/log") unless File.exists?("#{Padrino.root}/log")
8
+ log = File.new("#{Padrino.root}/log/#{PADRINO_ENV.downcase}.log", "a+")
9
+ Thread.current[:padrino_logger] = Padrino::Logger.new(:log_level => :error, :stream => log)
10
+ when :development
11
+ Thread.current[:padrino_logger] = Padrino::Logger.new
12
+ when :test
13
+ Thread.current[:padrino_logger] = Padrino::Logger.new(:stream => StringIO.new)
14
+ end
15
+ Thread.current[:padrino_logger]
16
+ end
17
+
18
+ class Logger
19
+
20
+ attr_accessor :level
21
+ attr_accessor :auto_flush
22
+ attr_reader :buffer
23
+ attr_reader :log
24
+ attr_reader :init_args
25
+
26
+ # ==== Notes
27
+ # Ruby (standard) logger levels:
28
+ #
29
+ # :fatal:: An unhandleable error that results in a program crash
30
+ # :error:: A handleable error condition
31
+ # :warn:: A warning
32
+ # :info:: generic (useful) information about system operation
33
+ # :debug:: low-level information for developers
34
+ Levels = {
35
+ :fatal => 7,
36
+ :error => 6,
37
+ :warn => 4,
38
+ :info => 3,
39
+ :debug => 0
40
+ } unless const_defined?(:Levels)
41
+
42
+ @@mutex = {}
43
+
44
+ public
45
+
46
+ # To initialize the logger you create a new object, proxies to set_log.
47
+ #
48
+ # ==== Options can be:
49
+ #
50
+ # :stream:: Either an IO object or a name of a logfile. Defaults to $stdout
51
+ # :log_level::
52
+ # The log level from, e.g. :fatal or :info. Defaults to :debug in the
53
+ # production environment and :debug otherwise.
54
+ # :auto_flush::
55
+ # Whether the log should automatically flush after new messages are
56
+ # added. Defaults to true.
57
+ # :format_datetime:: Format of datetime. Defaults to: "%d/%b/%Y %H:%M:%S"
58
+ # :format_message:: Format of message. Defaults to: ""%s - - [%s] \"%s\"""
59
+ def initialize(options={})
60
+ @buffer = []
61
+ @auto_flush = options.has_key?(:auto_flush) ? options[:auto_flush] : true
62
+ @level = options[:log_level] ? Levels[options[:log_level]] : Levels[:debug]
63
+ @log = options[:stream] || $stdout
64
+ @log.sync = true
65
+ @mutex = @@mutex[@log] ||= Mutex.new
66
+ @format_datetime = options[:format_datetime] || "%d/%b/%Y %H:%M:%S"
67
+ @format_message = options[:format_message] || "%s - [%s] \"%s\""
68
+ end
69
+
70
+ # Flush the entire buffer to the log object.
71
+ def flush
72
+ return unless @buffer.size > 0
73
+ @mutex.synchronize do
74
+ @log.write(@buffer.slice!(0..-1).join(''))
75
+ end
76
+ end
77
+
78
+ # Close and remove the current log object.
79
+ def close
80
+ flush
81
+ @log.close if @log.respond_to?(:close) && !@log.tty?
82
+ @log = nil
83
+ end
84
+
85
+ # Appends a message to the log. The methods yield to an optional block and
86
+ # the output of this block will be appended to the message.
87
+ #
88
+ # ==== Parameters
89
+ # message:: The message to be logged. Defaults to nil.
90
+ #
91
+ # ==== Returns
92
+ # message:: The resulting message added to the log file.
93
+ def push(message = nil, level = nil)
94
+ self << @format_message % [level.to_s.upcase, Time.now.strftime(@format_datetime), message.to_s]
95
+ end
96
+
97
+ def <<(message = nil)
98
+ message << "\n" unless message[-1] == ?\n
99
+ @buffer << message
100
+ flush if @auto_flush
101
+ message
102
+ end
103
+
104
+ # Generate the logging methods for Padrino.logger for each log level.
105
+ Levels.each_pair do |name, number|
106
+ class_eval <<-LEVELMETHODS, __FILE__, __LINE__
107
+
108
+ # Appends a message to the log if the log level is at least as high as
109
+ # the log level of the logger.
110
+ #
111
+ # ==== Parameters
112
+ # message:: The message to be logged. Defaults to nil.
113
+ #
114
+ # ==== Returns
115
+ # self:: The logger object for chaining.
116
+ def #{name}(message = nil)
117
+ if #{number} >= level
118
+ message = block_given? ? yield : message
119
+ self.push(message, :#{name}) if #{number} >= level
120
+ end
121
+ self
122
+ end
123
+
124
+ # Appends a message to the log if the log level is at least as high as
125
+ # the log level of the logger. The bang! version of the method also auto
126
+ # flushes the log buffer to disk.
127
+ #
128
+ # ==== Parameters
129
+ # message:: The message to be logged. Defaults to nil.
130
+ #
131
+ # ==== Returns
132
+ # self:: The logger object for chaining.
133
+ def #{name}!(message = nil)
134
+ if #{number} >= level
135
+ message = block_given? ? yield : message
136
+ self.push(message, :#{name}) if #{number} >= level
137
+ flush if #{number} >= level
138
+ end
139
+ self
140
+ end
141
+
142
+ # ==== Returns
143
+ # Boolean:: True if this level will be logged by this logger.
144
+ def #{name}?
145
+ #{number} >= level
146
+ end
147
+ LEVELMETHODS
148
+ end
149
+
150
+ end
151
+
152
+ # RackLogger forwards every request to an +app+ given, and
153
+ # logs a line in the Apache common log format to the +logger+, or
154
+ # rack.errors by default.
155
+ class RackLogger
156
+ # Common Log Format: http://httpd.apache.org/docs/1.3/logs.html#common
157
+ # "lilith.local - - GET / HTTP/1.1 500 -"
158
+ # %{%s - %s %s %s%s %s - %d %s %0.4f}
159
+ FORMAT = %{%s - %s %s %s%s %s - %d %s %0.4f}
160
+
161
+ def initialize(app)
162
+ @app = app
163
+ end
164
+
165
+ def call(env)
166
+ began_at = Time.now
167
+ status, header, body = @app.call(env)
168
+ log(env, status, header, began_at)
169
+ [status, header, body]
170
+ end
171
+
172
+ private
173
+
174
+ def log(env, status, header, began_at)
175
+ now = Time.now
176
+ length = extract_content_length(header)
177
+
178
+ logger.debug FORMAT % [
179
+ env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
180
+ env["REMOTE_USER"] || "-",
181
+ env["REQUEST_METHOD"],
182
+ env["PATH_INFO"],
183
+ env["QUERY_STRING"].empty? ? "" : "?"+env["QUERY_STRING"],
184
+ env["HTTP_VERSION"],
185
+ status.to_s[0..3],
186
+ length,
187
+ now - began_at ]
188
+ end
189
+
190
+ def extract_content_length(headers)
191
+ headers.each do |key, value|
192
+ if key.downcase == 'content-length'
193
+ return value.to_s == '0' ? '-' : value
194
+ end
195
+ end
196
+ '-'
197
+ end
198
+ end
199
+ end
200
+
201
+ # Define a logger available every where in our app
202
+ def logger
203
+ Thread.current[:padrino_logger] ||= Padrino::setup_logger!
204
+ end
@@ -4,19 +4,21 @@ module Padrino
4
4
  # @example Mounter.new("blog_app", :app_class => "Blog").to("/blog")
5
5
  # @example Mounter.new("blog_app", :app_file => "/path/to/blog/app.rb").to("/blog")
6
6
  class Mounter
7
- attr_accessor :name, :uri_root, :app_file, :app_class, :app_root
7
+ attr_accessor :name, :uri_root, :app_file, :app_class, :app_root, :app_obj
8
+
8
9
  def initialize(name, options={})
9
10
  @name = name.downcase
10
11
  @app_class = options[:app_class] || name.classify
11
12
  @app_file = options[:app_file] || locate_app_file
12
13
  @app_root = options[:app_root]
14
+ @app_obj = self.app_object
13
15
  end
14
16
 
15
17
  # Registers the mounted application onto Padrino
16
18
  # @example Mounter.new("blog_app").to("/blog")
17
19
  def to(mount_url)
18
- @uri_root = mount_url
19
- Padrino.mounted_apps << self
20
+ @uri_root = mount_url
21
+ Padrino.insert_mounted_app(self)
20
22
  self
21
23
  end
22
24
 
@@ -24,22 +26,32 @@ module Padrino
24
26
  # For use in constructing a Rack application
25
27
  # @example @app.map_onto(@builder)
26
28
  def map_onto(builder)
27
- self.app_class.constantize rescue require(self.app_file)
28
- app_data, app_class = self, self.app_class.constantize
29
+ app_data, app_obj = self, @app_obj
29
30
  builder.map self.uri_root do
30
- app_class.set :uri_root, app_data.uri_root
31
- app_class.set :app_file, app_data.app_file
32
- app_class.set :app_name, app_data.name
33
- app_class.set :root, app_data.app_root if app_data.app_root
34
- run app_class
31
+ app_obj.set :uri_root, app_data.uri_root
32
+ app_obj.set :app_name, app_data.name
33
+ app_obj.set :app_file, app_data.app_file unless File.exist?(app_obj.app_file)
34
+ app_obj.set :root, app_data.app_root unless app_data.app_root.blank?
35
+ run app_obj
35
36
  end
36
37
  end
37
38
 
39
+ # Return the class for the app
40
+ def app_object
41
+ app_class.constantize rescue Padrino.require_dependency(app_file)
42
+ app_class.constantize
43
+ end
44
+
38
45
  # Returns the determined location of the mounted application main file
39
46
  def locate_app_file
40
47
  callers_are_identical = File.identical?(Padrino.first_caller.to_s, Padrino.called_from.to_s)
41
48
  callers_are_identical ? Padrino.first_caller : Padrino.mounted_root(name, "app.rb")
42
49
  end
50
+
51
+ # Makes two Mounters equal if they have the same name and uri_root
52
+ def ==(other)
53
+ other.is_a?(Mounter) && self.name == other.name && self.uri_root == other.uri_root
54
+ end
43
55
  end
44
56
 
45
57
  class << self
@@ -55,6 +67,12 @@ module Padrino
55
67
  @mounted_apps ||= []
56
68
  end
57
69
 
70
+ # Inserts a Mounter object into the mounted applications (avoids duplicates)
71
+ def insert_mounted_app(mounter)
72
+ return false if Padrino.mounted_apps.include?(mounter)
73
+ Padrino.mounted_apps << mounter
74
+ end
75
+
58
76
  # Mounts the core application onto Padrino project with given app settings (file, class, root)
59
77
  # @example Padrino.mount_core("Blog")
60
78
  # @example Padrino.mount_core(:app_file => "/path/to/file", :app_class => "Blog")
@@ -7,7 +7,7 @@ module Padrino
7
7
  # also respects a cool down time, during which nothing will be done.
8
8
  class Reloader
9
9
 
10
- def initialize(app, cooldown = 1, backend = Stat)
10
+ def initialize(app, cooldown = 1)
11
11
  @app = app
12
12
  @cooldown = cooldown
13
13
  @last = (Time.now - cooldown)
@@ -17,9 +17,18 @@ module Padrino
17
17
  end
18
18
  end
19
19
 
20
+ def changed?
21
+ changed = false
22
+ rotation do |file, mtime|
23
+ previous_mtime = MTIMES[file] ||= mtime
24
+ changed = true if mtime > MTIMES[file]
25
+ end
26
+ changed
27
+ end
28
+
20
29
  # A safe Kernel::load, issuing the hooks depending on the results
21
30
  def safe_load(file, mtime)
22
- puts "=> Reloading #{file}"
31
+ Padrino.say! "=> Reloading #{file}"
23
32
  load(file)
24
33
  file
25
34
  rescue LoadError, SyntaxError => ex
@@ -32,9 +41,8 @@ module Padrino
32
41
  files = [$0, *$LOADED_FEATURES].uniq
33
42
  paths = ['./', *$LOAD_PATH].uniq
34
43
 
35
- files.map{|file|
36
- next if file =~ /\.(so|bundle)$/ # cannot reload compiled files
37
-
44
+ files.map{ |file|
45
+ next if file =~ /\.(so|bundle)$/ # cannot reload compiled files
38
46
  found, stat = figure_path(file, paths)
39
47
  next unless found && stat && mtime = stat.mtime
40
48
 
@@ -11,6 +11,7 @@ Required for Padrino to run:
11
11
  * Array#extract_options!
12
12
  * Object#blank?
13
13
  * Object#present?
14
+ * Hash#slice, Hash#slice!
14
15
  * Hash#symbolize_keys
15
16
  * Hash#reverse_merge, Hash#reverse_merge!
16
17
  * SupportLite::OrderedHash
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Class#cattr_accessor
4
4
  require 'active_support/core_ext/class/attribute_accessors' unless Class.method_defined?(:cattr_accessor)
5
- ## Hash#symbolize_keys, Hash#reverse_merge, Hash#reverse_merge!, Hash#extract_options!
5
+ ## Hash#symbolize_keys, Hash#reverse_merge, Hash#reverse_merge!, Hash#extract_options!, Hash#slice!
6
6
  require 'active_support/core_ext/hash' unless Hash.method_defined?(:reverse_merge)
7
7
  ## String#inflectors
8
8
  require 'active_support/inflector' unless String.method_defined?(:constantize)
@@ -24,6 +24,18 @@ unless Hash.method_defined?(:symbolize_keys)
24
24
  end
25
25
  end
26
26
 
27
+ ## Hash#slice, Hash#slice!
28
+ unless Hash.method_defined?(:slice)
29
+ require 'extlib/hash'
30
+ class Hash
31
+ def slice(*keys)
32
+ keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key)
33
+ hash = self.class.new
34
+ keys.each { |k| hash[k] = self[k] if has_key?(k) }
35
+ hash
36
+ end
37
+ end
38
+ end
27
39
 
28
40
  ## Hash#reverse_merge, Hash#reverse_merge!
29
41
  unless Hash.method_defined?(:present?)
@@ -16,6 +16,7 @@ module Padrino
16
16
  method_option :port, :type => :numeric, :aliases => "-p", :required => true, :default => 3000
17
17
  method_option :boot, :type => :string, :aliases => "-b", :required => true, :default => "config/boot.rb"
18
18
  method_option :daemonize, :type => :boolean, :aliases => "-d"
19
+
19
20
  def start
20
21
  require File.dirname(__FILE__) + "/tasks/adapter"
21
22
  chdir(options.chdir)
@@ -47,11 +48,11 @@ module Padrino
47
48
  end
48
49
  ENV["PADRINO_ENV"] ||= environment
49
50
  puts "=> Loading #{environment} console (Padrino v.#{Padrino.version})"
50
- irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
51
- libs = " -r irb/completion"
52
- libs << " -r #{boot}"
53
- libs << " -r #{File.dirname(__FILE__)}/tasks/console"
54
- exec "#{irb} #{libs} --simple-prompt"
51
+ require 'irb'
52
+ require "irb/completion"
53
+ require boot
54
+ require File.dirname(__FILE__) + '/tasks/console'
55
+ IRB.start
55
56
  end
56
57
  end
57
58
  end
@@ -1,7 +1,6 @@
1
1
  # Reloads classes
2
2
  def reload!
3
3
  Padrino.reload!
4
- true
5
4
  end
6
5
 
7
6
  # Show applications
@@ -17,5 +16,6 @@ end
17
16
  # Load apps
18
17
  Padrino.mounted_apps.each do |app|
19
18
  puts "=> Loading Application #{app.name}"
20
- Padrino.load_dependency(app.app_file)
21
- end
19
+ Padrino.require_dependency(app.app_file)
20
+ app.app_object.setup_application!
21
+ end
data/padrino-core.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{padrino-core}
8
- s.version = "0.1.5"
8
+ s.version = "0.2.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Padrino Team", "Nathan Esquenazi", "Davide D'Agostino", "Arthur Chiu"]
12
- s.date = %q{2009-11-23}
12
+ s.date = %q{2009-11-24}
13
13
  s.default_executable = %q{padrino}
14
14
  s.description = %q{The Padrino core gem required for use of this framework}
15
15
  s.email = %q{nesquena@gmail.com}
@@ -29,6 +29,7 @@ Gem::Specification.new do |s|
29
29
  "lib/padrino-core/application.rb",
30
30
  "lib/padrino-core/caller.rb",
31
31
  "lib/padrino-core/loader.rb",
32
+ "lib/padrino-core/logger.rb",
32
33
  "lib/padrino-core/mounter.rb",
33
34
  "lib/padrino-core/reloader.rb",
34
35
  "lib/padrino-core/stat.rb",
@@ -44,11 +45,14 @@ Gem::Specification.new do |s|
44
45
  "padrino-core.gemspec",
45
46
  "test/fixtures/apps/.components",
46
47
  "test/fixtures/apps/.gitignore",
47
- "test/fixtures/apps/app.rb",
48
- "test/fixtures/apps/multi.rb",
48
+ "test/fixtures/apps/complex.rb",
49
+ "test/fixtures/apps/simple.rb",
49
50
  "test/helper.rb",
51
+ "test/test_application.rb",
50
52
  "test/test_padrino_core.rb",
51
- "test/test_padrino_mounter.rb"
53
+ "test/test_padrino_mounter.rb",
54
+ "test/test_reloader_complex.rb",
55
+ "test/test_reloader_simple.rb"
52
56
  ]
53
57
  s.homepage = %q{http://github.com/padrino/padrino-framework/tree/master/padrino-core}
54
58
  s.rdoc_options = ["--charset=UTF-8"]
@@ -0,0 +1,29 @@
1
+ PADRINO_ROOT = File.dirname(__FILE__) unless defined? PADRINO_ROOT
2
+
3
+ module LibDemo
4
+ module_function
5
+
6
+ def give_me_a_random
7
+ @rand ||= rand(100)
8
+ end
9
+ end
10
+
11
+ class Complex1Demo < Padrino::Application
12
+ set :reload, true
13
+ get("/old"){ "Old Sinatra Way" }
14
+ end
15
+
16
+ class Complex2Demo < Padrino::Application
17
+ set :reload, true
18
+ get("/old"){ "Old Sinatra Way" }
19
+ end
20
+
21
+ Complex1Demo.controllers do
22
+ get(""){ "Given random #{LibDemo.give_me_a_random}" }
23
+ end
24
+
25
+ Complex2Demo.controllers do
26
+ get(""){ "The magick number is: 88!" } # Change only the number!!!
27
+ end
28
+
29
+ Padrino.load!
@@ -0,0 +1,23 @@
1
+ PADRINO_ROOT = File.dirname(__FILE__) unless defined? PADRINO_ROOT
2
+ # Remove this comment if you want do some like this: ruby PADRINO_ENV=test app.rb
3
+ #
4
+ # require 'rubygems'
5
+ # require 'lib/padrino-core'
6
+ #
7
+ # Use this for prevent (when reload is in use) to re run the server.
8
+ #
9
+ # if Padrino.load!
10
+ # SingleDemo.run!
11
+ # end
12
+
13
+ class SimpleDemo < Padrino::Application
14
+ set :reload, true
15
+ end
16
+
17
+ SimpleDemo.controllers do
18
+ get "/" do
19
+ 'The magick number is: 66!' # Change only the number!!!
20
+ end
21
+ end
22
+
23
+ Padrino.load! # Remove this if you will run the app standalone
data/test/helper.rb CHANGED
@@ -7,19 +7,17 @@ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
7
  require 'rubygems'
8
8
  require 'padrino-core'
9
9
  require 'test/unit'
10
- require 'shoulda'
11
- require 'mocha'
12
10
  require 'rack/test'
13
- require 'webrat'
11
+ require 'rack'
12
+ require 'shoulda'
13
+
14
+ class Padrino::Application
15
+ # Allow assertions in request context
16
+ include Test::Unit::Assertions
17
+ end
14
18
 
15
19
  class Test::Unit::TestCase
16
20
  include Rack::Test::Methods
17
- include Webrat::Methods
18
- include Webrat::Matchers
19
-
20
- Webrat.configure do |config|
21
- config.mode = :rack
22
- end
23
21
 
24
22
  # Test App
25
23
  class PadrinoTestApp < Padrino::Application; end
@@ -46,24 +44,27 @@ class Test::Unit::TestCase
46
44
  assert File.exist?(file), "File '#{file}' does not exist!"
47
45
  assert_match pattern, File.read(file)
48
46
  end
49
- end
50
-
51
- class Object
52
- # Silences the output by redirecting to stringIO
53
- # silence_logger { ...commands... } => "...output..."
54
- def silence_logger(&block)
55
- orig_stdout = $stdout
56
- $stdout = log_buffer = StringIO.new
57
- block.call
58
- $stdout = orig_stdout
59
- log_buffer.rewind && log_buffer.read
60
- end
61
- end
62
-
63
- module Webrat
64
- module Logging
65
- def logger # :nodoc:
66
- @logger = nil
47
+
48
+ # Delegate other missing methods to response.
49
+ def method_missing(name, *args, &block)
50
+ if response && response.respond_to?(name)
51
+ response.send(name, *args, &block)
52
+ else
53
+ super
67
54
  end
68
55
  end
69
- end
56
+
57
+ alias :response :last_response
58
+ end
59
+ #
60
+ # class Object
61
+ # # Silences the output by redirecting to stringIO
62
+ # # silence_logger { ...commands... } => "...output..."
63
+ # def silence_logger(&block)
64
+ # orig_stdout = $stdout
65
+ # $stdout = log_buffer = StringIO.new
66
+ # block.call
67
+ # $stdout = orig_stdout
68
+ # log_buffer.rewind && log_buffer.read
69
+ # end
70
+ # end
@@ -0,0 +1,30 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestApplication < Test::Unit::TestCase
4
+
5
+ context 'for application functionality' do
6
+
7
+ should 'check default options' do
8
+ assert_match %r{test/helper.rb}, PadrinoTestApp.app_file
9
+ assert_equal :test, PadrinoTestApp.environment
10
+ assert_equal Padrino.root("views"), PadrinoTestApp.views
11
+ assert PadrinoTestApp.raise_errors
12
+ assert !PadrinoTestApp.logging
13
+ assert PadrinoTestApp.sessions
14
+ assert 'PadrinoTestApp', PadrinoTestApp.app_name
15
+ end
16
+
17
+ should 'check padrino specific options' do
18
+ assert !PadrinoTestApp.instance_variable_get(:@_configured)
19
+ PadrinoTestApp.send(:setup_application!)
20
+ assert PadrinoTestApp.instance_variable_get(:@_configured)
21
+ assert !PadrinoTestApp.send(:find_view_path)
22
+ assert !PadrinoTestApp.reload?
23
+ assert 'padrino_test_app', PadrinoTestApp.app_name
24
+ assert 'StandardFormBuilder', PadrinoTestApp.default_builder
25
+ assert !PadrinoTestApp.flash
26
+ assert !PadrinoTestApp.padrino_mailer
27
+ assert !PadrinoTestApp.padrino_helpers
28
+ end
29
+ end
30
+ end
@@ -13,7 +13,7 @@ class TestPadrinoCore < Test::Unit::TestCase
13
13
  end
14
14
 
15
15
  should 'validate global helpers' do
16
- assert_equal "test", Padrino.env
16
+ assert_equal :test, Padrino.env
17
17
  assert_match /\/test/, Padrino.root
18
18
  end
19
19
 
@@ -27,15 +27,15 @@ class TestPadrinoMounter < Test::Unit::TestCase
27
27
  mounter.to("/test")
28
28
  assert_equal "test", mounter.name
29
29
  assert_equal "Test", mounter.app_class
30
- assert_equal __FILE__, mounter.app_file
30
+ assert_match %r{test/apps/test/app.rb}, mounter.app_file
31
31
  assert_equal "/test", mounter.uri_root
32
32
  assert_nil mounter.app_root
33
33
  end
34
34
 
35
35
  should 'mount an app' do
36
- class AnApp < Padrino::Application; end
37
-
36
+ class ::AnApp < Padrino::Application; end
38
37
  Padrino.mount_core("an_app")
38
+ assert_equal AnApp, Padrino.mounted_apps.first.app_obj
39
39
  assert_equal ["core"], Padrino.mounted_apps.collect(&:name)
40
40
  end
41
41
 
@@ -43,18 +43,25 @@ class TestPadrinoMounter < Test::Unit::TestCase
43
43
  mounter = Padrino.mount_core("test")
44
44
  assert_equal "core", mounter.name
45
45
  assert_equal "Test", mounter.app_class
46
+ assert_equal Test, mounter.app_obj
46
47
  assert_equal Padrino.root('app/app.rb'), mounter.app_file
47
48
  assert_equal "/", mounter.uri_root
48
49
  assert_equal Padrino.root, mounter.app_root
49
50
  end
50
51
 
51
52
  should 'mount multiple apps' do
52
- class OneApp < Padrino::Application; end
53
- class TwoApp < Padrino::Application; end
53
+ class ::OneApp < Padrino::Application; end
54
+ class ::TwoApp < Padrino::Application; end
54
55
 
56
+ Padrino.mount("one_app").to("/one_app")
57
+ Padrino.mount("two_app").to("/two_app")
58
+ # And testing no duplicates
55
59
  Padrino.mount("one_app").to("/one_app")
56
60
  Padrino.mount("two_app").to("/two_app")
57
61
 
62
+ assert_equal OneApp, Padrino.mounted_apps[0].app_obj
63
+ assert_equal TwoApp, Padrino.mounted_apps[1].app_obj
64
+ assert_equal 2, Padrino.mounted_apps.size, "should not mount duplicate apps"
58
65
  assert_equal ["one_app", "two_app"], Padrino.mounted_apps.collect(&:name)
59
66
  end
60
67
 
@@ -74,9 +81,9 @@ class TestPadrinoMounter < Test::Unit::TestCase
74
81
  end
75
82
 
76
83
  get '/demo_1'
77
- assert_contain "Im Demo 1"
78
- visit '/demo_2'
79
- assert_contain "Im Demo 2"
84
+ assert_equal "Im Demo 1", body
85
+ get '/demo_2'
86
+ assert_equal "Im Demo 2", body
80
87
  end
81
88
  end
82
89
  end
@@ -0,0 +1,59 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+ require 'fixtures/apps/complex'
3
+
4
+ class TestComplexReloader < Test::Unit::TestCase
5
+
6
+ context 'for complex reload functionality' do
7
+
8
+ should 'correctly instantiate Complex(1-2)Demo fixture' do
9
+ Padrino.mounted_apps.clear
10
+ Padrino.mount("complex_1_demo").to("/complex_1_demo")
11
+ Padrino.mount("complex_2_demo").to("/complex_2_demo")
12
+ assert_equal ["complex_1_demo", "complex_2_demo"], Padrino.mounted_apps.collect(&:name)
13
+ assert Complex1Demo.reload?
14
+ assert Complex2Demo.reload?
15
+ assert_match %r{fixtures/apps/complex.rb}, Complex1Demo.app_file
16
+ assert_match %r{fixtures/apps/complex.rb}, Complex2Demo.app_file
17
+ end
18
+
19
+ should 'correctly reload Complex(1-2)Demo fixture' do
20
+ assert_match %r{fixtures/apps/complex.rb}, Complex1Demo.app_file
21
+ @app = Padrino.application
22
+
23
+ get "/"
24
+ assert_equal 404, status
25
+
26
+ get "/complex_1_demo"
27
+ assert_equal "Given random #{LibDemo.give_me_a_random}", body
28
+
29
+ get "/complex_2_demo"
30
+ assert_equal 200, status
31
+
32
+ get "/complex_1_demo/old"
33
+ assert_equal 200, status
34
+
35
+ get "/complex_2_demo/old"
36
+ assert_equal 200, status
37
+
38
+ new_phrase = "The magick number is: #{rand(100)}!"
39
+ buffer = File.read(Complex1Demo.app_file).gsub!(/The magick number is: \d+!/, new_phrase)
40
+ File.open(Complex1Demo.app_file, "w") { |f| f.write(buffer) }
41
+ sleep 1.2 # We need at least a cooldown of 1 sec.
42
+ get "/complex_2_demo"
43
+ assert_equal new_phrase, body
44
+
45
+ # Re-Check that we didn't forget any route
46
+ get "/complex_1_demo"
47
+ assert_equal "Given random #{LibDemo.give_me_a_random}", body
48
+
49
+ get "/complex_2_demo"
50
+ assert_equal 200, status
51
+
52
+ get "/complex_1_demo/old"
53
+ assert_equal 200, status
54
+
55
+ get "/complex_2_demo/old"
56
+ assert_equal 200, status
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,48 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+ require 'fixtures/apps/simple'
3
+
4
+ class TestSimpleReloader < Test::Unit::TestCase
5
+
6
+ context 'for simple reset functionality' do
7
+
8
+ should 'reset routes' do
9
+ mock_app do
10
+ 1.step(10).each do |i|
11
+ get("/#{i}") { "Foo #{i}" }
12
+ end
13
+ end
14
+ 1.step(10).each do |i|
15
+ get "/#{i}"
16
+ assert_equal "Foo #{i}", body
17
+ end
18
+ @app.reset_routes!
19
+ 1.step(10).each do |i|
20
+ get "/#{i}"
21
+ assert_equal 404, status
22
+ end
23
+ end
24
+ end
25
+
26
+ context 'for simple reload functionality' do
27
+
28
+ should 'correctly instantiate SimpleDemo fixture' do
29
+ Padrino.mounted_apps.clear
30
+ Padrino.mount_core("simple_demo")
31
+ assert_equal ["core"], Padrino.mounted_apps.collect(&:name)
32
+ assert SimpleDemo.reload?
33
+ assert_match %r{fixtures/apps/simple.rb}, SimpleDemo.app_file
34
+ end
35
+
36
+ should 'correctly reload SimpleDemo fixture' do
37
+ @app = SimpleDemo
38
+ get "/"
39
+ assert_equal 200, status
40
+ new_phrase = "The magick number is: #{rand(100)}!"
41
+ buffer = File.read(SimpleDemo.app_file).gsub!(/The magick number is: \d+!/, new_phrase)
42
+ File.open(SimpleDemo.app_file, "w") { |f| f.write(buffer) }
43
+ sleep 1.2 # We need at least a cooldown of 1 sec.
44
+ get "/"
45
+ assert_equal new_phrase, body
46
+ end
47
+ end
48
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: padrino-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Padrino Team
@@ -12,7 +12,7 @@ autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
14
 
15
- date: 2009-11-23 00:00:00 -08:00
15
+ date: 2009-11-24 00:00:00 -08:00
16
16
  default_executable: padrino
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
@@ -115,6 +115,7 @@ files:
115
115
  - lib/padrino-core/application.rb
116
116
  - lib/padrino-core/caller.rb
117
117
  - lib/padrino-core/loader.rb
118
+ - lib/padrino-core/logger.rb
118
119
  - lib/padrino-core/mounter.rb
119
120
  - lib/padrino-core/reloader.rb
120
121
  - lib/padrino-core/stat.rb
@@ -130,11 +131,14 @@ files:
130
131
  - padrino-core.gemspec
131
132
  - test/fixtures/apps/.components
132
133
  - test/fixtures/apps/.gitignore
133
- - test/fixtures/apps/app.rb
134
- - test/fixtures/apps/multi.rb
134
+ - test/fixtures/apps/complex.rb
135
+ - test/fixtures/apps/simple.rb
135
136
  - test/helper.rb
137
+ - test/test_application.rb
136
138
  - test/test_padrino_core.rb
137
139
  - test/test_padrino_mounter.rb
140
+ - test/test_reloader_complex.rb
141
+ - test/test_reloader_simple.rb
138
142
  has_rdoc: true
139
143
  homepage: http://github.com/padrino/padrino-framework/tree/master/padrino-core
140
144
  licenses: []
@@ -1,15 +0,0 @@
1
- PADRINO_ROOT = File.dirname(__FILE__) unless defined? PADRINO_ROOT
2
- require 'rubygems'
3
- require 'sinatra/base'
4
- require 'lib/padrino-core'
5
-
6
- class SingleDemo < Padrino::Application; end
7
-
8
- SingleDemo.controllers do
9
- get "/test" do
10
- 'This should work'
11
- end
12
- end
13
-
14
- Padrino.mount_core("single_demo")
15
- Padrino.load!
@@ -1,27 +0,0 @@
1
- PADRINO_ROOT = File.dirname(__FILE__) unless defined? PADRINO_ROOT
2
- require 'sinatra/base'
3
- require 'padrino-core'
4
-
5
- class Multi1Demo < Padrino::Application
6
- disable :padrino_routing
7
- disable :padrino_mailer
8
- disable :padrino_helpers
9
-
10
- get "" do
11
- "Im Core1Demo"
12
- end
13
- end
14
-
15
- class Mutli2Demo < Padrino::Application
16
- disable :padrino_routing
17
- disable :padrino_mailer
18
- disable :padrino_helpers
19
-
20
- get "" do
21
- "Im Core2Demo"
22
- end
23
- end
24
-
25
- Padrino.mount("multi_1_demo").to("/multi_1_demo")
26
- Padrino.mount("multi_2_demo").to("/multi_2_demo")
27
- Padrino.load!