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,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