tennpipes-base 3.6.6

Sign up to get free protection for your applications and to get access to all the features.
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