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.
Files changed (101) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +20 -0
  3. data/README.rdoc +294 -0
  4. data/Rakefile +1 -0
  5. data/bin/tennpipes +8 -0
  6. data/lib/tennpipes-base.rb +196 -0
  7. data/lib/tennpipes-base/application.rb +175 -0
  8. data/lib/tennpipes-base/application/application_setup.rb +202 -0
  9. data/lib/tennpipes-base/application/authenticity_token.rb +25 -0
  10. data/lib/tennpipes-base/application/flash.rb +229 -0
  11. data/lib/tennpipes-base/application/params_protection.rb +129 -0
  12. data/lib/tennpipes-base/application/routing.rb +1002 -0
  13. data/lib/tennpipes-base/application/show_exceptions.rb +50 -0
  14. data/lib/tennpipes-base/caller.rb +53 -0
  15. data/lib/tennpipes-base/cli/adapter.rb +33 -0
  16. data/lib/tennpipes-base/cli/base.rb +105 -0
  17. data/lib/tennpipes-base/cli/console.rb +20 -0
  18. data/lib/tennpipes-base/cli/launcher.rb +103 -0
  19. data/lib/tennpipes-base/cli/rake.rb +50 -0
  20. data/lib/tennpipes-base/cli/rake_tasks.rb +72 -0
  21. data/lib/tennpipes-base/command.rb +38 -0
  22. data/lib/tennpipes-base/ext/sinatra.rb +29 -0
  23. data/lib/tennpipes-base/filter.rb +52 -0
  24. data/lib/tennpipes-base/images/404.png +0 -0
  25. data/lib/tennpipes-base/images/500.png +0 -0
  26. data/lib/tennpipes-base/loader.rb +202 -0
  27. data/lib/tennpipes-base/logger.rb +492 -0
  28. data/lib/tennpipes-base/module.rb +58 -0
  29. data/lib/tennpipes-base/mounter.rb +308 -0
  30. data/lib/tennpipes-base/path_router.rb +119 -0
  31. data/lib/tennpipes-base/path_router/compiler.rb +110 -0
  32. data/lib/tennpipes-base/path_router/error_handler.rb +8 -0
  33. data/lib/tennpipes-base/path_router/matcher.rb +123 -0
  34. data/lib/tennpipes-base/path_router/route.rb +169 -0
  35. data/lib/tennpipes-base/reloader.rb +309 -0
  36. data/lib/tennpipes-base/reloader/rack.rb +26 -0
  37. data/lib/tennpipes-base/reloader/storage.rb +55 -0
  38. data/lib/tennpipes-base/router.rb +98 -0
  39. data/lib/tennpipes-base/server.rb +119 -0
  40. data/lib/tennpipes-base/tasks.rb +21 -0
  41. data/lib/tennpipes-base/version.rb +20 -0
  42. data/lib/tennpipes-base/version.rb~ +20 -0
  43. data/test/fixtures/app_gem/Gemfile +4 -0
  44. data/test/fixtures/app_gem/app/app.rb +3 -0
  45. data/test/fixtures/app_gem/app_gem.gemspec +17 -0
  46. data/test/fixtures/app_gem/lib/app_gem.rb +7 -0
  47. data/test/fixtures/app_gem/lib/app_gem/version.rb +3 -0
  48. data/test/fixtures/apps/complex.rb +32 -0
  49. data/test/fixtures/apps/demo_app.rb +7 -0
  50. data/test/fixtures/apps/demo_demo.rb +7 -0
  51. data/test/fixtures/apps/demo_project/api/app.rb +7 -0
  52. data/test/fixtures/apps/demo_project/api/lib/api_lib.rb +3 -0
  53. data/test/fixtures/apps/demo_project/app.rb +7 -0
  54. data/test/fixtures/apps/external_apps/fake_lib.rb +1 -0
  55. data/test/fixtures/apps/external_apps/fake_root.rb +2 -0
  56. data/test/fixtures/apps/helpers/class_methods_helpers.rb +4 -0
  57. data/test/fixtures/apps/helpers/instance_methods_helpers.rb +4 -0
  58. data/test/fixtures/apps/helpers/support.rb +1 -0
  59. data/test/fixtures/apps/helpers/system_helpers.rb +8 -0
  60. data/test/fixtures/apps/kiq.rb +3 -0
  61. data/test/fixtures/apps/lib/myklass.rb +2 -0
  62. data/test/fixtures/apps/lib/myklass/mysubklass.rb +4 -0
  63. data/test/fixtures/apps/models/child.rb +2 -0
  64. data/test/fixtures/apps/models/parent.rb +5 -0
  65. data/test/fixtures/apps/mountable_apps/rack_apps.rb +15 -0
  66. data/test/fixtures/apps/mountable_apps/static.html +1 -0
  67. data/test/fixtures/apps/precompiled_app.rb +19 -0
  68. data/test/fixtures/apps/simple.rb +32 -0
  69. data/test/fixtures/apps/static.rb +10 -0
  70. data/test/fixtures/apps/system.rb +13 -0
  71. data/test/fixtures/apps/system_class_methods_demo.rb +7 -0
  72. data/test/fixtures/apps/system_instance_methods_demo.rb +7 -0
  73. data/test/fixtures/dependencies/a.rb +9 -0
  74. data/test/fixtures/dependencies/b.rb +4 -0
  75. data/test/fixtures/dependencies/c.rb +1 -0
  76. data/test/fixtures/dependencies/circular/e.rb +13 -0
  77. data/test/fixtures/dependencies/circular/f.rb +2 -0
  78. data/test/fixtures/dependencies/circular/g.rb +2 -0
  79. data/test/fixtures/dependencies/d.rb +4 -0
  80. data/test/fixtures/reloadable_apps/external/app/app.rb +6 -0
  81. data/test/fixtures/reloadable_apps/external/app/controllers/base.rb +6 -0
  82. data/test/fixtures/reloadable_apps/main/app.rb +10 -0
  83. data/test/helper.rb +30 -0
  84. data/test/test_application.rb +185 -0
  85. data/test/test_core.rb +93 -0
  86. data/test/test_csrf_protection.rb +208 -0
  87. data/test/test_dependencies.rb +57 -0
  88. data/test/test_filters.rb +389 -0
  89. data/test/test_flash.rb +168 -0
  90. data/test/test_locale.rb +21 -0
  91. data/test/test_logger.rb +295 -0
  92. data/test/test_mounter.rb +302 -0
  93. data/test/test_params_protection.rb +195 -0
  94. data/test/test_reloader_complex.rb +74 -0
  95. data/test/test_reloader_external.rb +21 -0
  96. data/test/test_reloader_simple.rb +101 -0
  97. data/test/test_reloader_system.rb +113 -0
  98. data/test/test_restful_routing.rb +33 -0
  99. data/test/test_router.rb +281 -0
  100. data/test/test_routing.rb +2328 -0
  101. 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