tennpipes-base 3.6.6
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.
- checksums.yaml +7 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +294 -0
- data/Rakefile +1 -0
- data/bin/tennpipes +8 -0
- data/lib/tennpipes-base.rb +196 -0
- data/lib/tennpipes-base/application.rb +175 -0
- data/lib/tennpipes-base/application/application_setup.rb +202 -0
- data/lib/tennpipes-base/application/authenticity_token.rb +25 -0
- data/lib/tennpipes-base/application/flash.rb +229 -0
- data/lib/tennpipes-base/application/params_protection.rb +129 -0
- data/lib/tennpipes-base/application/routing.rb +1002 -0
- data/lib/tennpipes-base/application/show_exceptions.rb +50 -0
- data/lib/tennpipes-base/caller.rb +53 -0
- data/lib/tennpipes-base/cli/adapter.rb +33 -0
- data/lib/tennpipes-base/cli/base.rb +105 -0
- data/lib/tennpipes-base/cli/console.rb +20 -0
- data/lib/tennpipes-base/cli/launcher.rb +103 -0
- data/lib/tennpipes-base/cli/rake.rb +50 -0
- data/lib/tennpipes-base/cli/rake_tasks.rb +72 -0
- data/lib/tennpipes-base/command.rb +38 -0
- data/lib/tennpipes-base/ext/sinatra.rb +29 -0
- data/lib/tennpipes-base/filter.rb +52 -0
- data/lib/tennpipes-base/images/404.png +0 -0
- data/lib/tennpipes-base/images/500.png +0 -0
- data/lib/tennpipes-base/loader.rb +202 -0
- data/lib/tennpipes-base/logger.rb +492 -0
- data/lib/tennpipes-base/module.rb +58 -0
- data/lib/tennpipes-base/mounter.rb +308 -0
- data/lib/tennpipes-base/path_router.rb +119 -0
- data/lib/tennpipes-base/path_router/compiler.rb +110 -0
- data/lib/tennpipes-base/path_router/error_handler.rb +8 -0
- data/lib/tennpipes-base/path_router/matcher.rb +123 -0
- data/lib/tennpipes-base/path_router/route.rb +169 -0
- data/lib/tennpipes-base/reloader.rb +309 -0
- data/lib/tennpipes-base/reloader/rack.rb +26 -0
- data/lib/tennpipes-base/reloader/storage.rb +55 -0
- data/lib/tennpipes-base/router.rb +98 -0
- data/lib/tennpipes-base/server.rb +119 -0
- data/lib/tennpipes-base/tasks.rb +21 -0
- data/lib/tennpipes-base/version.rb +20 -0
- data/lib/tennpipes-base/version.rb~ +20 -0
- data/test/fixtures/app_gem/Gemfile +4 -0
- data/test/fixtures/app_gem/app/app.rb +3 -0
- data/test/fixtures/app_gem/app_gem.gemspec +17 -0
- data/test/fixtures/app_gem/lib/app_gem.rb +7 -0
- data/test/fixtures/app_gem/lib/app_gem/version.rb +3 -0
- data/test/fixtures/apps/complex.rb +32 -0
- data/test/fixtures/apps/demo_app.rb +7 -0
- data/test/fixtures/apps/demo_demo.rb +7 -0
- data/test/fixtures/apps/demo_project/api/app.rb +7 -0
- data/test/fixtures/apps/demo_project/api/lib/api_lib.rb +3 -0
- data/test/fixtures/apps/demo_project/app.rb +7 -0
- data/test/fixtures/apps/external_apps/fake_lib.rb +1 -0
- data/test/fixtures/apps/external_apps/fake_root.rb +2 -0
- data/test/fixtures/apps/helpers/class_methods_helpers.rb +4 -0
- data/test/fixtures/apps/helpers/instance_methods_helpers.rb +4 -0
- data/test/fixtures/apps/helpers/support.rb +1 -0
- data/test/fixtures/apps/helpers/system_helpers.rb +8 -0
- data/test/fixtures/apps/kiq.rb +3 -0
- data/test/fixtures/apps/lib/myklass.rb +2 -0
- data/test/fixtures/apps/lib/myklass/mysubklass.rb +4 -0
- data/test/fixtures/apps/models/child.rb +2 -0
- data/test/fixtures/apps/models/parent.rb +5 -0
- data/test/fixtures/apps/mountable_apps/rack_apps.rb +15 -0
- data/test/fixtures/apps/mountable_apps/static.html +1 -0
- data/test/fixtures/apps/precompiled_app.rb +19 -0
- data/test/fixtures/apps/simple.rb +32 -0
- data/test/fixtures/apps/static.rb +10 -0
- data/test/fixtures/apps/system.rb +13 -0
- data/test/fixtures/apps/system_class_methods_demo.rb +7 -0
- data/test/fixtures/apps/system_instance_methods_demo.rb +7 -0
- data/test/fixtures/dependencies/a.rb +9 -0
- data/test/fixtures/dependencies/b.rb +4 -0
- data/test/fixtures/dependencies/c.rb +1 -0
- data/test/fixtures/dependencies/circular/e.rb +13 -0
- data/test/fixtures/dependencies/circular/f.rb +2 -0
- data/test/fixtures/dependencies/circular/g.rb +2 -0
- data/test/fixtures/dependencies/d.rb +4 -0
- data/test/fixtures/reloadable_apps/external/app/app.rb +6 -0
- data/test/fixtures/reloadable_apps/external/app/controllers/base.rb +6 -0
- data/test/fixtures/reloadable_apps/main/app.rb +10 -0
- data/test/helper.rb +30 -0
- data/test/test_application.rb +185 -0
- data/test/test_core.rb +93 -0
- data/test/test_csrf_protection.rb +208 -0
- data/test/test_dependencies.rb +57 -0
- data/test/test_filters.rb +389 -0
- data/test/test_flash.rb +168 -0
- data/test/test_locale.rb +21 -0
- data/test/test_logger.rb +295 -0
- data/test/test_mounter.rb +302 -0
- data/test/test_params_protection.rb +195 -0
- data/test/test_reloader_complex.rb +74 -0
- data/test/test_reloader_external.rb +21 -0
- data/test/test_reloader_simple.rb +101 -0
- data/test/test_reloader_system.rb +113 -0
- data/test/test_restful_routing.rb +33 -0
- data/test/test_router.rb +281 -0
- data/test/test_routing.rb +2328 -0
- metadata +301 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
module Tennpipes
|
|
2
|
+
module Module
|
|
3
|
+
attr_accessor :root
|
|
4
|
+
|
|
5
|
+
##
|
|
6
|
+
# Register this module as being loaded from a gem. This automatically
|
|
7
|
+
# sets the root and therefore the dependency paths correctly.
|
|
8
|
+
#
|
|
9
|
+
# @param [String] name
|
|
10
|
+
# The name of the gem. Has to be the name as stated in the gemspec.
|
|
11
|
+
#
|
|
12
|
+
# @returns the gems root.
|
|
13
|
+
def gem!(name)
|
|
14
|
+
self.root = Tennpipes.gem(name, self)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
##
|
|
18
|
+
# Helper method for file references within a Tennpipes module.
|
|
19
|
+
#
|
|
20
|
+
# @param [Array<String>] args
|
|
21
|
+
# The directories to join to {Module.root}.
|
|
22
|
+
#
|
|
23
|
+
# @return [String]
|
|
24
|
+
# The absolute path.
|
|
25
|
+
#
|
|
26
|
+
# @example
|
|
27
|
+
# module MyModule
|
|
28
|
+
# extend Tennpipes::Module
|
|
29
|
+
# gem! 'my_gem'
|
|
30
|
+
# end
|
|
31
|
+
# Module.root!
|
|
32
|
+
def root(*args)
|
|
33
|
+
File.expand_path(File.join(@root, *args))
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
##
|
|
37
|
+
# Returns the list of path globs to load as dependencies.
|
|
38
|
+
# Appends custom dependency patterns to the be loaded for Tennpipes.
|
|
39
|
+
#
|
|
40
|
+
# @return [Array<String>]
|
|
41
|
+
# The dependency paths.
|
|
42
|
+
#
|
|
43
|
+
# @example
|
|
44
|
+
# module MyModule
|
|
45
|
+
# extend Tennpipes::Module
|
|
46
|
+
# gem! 'my_gem'
|
|
47
|
+
# end
|
|
48
|
+
#
|
|
49
|
+
# Module.dependency_paths << "#{MyModule.root}/uploaders/*.rb"
|
|
50
|
+
#
|
|
51
|
+
def dependency_paths
|
|
52
|
+
[
|
|
53
|
+
"#{root}/lib/**/*.rb", "#{root}/shared/lib/**/*.rb",
|
|
54
|
+
"#{root}/models/**/*.rb", "#{root}/shared/models/**/*.rb"
|
|
55
|
+
]
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
require 'delegate'
|
|
2
|
+
|
|
3
|
+
module Tennpipes
|
|
4
|
+
##
|
|
5
|
+
# Represents a particular mounted Tennpipes application.
|
|
6
|
+
# Stores the name of the application (app folder name) and url mount path.
|
|
7
|
+
#
|
|
8
|
+
# @example
|
|
9
|
+
# Mounter.new("blog_app", :app_class => "Blog").to("/blog")
|
|
10
|
+
# Mounter.new("blog_app", :app_file => "/path/to/blog/app.rb").to("/blog")
|
|
11
|
+
#
|
|
12
|
+
class Mounter
|
|
13
|
+
DEFAULT_CASCADE = [404, 405]
|
|
14
|
+
class MounterException < RuntimeError
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class ApplicationWrapper < SimpleDelegator
|
|
18
|
+
attr_accessor :uri_root
|
|
19
|
+
attr_writer :public_folder
|
|
20
|
+
|
|
21
|
+
def initialize(app, options = {})
|
|
22
|
+
@options = options
|
|
23
|
+
super(app)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def dependencies
|
|
27
|
+
@__dependencies ||= Dir["#{root}/**/*.rb"]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def prerequisites
|
|
31
|
+
@__prerequisites ||= []
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def app_file
|
|
35
|
+
return @__app_file if @__app_file
|
|
36
|
+
obj = __getobj__
|
|
37
|
+
@__app_file = obj.respond_to?(:app_file) ? obj.app_file : @options[:app_file]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def root
|
|
41
|
+
return @__root if @__root
|
|
42
|
+
obj = __getobj__
|
|
43
|
+
@__root = obj.respond_to?(:root) ? obj.root : File.expand_path("#{app_file}/../")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def public_folder
|
|
47
|
+
return @public_folder if @public_folder
|
|
48
|
+
obj = __getobj__
|
|
49
|
+
@public_folder = obj.respond_to?(:public_folder) ? obj.public_folder : ""
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def app_name
|
|
53
|
+
@__app_name ||= @options[:app_name] || __getobj__.to_s.underscore.to_sym
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def setup_application!
|
|
57
|
+
@configured ||=
|
|
58
|
+
begin
|
|
59
|
+
$LOAD_PATH.concat(prerequisites)
|
|
60
|
+
Tennpipes.require_dependencies(dependencies, :force => true) if root.start_with?(Tennpipes.root)
|
|
61
|
+
true
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
attr_accessor :name, :uri_root, :app_file, :app_class, :app_root, :app_obj, :app_host, :cascade
|
|
67
|
+
|
|
68
|
+
##
|
|
69
|
+
# @param [String, Tennpipes::Application] name
|
|
70
|
+
# The app name or the {Tennpipes::Application} class.
|
|
71
|
+
#
|
|
72
|
+
# @param [Hash] options
|
|
73
|
+
# @option options [Symbol] :app_class (Detected from name)
|
|
74
|
+
# @option options [Symbol] :app_file (Automatically detected)
|
|
75
|
+
# @option options [Symbol] :app_obj (Detected)
|
|
76
|
+
# @option options [Symbol] :app_root (Directory of :app_file)
|
|
77
|
+
# @option options [Symbol] :gem The gem to load the app from (Detected from name)
|
|
78
|
+
#
|
|
79
|
+
def initialize(name, options={})
|
|
80
|
+
@name = name.to_s
|
|
81
|
+
@app_class = options[:app_class] || @name.camelize
|
|
82
|
+
@gem = options[:gem] || @app_class.split("::").first.underscore
|
|
83
|
+
@app_file = options[:app_file] || locate_app_file
|
|
84
|
+
@app_obj = options[:app_obj] || app_constant || locate_app_object
|
|
85
|
+
ensure_app_file! || ensure_app_object!
|
|
86
|
+
@app_obj = ApplicationWrapper.new(@app_obj, options) unless tennpipes_application?
|
|
87
|
+
@app_root = options[:app_root] || (@app_obj.respond_to?(:root) && @app_obj.root || File.dirname(@app_file))
|
|
88
|
+
@uri_root = "/"
|
|
89
|
+
@cascade = options[:cascade] ? true == options[:cascade] ? DEFAULT_CASCADE.dup : Array(options[:cascade]) : []
|
|
90
|
+
Tennpipes::Reloader.exclude_constants << @app_class
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def tennpipes_application?
|
|
94
|
+
@app_obj.ancestors.include?(Tennpipes::Application)
|
|
95
|
+
rescue NameError
|
|
96
|
+
false
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
##
|
|
100
|
+
# Registers the mounted application onto Tennpipes.
|
|
101
|
+
#
|
|
102
|
+
# @param [String] mount_url
|
|
103
|
+
# Path where we mount the app.
|
|
104
|
+
#
|
|
105
|
+
# @example
|
|
106
|
+
# Mounter.new("blog_app").to("/blog")
|
|
107
|
+
#
|
|
108
|
+
def to(mount_url)
|
|
109
|
+
@uri_root = mount_url
|
|
110
|
+
Tennpipes.insert_mounted_app(self)
|
|
111
|
+
self
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
##
|
|
115
|
+
# Registers the mounted application onto Tennpipes for the given host.
|
|
116
|
+
#
|
|
117
|
+
# @param [String] mount_host
|
|
118
|
+
# Host name.
|
|
119
|
+
#
|
|
120
|
+
# @example
|
|
121
|
+
# Mounter.new("blog_app").to("/blog").host("blog.tennpipes.org")
|
|
122
|
+
# Mounter.new("blog_app").host("blog.tennpipes.org")
|
|
123
|
+
# Mounter.new("catch_all").host(/.*\.tennpipes.org/)
|
|
124
|
+
#
|
|
125
|
+
def host(mount_host)
|
|
126
|
+
@app_host = mount_host
|
|
127
|
+
Tennpipes.insert_mounted_app(self)
|
|
128
|
+
self
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
##
|
|
132
|
+
# Maps Tennpipes application onto a Tennpipes::Router.
|
|
133
|
+
# For use in constructing a Rack application.
|
|
134
|
+
#
|
|
135
|
+
# @param [Tennpipes::Router]
|
|
136
|
+
#
|
|
137
|
+
# @return [Tennpipes::Router]
|
|
138
|
+
#
|
|
139
|
+
# @example
|
|
140
|
+
# @app.map_onto(router)
|
|
141
|
+
#
|
|
142
|
+
def map_onto(router)
|
|
143
|
+
app_data = self
|
|
144
|
+
app_obj = @app_obj
|
|
145
|
+
if tennpipes_application?
|
|
146
|
+
app_obj.set :uri_root, app_data.uri_root
|
|
147
|
+
app_obj.set :app_name, app_data.app_obj.app_name.to_s
|
|
148
|
+
app_obj.set :app_file, app_data.app_file unless File.exist?(app_obj.app_file)
|
|
149
|
+
app_obj.set :root, app_data.app_root unless app_data.app_root.blank?
|
|
150
|
+
app_obj.set :public_folder, Tennpipes.root('public', app_data.uri_root) unless File.exist?(app_obj.public_folder)
|
|
151
|
+
app_obj.set :static, File.exist?(app_obj.public_folder) if app_obj.nil?
|
|
152
|
+
app_obj.set :cascade, app_data.cascade
|
|
153
|
+
else
|
|
154
|
+
app_obj.uri_root = app_data.uri_root
|
|
155
|
+
app_obj.public_folder = Tennpipes.root('public', app_data.uri_root) unless File.exist?(app_obj.public_folder)
|
|
156
|
+
end
|
|
157
|
+
app_obj.setup_application! # Initializes the app here with above settings.
|
|
158
|
+
router.map(:to => app_obj, :path => app_data.uri_root, :host => app_data.app_host)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
###
|
|
162
|
+
# Returns the route objects for the mounted application.
|
|
163
|
+
#
|
|
164
|
+
def routes
|
|
165
|
+
app_obj.routes
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
###
|
|
169
|
+
# Returns the basic route information for each named route.
|
|
170
|
+
#
|
|
171
|
+
# @return [Array]
|
|
172
|
+
# Array of routes.
|
|
173
|
+
#
|
|
174
|
+
def named_routes
|
|
175
|
+
app_obj.routes.map { |route|
|
|
176
|
+
route_name = route.name.to_s
|
|
177
|
+
route_name =
|
|
178
|
+
if route.controller
|
|
179
|
+
route_name.split(" ", 2).map{|name| ":#{name}" }.join(", ")
|
|
180
|
+
else
|
|
181
|
+
":#{route_name}"
|
|
182
|
+
end
|
|
183
|
+
name_array = "(#{route_name})"
|
|
184
|
+
request_method = route.request_methods.first
|
|
185
|
+
next if route.name.blank? || request_method == 'HEAD'
|
|
186
|
+
original_path = route.original_path.is_a?(Regexp) ? route.original_path.inspect : route.original_path
|
|
187
|
+
full_path = File.join(uri_root, original_path)
|
|
188
|
+
OpenStruct.new(:verb => request_method, :identifier => route.name, :name => name_array, :path => full_path)
|
|
189
|
+
}.compact
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
##
|
|
193
|
+
# Makes two Mounters equal if they have the same name and uri_root.
|
|
194
|
+
#
|
|
195
|
+
# @param [Tennpipes::Mounter] other
|
|
196
|
+
#
|
|
197
|
+
def ==(other)
|
|
198
|
+
other.is_a?(Mounter) && self.app_class == other.app_class && self.uri_root == other.uri_root
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
##
|
|
202
|
+
# @return [Tennpipes::Application]
|
|
203
|
+
# the class object for the app if defined, nil otherwise.
|
|
204
|
+
#
|
|
205
|
+
def app_constant
|
|
206
|
+
klass = Object
|
|
207
|
+
for piece in app_class.split("::")
|
|
208
|
+
piece = piece.to_sym
|
|
209
|
+
if klass.const_defined?(piece, false)
|
|
210
|
+
klass = klass.const_get(piece)
|
|
211
|
+
else
|
|
212
|
+
return
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
klass
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
protected
|
|
219
|
+
##
|
|
220
|
+
# Locates and requires the file to load the app constant.
|
|
221
|
+
#
|
|
222
|
+
def locate_app_object
|
|
223
|
+
@_app_object ||= begin
|
|
224
|
+
ensure_app_file!
|
|
225
|
+
Tennpipes.require_dependencies(app_file)
|
|
226
|
+
app_constant
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
##
|
|
231
|
+
# Returns the determined location of the mounted application main file.
|
|
232
|
+
#
|
|
233
|
+
def locate_app_file
|
|
234
|
+
candidates = []
|
|
235
|
+
candidates << app_constant.app_file if app_constant.respond_to?(:app_file) && File.exist?(app_constant.app_file.to_s)
|
|
236
|
+
candidates << Tennpipes.first_caller if File.identical?(Tennpipes.first_caller.to_s, Tennpipes.called_from.to_s)
|
|
237
|
+
candidates << Tennpipes.mounted_root(name.downcase, "app.rb")
|
|
238
|
+
simple_name = name.split("::").last.downcase
|
|
239
|
+
mod_name = name.split("::")[0..-2].join("::")
|
|
240
|
+
Tennpipes.modules.each do |mod|
|
|
241
|
+
if mod.name == mod_name
|
|
242
|
+
candidates << mod.root(simple_name, "app.rb")
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
candidates << Tennpipes.root("app", "app.rb")
|
|
246
|
+
candidates.find { |candidate| File.exist?(candidate) }
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
###
|
|
250
|
+
# Raises an exception unless app_file is located properly.
|
|
251
|
+
#
|
|
252
|
+
def ensure_app_file!
|
|
253
|
+
message = "Unable to locate source file for app '#{app_class}', try with :app_file => '/path/app.rb'"
|
|
254
|
+
raise MounterException, message unless @app_file
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
###
|
|
258
|
+
# Raises an exception unless app_obj is defined properly.
|
|
259
|
+
#
|
|
260
|
+
def ensure_app_object!
|
|
261
|
+
message = "Unable to locate app for '#{app_class}', try with :app_class => 'MyAppClass'"
|
|
262
|
+
raise MounterException, message unless @app_obj
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
class << self
|
|
267
|
+
attr_writer :mounted_root # Set root directory where tennpipes searches mounted apps
|
|
268
|
+
|
|
269
|
+
##
|
|
270
|
+
# @param [Array] args
|
|
271
|
+
#
|
|
272
|
+
# @return [String]
|
|
273
|
+
# the root to the mounted apps base directory.
|
|
274
|
+
#
|
|
275
|
+
def mounted_root(*args)
|
|
276
|
+
Tennpipes.root(@mounted_root ||= "", *args)
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
##
|
|
280
|
+
# @return [Array]
|
|
281
|
+
# the mounted tennpipes applications (MountedApp objects)
|
|
282
|
+
#
|
|
283
|
+
def mounted_apps
|
|
284
|
+
@mounted_apps ||= []
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
##
|
|
288
|
+
# Inserts a Mounter object into the mounted applications (avoids duplicates).
|
|
289
|
+
#
|
|
290
|
+
# @param [Tennpipes::Mounter] mounter
|
|
291
|
+
#
|
|
292
|
+
def insert_mounted_app(mounter)
|
|
293
|
+
Tennpipes.mounted_apps.push(mounter) unless Tennpipes.mounted_apps.include?(mounter)
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
##
|
|
297
|
+
# Mounts a new sub-application onto Tennpipes project.
|
|
298
|
+
#
|
|
299
|
+
# @see Tennpipes::Mounter#new
|
|
300
|
+
#
|
|
301
|
+
# @example
|
|
302
|
+
# Tennpipes.mount("blog_app").to("/blog")
|
|
303
|
+
#
|
|
304
|
+
def mount(name, options={})
|
|
305
|
+
Mounter.new(name, options)
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
end
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
require 'tennpipes-base/path_router/error_handler'
|
|
2
|
+
require 'tennpipes-base/path_router/route'
|
|
3
|
+
require 'tennpipes-base/path_router/matcher'
|
|
4
|
+
require 'tennpipes-base/path_router/compiler'
|
|
5
|
+
|
|
6
|
+
module Tennpipes
|
|
7
|
+
##
|
|
8
|
+
# Provides an HTTP router for use in path routing.
|
|
9
|
+
#
|
|
10
|
+
module PathRouter
|
|
11
|
+
##
|
|
12
|
+
# Constructs an instance of PathRouter::Router.
|
|
13
|
+
#
|
|
14
|
+
def self.new
|
|
15
|
+
Router.new
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class Router
|
|
19
|
+
attr_reader :current_order, :routes, :engine
|
|
20
|
+
|
|
21
|
+
##
|
|
22
|
+
# Constructs an instance of PathRouter::Router.
|
|
23
|
+
#
|
|
24
|
+
def initialize
|
|
25
|
+
reset!
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
##
|
|
29
|
+
# Adds a new route to routes.
|
|
30
|
+
#
|
|
31
|
+
def add(verb, path, options = {}, &block)
|
|
32
|
+
route = Route.new(path, verb, options, &block)
|
|
33
|
+
route.router = self
|
|
34
|
+
@routes << route
|
|
35
|
+
route
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
##
|
|
39
|
+
# Returns all routes which are matched with the condition
|
|
40
|
+
#
|
|
41
|
+
def call(request, &block)
|
|
42
|
+
prepare! unless prepared?
|
|
43
|
+
@engine.call_by_request(request, &block)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
##
|
|
47
|
+
# Returns all routes which are matched with the condition without block
|
|
48
|
+
#
|
|
49
|
+
def recognize(request_or_env)
|
|
50
|
+
prepare! unless prepared?
|
|
51
|
+
@engine.find_by(request_or_env)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
##
|
|
55
|
+
# Finds a path which is matched with conditions from arguments
|
|
56
|
+
#
|
|
57
|
+
def path(name, *args)
|
|
58
|
+
params = args.extract_options!
|
|
59
|
+
@routes.each do |route|
|
|
60
|
+
next unless route.name == name
|
|
61
|
+
matcher = route.matcher
|
|
62
|
+
params_for_expand = params.dup
|
|
63
|
+
if !args.empty? && matcher.mustermann?
|
|
64
|
+
matcher.names.each_with_index do |matcher_name, index|
|
|
65
|
+
params_for_expand[matcher_name.to_sym] ||= args[index]
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
return matcher.mustermann? ? matcher.expand(params_for_expand) : route.path_for_generation
|
|
69
|
+
end
|
|
70
|
+
fail InvalidRouteException
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
##
|
|
74
|
+
# Recognizes route and expanded params from a path.
|
|
75
|
+
#
|
|
76
|
+
def recognize_path(path_info)
|
|
77
|
+
prepare! unless prepared?
|
|
78
|
+
route = @engine.find_by_pattern(path_info).first
|
|
79
|
+
[route.name, route.params_for(path_info, {})]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
##
|
|
83
|
+
# Resets all routes, current order and preparation.
|
|
84
|
+
#
|
|
85
|
+
def reset!
|
|
86
|
+
@routes = []
|
|
87
|
+
@current_order = 0
|
|
88
|
+
@prepared = nil
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
##
|
|
92
|
+
# Increments the order.
|
|
93
|
+
#
|
|
94
|
+
def increment_order
|
|
95
|
+
@current_order += 1
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
##
|
|
99
|
+
# Constructs an instance of PathRouter::Compiler,
|
|
100
|
+
# and sorts all routes by using the order.
|
|
101
|
+
#
|
|
102
|
+
def prepare!
|
|
103
|
+
@engine = Compiler.new(@routes)
|
|
104
|
+
@prepared = true
|
|
105
|
+
return if @current_order.zero?
|
|
106
|
+
@routes.sort_by!(&:order)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
private
|
|
110
|
+
|
|
111
|
+
##
|
|
112
|
+
# Returns true if the router has been prepared.
|
|
113
|
+
#
|
|
114
|
+
def prepared?
|
|
115
|
+
!!@prepared
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|