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,26 @@
1
+ module Tennpipes
2
+ module Reloader
3
+ ##
4
+ # This class acts as a Rack middleware to be added to the application stack.
5
+ # This middleware performs a check and reload for source files at the start
6
+ # of each request, but also respects a specified cool down time
7
+ # during which no further action will be taken.
8
+ #
9
+ class Rack
10
+ def initialize(app, cooldown=1)
11
+ @app = app
12
+ @cooldown = cooldown
13
+ @last = (Time.now - cooldown)
14
+ end
15
+
16
+ # Invoked in order to perform the reload as part of the request stack.
17
+ def call(env)
18
+ if @cooldown && Time.now > @last + @cooldown
19
+ Thread.list.size > 1 ? Thread.exclusive { Tennpipes.reload! } : Tennpipes.reload!
20
+ @last = Time.now
21
+ end
22
+ @app.call(env)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,55 @@
1
+ module Tennpipes
2
+ module Reloader
3
+ module Storage
4
+ extend self
5
+
6
+ def clear!
7
+ files.each_key do |file|
8
+ remove(file)
9
+ Reloader.remove_feature(file)
10
+ end
11
+ @files = {}
12
+ end
13
+
14
+ def remove(name)
15
+ file = files[name] || return
16
+ file[:constants].each{ |constant| Reloader.remove_constant(constant) }
17
+ file[:features].each{ |feature| Reloader.remove_feature(feature) }
18
+ files.delete(name)
19
+ end
20
+
21
+ def prepare(name)
22
+ file = remove(name)
23
+ @old_entries ||= {}
24
+ @old_entries[name] = {
25
+ :constants => ObjectSpace.classes,
26
+ :features => old_features = Set.new($LOADED_FEATURES.dup)
27
+ }
28
+ features = file && file[:features] || []
29
+ features.each{ |feature| Reloader.safe_load(feature, :force => true) }
30
+ Reloader.remove_feature(name) if old_features.include?(name)
31
+ end
32
+
33
+ def commit(name)
34
+ entry = {
35
+ :constants => ObjectSpace.new_classes(@old_entries[name][:constants]),
36
+ :features => Set.new($LOADED_FEATURES) - @old_entries[name][:features] - [name]
37
+ }
38
+ files[name] = entry
39
+ @old_entries.delete(name)
40
+ end
41
+
42
+ def rollback(name)
43
+ new_constants = ObjectSpace.new_classes(@old_entries[name][:constants])
44
+ new_constants.each{ |klass| Reloader.remove_constant(klass) }
45
+ @old_entries.delete(name)
46
+ end
47
+
48
+ private
49
+
50
+ def files
51
+ @files ||= {}
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,98 @@
1
+ module Tennpipes
2
+ ##
3
+ # This class is an extended version of Rack::URLMap.
4
+ #
5
+ # Tennpipes::Router like Rack::URLMap dispatches in such a way that the
6
+ # longest paths are tried first, since they are most specific.
7
+ #
8
+ # Features:
9
+ #
10
+ # * Map a path to the specified App
11
+ # * Ignore server names (this solve issues with vhost and domain aliases)
12
+ # * Use hosts instead of server name for mappings (this help us with our vhost and domain aliases)
13
+ #
14
+ # @example
15
+ #
16
+ # routes = Tennpipes::Router.new do
17
+ # map(:path => "/", :to => TennpipesWeb, :host => "tennpipes.local")
18
+ # map(:path => "/", :to => Admin, :host => "admin.tennpipes.local")
19
+ # end
20
+ # run routes
21
+ #
22
+ # routes = Tennpipes::Router.new do
23
+ # map(:path => "/", :to => TennpipesWeb, :host => /*.tennpipes.local/)
24
+ # end
25
+ # run routes
26
+ #
27
+ # @api semipublic
28
+ class Router
29
+ def initialize(*mapping, &block)
30
+ @mapping = []
31
+ mapping.each { |m| map(m) }
32
+ instance_eval(&block) if block
33
+ end
34
+
35
+ ##
36
+ # Map a route path and host to a specified application.
37
+ #
38
+ # @param [Hash] options
39
+ # The options to map.
40
+ # @option options [Sinatra::Application] :to
41
+ # The class of the application to mount.
42
+ # @option options [String] :path ("/")
43
+ # The path to map the specified application.
44
+ # @option options [String] :host
45
+ # The host to map the specified application.
46
+ #
47
+ # @example
48
+ # map(:path => "/", :to => TennpipesWeb, :host => "tennpipes.local")
49
+ #
50
+ # @return [Array] The sorted route mappings.
51
+ # @api semipublic
52
+ def map(options={})
53
+ path = options[:path] || "/"
54
+ host = options[:host]
55
+ app = options[:to]
56
+
57
+ raise ArgumentError, "paths need to start with /" if path[0] != ?/
58
+ raise ArgumentError, "app is required" if app.nil?
59
+
60
+ path = path.chomp('/')
61
+ match = Regexp.new("^#{Regexp.quote(path).gsub('/', '/+')}(.*)", nil, 'n')
62
+ host = Regexp.new("^#{Regexp.quote(host)}$", true, 'n') unless host.nil? || host.is_a?(Regexp)
63
+
64
+ @mapping << [host, path, match, app]
65
+ end
66
+
67
+ # The call handler setup to route a request given the mappings specified.
68
+ def call(env)
69
+ began_at = Time.now
70
+ path_info = env["PATH_INFO"].to_s
71
+ script_name = env['SCRIPT_NAME']
72
+ http_host = env['HTTP_HOST']
73
+ last_result = nil
74
+
75
+ @mapping.each do |host, path, match, app|
76
+ next unless host.nil? || http_host =~ host
77
+ next unless path_info =~ match && rest = $1
78
+ next unless rest.empty? || rest[0] == ?/
79
+
80
+ rest = "/" if rest.empty?
81
+
82
+ env['SCRIPT_NAME'] = script_name + path
83
+ env['PATH_INFO'] = rest
84
+ last_result = app.call(env)
85
+
86
+ cascade_setting = app.respond_to?(:cascade) ? app.cascade : true
87
+ cascade_statuses = cascade_setting.respond_to?(:include?) ? cascade_setting : Mounter::DEFAULT_CASCADE
88
+ break unless cascade_setting && cascade_statuses.include?(last_result[0])
89
+ end
90
+ last_result || begin
91
+ env['SCRIPT_NAME'] = script_name
92
+ env['PATH_INFO'] = path_info
93
+ Tennpipes::Logger::Rack.new(nil,'/').send(:log, env, 404, {}, began_at) if logger.debug?
94
+ [404, {"Content-Type" => "text/plain", "X-Cascade" => "pass"}, ["Not Found: #{path_info}"]]
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,119 @@
1
+ module Tennpipes
2
+ ##
3
+ # Runs the Tennpipes apps as a self-hosted server using:
4
+ # thin, mongrel, or WEBrick in that order.
5
+ #
6
+ # @example
7
+ # Tennpipes.run! # with these defaults => host: "127.0.0.1", port: "3000", adapter: the first found
8
+ # Tennpipes.run!("0.0.0.0", "4000", "mongrel") # use => host: "0.0.0.0", port: "4000", adapter: "mongrel"
9
+ #
10
+ def self.run!(options={})
11
+ Tennpipes.load!
12
+ Server.start(*detect_application(options))
13
+ end
14
+
15
+ private
16
+
17
+ #
18
+ #
19
+ def self.detect_application(options)
20
+ default_config_file = 'config.ru'
21
+ if (config_file = options.delete(:config)) || File.file?(default_config_file)
22
+ config_file ||= default_config_file
23
+ fail "Rack config file `#{config_file}` must have `.ru` extension" unless config_file =~ /\.ru$/
24
+ rack_app, rack_options = Rack::Builder.parse_file(config_file)
25
+ [rack_app, rack_options.merge(options)]
26
+ else
27
+ [Tennpipes.application, options]
28
+ end
29
+ end
30
+
31
+ ##
32
+ # This module builds a Tennpipes server to run the project based on available handlers.
33
+ #
34
+ class Server < Rack::Server
35
+ DEFAULT_ADDRESS = { :Host => '127.0.0.1', :Port => 3000 }
36
+
37
+ # Server Handlers
38
+ Handlers = [:thin, :puma, :'spider-gazelle', :mongrel, :trinidad, :webrick]
39
+
40
+ # Starts the application on the available server with specified options.
41
+ def self.start(app, options={})
42
+ options = options.to_hash.symbolize_keys
43
+ options.update(parse_server_options(options.delete(:options)))
44
+ options.update(detect_address(options))
45
+ options[:pid] = prepare_pid(options[:pid]) if options[:daemonize]
46
+ options[:server] = detect_rack_handler if options[:server].blank?
47
+ # disable Webrick AccessLog
48
+ options[:AccessLog] = []
49
+ new(options, app).start
50
+ end
51
+
52
+ def initialize(options, app)
53
+ @options, @app = options, app
54
+ end
55
+
56
+ # Starts the application on the available server with specified options.
57
+ def start
58
+ puts "=> Tennpipes/#{Tennpipes.version} has taken the stage #{Tennpipes.env} at http://#{options[:Host]}:#{options[:Port]}"
59
+ [:INT, :TERM].each { |sig| trap(sig) { exit } }
60
+ super do |server|
61
+ server.threaded = true if server.respond_to?(:threaded=)
62
+ end
63
+ ensure
64
+ puts "<= Tennpipes leaves the gun, takes the cannoli" unless options[:daemonize]
65
+ end
66
+
67
+ # The application the server will run.
68
+ def app
69
+ @app
70
+ end
71
+ alias :wrapped_app :app
72
+
73
+ def options
74
+ @options
75
+ end
76
+
77
+ private
78
+
79
+ # Detects the supported handler to use.
80
+ #
81
+ # @example
82
+ # detect_rack_handler => <ThinHandler>
83
+ #
84
+ def self.detect_rack_handler
85
+ Handlers.each do |handler|
86
+ begin
87
+ return handler if Rack::Handler.get(handler.to_s.downcase)
88
+ rescue LoadError
89
+ rescue NameError
90
+ end
91
+ end
92
+ fail "Server handler (#{Handlers.join(', ')}) not found."
93
+ end
94
+
95
+ # Prepares a directory for pid file.
96
+ #
97
+ def self.prepare_pid(pid)
98
+ pid = 'tmp/pids/server.pid' if pid.blank?
99
+ FileUtils.mkdir_p(File.dirname(pid))
100
+ File.expand_path(pid)
101
+ end
102
+
103
+ # Parses an array of server options.
104
+ #
105
+ def self.parse_server_options(options)
106
+ parsed_server_options = Array(options).flat_map{ |option| option.split('=', 2) }
107
+ Hash[*parsed_server_options].symbolize_keys
108
+ end
109
+
110
+ # Detects Host and Port for Rack server.
111
+ #
112
+ def self.detect_address(options)
113
+ address = DEFAULT_ADDRESS.merge options.slice(:Host, :Port)
114
+ address[:Host] = options[:host] if options[:host].present?
115
+ address[:Port] = options[:port] if options[:port].present?
116
+ address
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,21 @@
1
+ module Tennpipes
2
+ ##
3
+ # This module it's used for bootstrap with tennpipes rake
4
+ # third party tasks, in your gem/plugin/extension you
5
+ # need only do this:
6
+ #
7
+ # @example
8
+ # Tennpipes::Tasks.files << yourtask.rb
9
+ # Tennpipes::Tasks.files.concat(Dir["/path/to/all/my/tasks/*.rb"])
10
+ # Tennpipes::Tasks.files.unshift("yourtask.rb")
11
+ #
12
+ module Tasks
13
+
14
+ ##
15
+ # Returns a list of files to handle with tennpipes rake
16
+ #
17
+ def self.files
18
+ @files ||= []
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,20 @@
1
+ #
2
+ # Manages current Tennpipes version for use in gem generation.
3
+ #
4
+ # We put this in a separate file so you can get tennpipes version
5
+ # without include full tennpipes core.
6
+ #
7
+ module Tennpipes
8
+ # The version constant for the current version of Tennpipes.
9
+ VERSION = '3.6.6' unless defined?(Tennpipes::VERSION)
10
+
11
+ #
12
+ # The current Tennpipes version.
13
+ #
14
+ # @return [String]
15
+ # The version number.
16
+ #
17
+ def self.version
18
+ VERSION
19
+ end
20
+ end # Tennpipes
@@ -0,0 +1,20 @@
1
+ #
2
+ # Manages current Tennpipes version for use in gem generation.
3
+ #
4
+ # We put this in a separate file so you can get tennpipes version
5
+ # without include full tennpipes core.
6
+ #
7
+ module Tennpipes
8
+ # The version constant for the current version of Tennpipes.
9
+ VERSION = '0.0.2' unless defined?(Tennpipes::VERSION)
10
+
11
+ #
12
+ # The current Tennpipes version.
13
+ #
14
+ # @return [String]
15
+ # The version number.
16
+ #
17
+ def self.version
18
+ VERSION
19
+ end
20
+ end # Tennpipes
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in app_gem.gemspec
4
+ gemspec
@@ -0,0 +1,3 @@
1
+ class AppGem::App < Tennpipes::Application
2
+ set :version, AppGem::VERSION
3
+ end
@@ -0,0 +1,17 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/app_gem/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Florian Gilcher"]
6
+ gem.email = ["florian.gilcher@asquera.de"]
7
+ gem.description = %q{TODO: Write a gem description}
8
+ gem.summary = %q{TODO: Write a gem summary}
9
+ gem.homepage = ""
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "app_gem"
15
+ gem.require_paths = ["app", "lib"]
16
+ gem.version = AppGem::VERSION
17
+ end
@@ -0,0 +1,7 @@
1
+ require 'tennpipes'
2
+
3
+ module AppGem
4
+ extend Tennpipes::Module
5
+
6
+ gem! 'app_gem'
7
+ end
@@ -0,0 +1,3 @@
1
+ module AppGem
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,32 @@
1
+ TENNPIPES_ROOT = File.dirname(__FILE__) unless defined? TENNPIPES_ROOT
2
+
3
+ module LibDemo
4
+ def self.give_me_a_random
5
+ @rand ||= rand(100)
6
+ end
7
+ end
8
+
9
+ class Complex1Demo < Tennpipes::Application
10
+ set :reload, true
11
+ get("/old"){ "Old Sinatra Way" }
12
+ end
13
+
14
+ class Complex2Demo < Tennpipes::Application
15
+ set :reload, true
16
+ get("/old"){ "Old Sinatra Way" }
17
+
18
+ controllers :var do
19
+ get(:destroy){ params.inspect }
20
+ end
21
+
22
+ get("/"){ "The magick number is: 12!" } # Change only the number!!!
23
+ end
24
+
25
+ Complex1Demo.controllers do
26
+ get("/"){ "Given random #{LibDemo.give_me_a_random}" }
27
+ end
28
+
29
+ Complex2Demo.controllers do
30
+ end
31
+
32
+ Tennpipes.load!
@@ -0,0 +1,7 @@
1
+ TENNPIPES_ROOT = File.dirname(__FILE__) unless defined? TENNPIPES_ROOT
2
+
3
+ module Demo
4
+ class App < Tennpipes::Application
5
+ set :reload, true
6
+ end
7
+ end