ghazel-rack-bug 0.3.0.1

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 (86) hide show
  1. data/.gitignore +3 -0
  2. data/History.txt +45 -0
  3. data/MIT-LICENSE.txt +19 -0
  4. data/README.md +118 -0
  5. data/Rakefile +23 -0
  6. data/Thorfile +113 -0
  7. data/lib/rack/bug.rb +83 -0
  8. data/lib/rack/bug/autoloading.rb +24 -0
  9. data/lib/rack/bug/filtered_backtrace.rb +38 -0
  10. data/lib/rack/bug/options.rb +89 -0
  11. data/lib/rack/bug/panel.rb +50 -0
  12. data/lib/rack/bug/panel_app.rb +33 -0
  13. data/lib/rack/bug/panels/active_record_panel.rb +45 -0
  14. data/lib/rack/bug/panels/active_record_panel/activerecord_extensions.rb +18 -0
  15. data/lib/rack/bug/panels/cache_panel.rb +51 -0
  16. data/lib/rack/bug/panels/cache_panel/dalli_extension.rb +16 -0
  17. data/lib/rack/bug/panels/cache_panel/memcache_extension.rb +129 -0
  18. data/lib/rack/bug/panels/cache_panel/panel_app.rb +48 -0
  19. data/lib/rack/bug/panels/cache_panel/stats.rb +97 -0
  20. data/lib/rack/bug/panels/log_panel.rb +56 -0
  21. data/lib/rack/bug/panels/log_panel/logger_extension.rb +24 -0
  22. data/lib/rack/bug/panels/memory_panel.rb +27 -0
  23. data/lib/rack/bug/panels/rails_info_panel.rb +23 -0
  24. data/lib/rack/bug/panels/redis_panel.rb +44 -0
  25. data/lib/rack/bug/panels/redis_panel/redis_extension.rb +28 -0
  26. data/lib/rack/bug/panels/redis_panel/stats.rb +52 -0
  27. data/lib/rack/bug/panels/request_variables_panel.rb +52 -0
  28. data/lib/rack/bug/panels/sphinx_panel.rb +44 -0
  29. data/lib/rack/bug/panels/sphinx_panel/sphinx_extension.rb +25 -0
  30. data/lib/rack/bug/panels/sphinx_panel/stats.rb +96 -0
  31. data/lib/rack/bug/panels/sql_panel.rb +55 -0
  32. data/lib/rack/bug/panels/sql_panel/panel_app.rb +37 -0
  33. data/lib/rack/bug/panels/sql_panel/query.rb +63 -0
  34. data/lib/rack/bug/panels/sql_panel/sql_extension.rb +11 -0
  35. data/lib/rack/bug/panels/templates_panel.rb +44 -0
  36. data/lib/rack/bug/panels/templates_panel/actionview_extension.rb +12 -0
  37. data/lib/rack/bug/panels/templates_panel/rendering.rb +67 -0
  38. data/lib/rack/bug/panels/templates_panel/trace.rb +34 -0
  39. data/lib/rack/bug/panels/timer_panel.rb +40 -0
  40. data/lib/rack/bug/params_signature.rb +63 -0
  41. data/lib/rack/bug/public/__rack_bug__/bookmarklet.html +10 -0
  42. data/lib/rack/bug/public/__rack_bug__/bookmarklet.js +217 -0
  43. data/lib/rack/bug/public/__rack_bug__/bug.css +216 -0
  44. data/lib/rack/bug/public/__rack_bug__/bug.js +84 -0
  45. data/lib/rack/bug/public/__rack_bug__/jquery-1.3.2.js +4376 -0
  46. data/lib/rack/bug/public/__rack_bug__/jquery.tablesorter.min.js +1 -0
  47. data/lib/rack/bug/public/__rack_bug__/spinner.gif +0 -0
  48. data/lib/rack/bug/rack_static_bug_avoider.rb +16 -0
  49. data/lib/rack/bug/redirect_interceptor.rb +27 -0
  50. data/lib/rack/bug/render.rb +66 -0
  51. data/lib/rack/bug/toolbar.rb +66 -0
  52. data/lib/rack/bug/views/error.html.erb +16 -0
  53. data/lib/rack/bug/views/panels/active_record.html.erb +17 -0
  54. data/lib/rack/bug/views/panels/cache.html.erb +93 -0
  55. data/lib/rack/bug/views/panels/execute_sql.html.erb +32 -0
  56. data/lib/rack/bug/views/panels/explain_sql.html.erb +32 -0
  57. data/lib/rack/bug/views/panels/log.html.erb +21 -0
  58. data/lib/rack/bug/views/panels/profile_sql.html.erb +32 -0
  59. data/lib/rack/bug/views/panels/rails_info.html.erb +19 -0
  60. data/lib/rack/bug/views/panels/redis.html.erb +46 -0
  61. data/lib/rack/bug/views/panels/request_variables.html.erb +29 -0
  62. data/lib/rack/bug/views/panels/sphinx.html.erb +32 -0
  63. data/lib/rack/bug/views/panels/sql.html.erb +43 -0
  64. data/lib/rack/bug/views/panels/templates.html.erb +7 -0
  65. data/lib/rack/bug/views/panels/timer.html.erb +19 -0
  66. data/lib/rack/bug/views/panels/view_cache.html.erb +19 -0
  67. data/lib/rack/bug/views/redirect.html.erb +16 -0
  68. data/lib/rack/bug/views/toolbar.html.erb +42 -0
  69. data/rack-bug.gemspec +147 -0
  70. data/spec/fixtures/config.ru +8 -0
  71. data/spec/fixtures/dummy_panel.rb +2 -0
  72. data/spec/fixtures/sample_app.rb +46 -0
  73. data/spec/rack/bug/panels/active_record_panel_spec.rb +30 -0
  74. data/spec/rack/bug/panels/cache_panel_spec.rb +167 -0
  75. data/spec/rack/bug/panels/log_panel_spec.rb +43 -0
  76. data/spec/rack/bug/panels/memory_panel_spec.rb +22 -0
  77. data/spec/rack/bug/panels/rails_info_panel_spec.rb +40 -0
  78. data/spec/rack/bug/panels/redis_panel_spec.rb +69 -0
  79. data/spec/rack/bug/panels/sql_panel_spec.rb +146 -0
  80. data/spec/rack/bug/panels/templates_panel_spec.rb +71 -0
  81. data/spec/rack/bug/panels/timer_panel_spec.rb +38 -0
  82. data/spec/rack/bug_spec.rb +137 -0
  83. data/spec/rcov.opts +1 -0
  84. data/spec/spec.opts +1 -0
  85. data/spec/spec_helper.rb +44 -0
  86. metadata +201 -0
@@ -0,0 +1,3 @@
1
+ coverage
2
+ pkg
3
+ TODO
@@ -0,0 +1,45 @@
1
+ == HEAD
2
+
3
+ * New features
4
+
5
+ * Can use LoggerPanel on ruby stdlib Logger in non-rails app (Tim Connor)
6
+
7
+ * Bug fixes
8
+
9
+ * Fix profile, explain and select in the queries tab, fixes issue #22 (ebertech)
10
+
11
+ * Minor fixes
12
+
13
+ * Explicitly require 'digest/sha1' (Jérémy Lecour)
14
+ * Eliminate unreachable code in params signature validation (Tim Connor)
15
+
16
+ * Compatibilty
17
+
18
+ * Make Redis panel compatible with latest redis-rb gem, without breaking older redis-rb versions (Luke Melia)
19
+
20
+ * Other
21
+
22
+ * Refactoring and code cleanup (Tim Connor)
23
+ * Testing cleanup - better isolation of Rails vs. non-Rails in tests (Tim Connor)
24
+
25
+ == 0.3.0 / 2010-05-28
26
+
27
+ * New features
28
+
29
+ * Log panel includes log level and timestamp (Tim Connor)
30
+ * Sphinx panel (George Chatzigeorgiou)
31
+ * Backtraces for Redis panel (Luke Melia & Joey Aghion)
32
+
33
+ * Minor fixes
34
+
35
+ * Don't "enable" rack bug if you hit cancel on the bookmarklet prompt (Mischa Fierer)
36
+
37
+ * Compatibilty
38
+
39
+ * backtrace filtering now supports more than just Rails (Alex Chaffee)
40
+ * compatibility with current rack-test (Luke Melia & Joey Aghion)
41
+ * update Sinatra sample app (Tim Conner)
42
+
43
+ == 0.2.1
44
+
45
+ * The beginning of recorded history
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2009 Bryan Helmkamp
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,118 @@
1
+ Rack::Bug
2
+ =========
3
+
4
+ * Repository: [http://github.com/brynary/rack-bug](http://github.com/brynary/rack-bug)
5
+
6
+ Description
7
+ -----------
8
+
9
+ Rack::Bug adds a diagnostics toolbar to Rack apps. When enabled, it injects a floating div
10
+ allowing exploration of logging, database queries, template rendering times, etc.
11
+
12
+ Features
13
+ --------
14
+
15
+ * Password-based security
16
+ * IP-based security
17
+ * Rack::Bug instrumentation/reporting is broken up into panels.
18
+ * Panels in default configuration:
19
+ * Rails Info
20
+ * Timer
21
+ * Request Variables
22
+ * SQL
23
+ * Active Record
24
+ * Cache
25
+ * Templates
26
+ * Log
27
+ * Memory
28
+ * Other bundled panels:
29
+ * Redis
30
+ * Sphinx
31
+ * The API for adding your own panels is simple and powerful
32
+
33
+ Rails quick start
34
+ ---------------------------
35
+
36
+ script/plugin install git://github.com/brynary/rack-bug.git
37
+
38
+ In config/environments/development.rb, add:
39
+
40
+ config.middleware.use "Rack::Bug",
41
+ :secret_key => "someverylongandveryhardtoguesspreferablyrandomstring"
42
+
43
+ Add the bookmarklet to your browser:
44
+
45
+ open http://RAILS_APP/__rack_bug__/bookmarklet.html
46
+
47
+ Using with non-Rails Rack apps
48
+ ------------------------------
49
+
50
+ Just 'use Rack::Bug' as any other middleware. See the SampleApp in the spec/fixtures folder for an example Sinatra app.
51
+
52
+ If you wish to use the logger panel define the LOGGER constant that is a ruby Logger or ActiveSupport::BufferedLogger
53
+
54
+ Configuring custom panels
55
+ -------------------------
56
+
57
+ Specify the set of panels you want, in the order you want them to appear:
58
+
59
+ require "rack/bug"
60
+
61
+ ActionController::Dispatcher.middleware.use Rack::Bug,
62
+ :secret_key => "someverylongandveryhardtoguesspreferablyrandomstring",
63
+ :panel_classes => [
64
+ Rack::Bug::TimerPanel,
65
+ Rack::Bug::RequestVariablesPanel,
66
+ Rack::Bug::RedisPanel,
67
+ Rack::Bug::TemplatesPanel,
68
+ Rack::Bug::LogPanel,
69
+ Rack::Bug::MemoryPanel
70
+ ]
71
+
72
+
73
+ Running Rack::Bug in staging or production
74
+ ------------------------------------------
75
+
76
+ We have have found that Rack::Bug is fast enough to run in production for specific troubleshooting efforts.
77
+
78
+ ### Configuration ####
79
+
80
+ Add the middleware configuration to an initializer or the appropriate environment files, taking the rest of this section into consideration.
81
+
82
+ ### Security ####
83
+
84
+ Restrict access to particular IP addresses:
85
+
86
+ require "ipaddr"
87
+
88
+ ActionController::Dispatcher.middleware.use "Rack::Bug"
89
+ :secret_key => "someverylongandveryhardtoguesspreferablyrandomstring",
90
+ :ip_masks => [IPAddr.new("2.2.2.2/0")]
91
+
92
+ Restrict access using a password:
93
+
94
+ ActionController::Dispatcher.middleware.use "Rack::Bug",
95
+ :secret_key => "someverylongandveryhardtoguesspreferablyrandomstring",
96
+ :password => "yourpassword"
97
+
98
+
99
+ Authors
100
+ -------
101
+
102
+ - Maintained by [Bryan Helmkamp](mailto:bryan@brynary.com)
103
+ - Contributions from Luke Melia, Joey Aghion, Tim Connor, and more
104
+
105
+ Thanks
106
+ ------
107
+ Inspiration for Rack::Bug is primarily from the Django debug toolbar. Additional ideas from Rails footnotes, Rack's ShowException middleware, Oink, and Rack::Cache
108
+
109
+ Thanks to Weplay.com for supporting the development of Rack::Bug
110
+
111
+ Development
112
+ -----------
113
+ For development, you'll need to install the following gems: rspec, rack-test, webrat, sinatra
114
+
115
+ License
116
+ -------
117
+
118
+ See MIT-LICENSE.txt in this directory.
@@ -0,0 +1,23 @@
1
+ require "rubygems"
2
+ require "spec/rake/spectask"
3
+
4
+ Spec::Rake::SpecTask.new do |t|
5
+ t.spec_opts = ['--options', "\"#{File.dirname(__FILE__)}/spec/spec.opts\""]
6
+ end
7
+
8
+ desc "Run all specs in spec directory with RCov"
9
+ Spec::Rake::SpecTask.new(:rcov) do |t|
10
+ t.spec_opts = ['--options', "\"#{File.dirname(__FILE__)}/spec/spec.opts\""]
11
+ t.rcov = true
12
+ t.rcov_opts = lambda do
13
+ IO.readlines(File.dirname(__FILE__) + "/spec/rcov.opts").map {|l| l.chomp.split " "}.flatten
14
+ end
15
+ end
16
+
17
+ desc "Run the specs"
18
+ task :default => :spec
19
+
20
+ desc 'Removes trailing whitespace'
21
+ task :whitespace do
22
+ sh %{find . -name '*.rb' -exec sed -i '' 's/ *$//g' {} \\;}
23
+ end
@@ -0,0 +1,113 @@
1
+ module GemHelpers
2
+
3
+ def generate_gemspec
4
+ $LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), "lib")))
5
+ require "rack/bug"
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = "rack-bug"
9
+ s.version = Rack::Bug::VERSION
10
+ s.author = "Bryan Helmkamp"
11
+ s.email = "bryan@brynary.com"
12
+ s.homepage = "http://github.com/brynary/rack-bug"
13
+ s.summary = "Debugging toolbar for Rack applications implemented as middleware"
14
+ # s.description = "TODO"
15
+ s.rubyforge_project = "rack-bug"
16
+
17
+ require "git"
18
+ repo = Git.open(".")
19
+
20
+ s.files = normalize_files(repo.ls_files.keys - repo.lib.ignored_files)
21
+ s.test_files = normalize_files(Dir['spec/**/*.rb'] - repo.lib.ignored_files)
22
+
23
+ s.has_rdoc = true
24
+ s.extra_rdoc_files = %w[README.md MIT-LICENSE.txt]
25
+
26
+ s.add_dependency "rack", ">= 1.0"
27
+ s.add_development_dependency "webrat"
28
+ s.add_development_dependency "rspec"
29
+ s.add_development_dependency "sinatra"
30
+ s.add_development_dependency "git"
31
+ end
32
+ end
33
+
34
+ def normalize_files(array)
35
+ # only keep files, no directories, and sort
36
+ array.select do |path|
37
+ File.file?(path)
38
+ end.sort
39
+ end
40
+
41
+ # Adds extra space when outputting an array. This helps create better version
42
+ # control diffs, because otherwise it is all on the same line.
43
+ def prettyify_array(gemspec_ruby, array_name)
44
+ gemspec_ruby.gsub(/s\.#{array_name.to_s} = \[.+?\]/) do |match|
45
+ leadin, files = match[0..-2].split("[")
46
+ leadin + "[\n #{files.split(",").join(",\n ")}\n ]"
47
+ end
48
+ end
49
+
50
+ def read_gemspec
51
+ @read_gemspec ||= eval(File.read("rack-bug.gemspec"))
52
+ end
53
+
54
+ def sh(command)
55
+ puts command
56
+ system command
57
+ end
58
+ end
59
+
60
+ class Default < Thor
61
+ include GemHelpers
62
+
63
+ desc "gemspec", "Regenerate rack-bug.gemspec"
64
+ def gemspec
65
+ File.open("rack-bug.gemspec", "w") do |file|
66
+ gemspec_ruby = generate_gemspec.to_ruby
67
+ gemspec_ruby = prettyify_array(gemspec_ruby, :files)
68
+ gemspec_ruby = prettyify_array(gemspec_ruby, :test_files)
69
+ gemspec_ruby = prettyify_array(gemspec_ruby, :extra_rdoc_files)
70
+
71
+ file.write gemspec_ruby
72
+ end
73
+
74
+ puts "Wrote gemspec to rack-bug.gemspec"
75
+ read_gemspec.validate
76
+ end
77
+
78
+ desc "build", "Build a rack-bug gem"
79
+ def build
80
+ sh "gem build rack-bug.gemspec"
81
+ FileUtils.mkdir_p "pkg"
82
+ FileUtils.mv read_gemspec.file_name, "pkg"
83
+ end
84
+
85
+ desc "install", "Install the latest built gem"
86
+ def install
87
+ sh "gem install --local pkg/#{read_gemspec.file_name}"
88
+ end
89
+
90
+ desc "release", "Release the current branch to GitHub and Gemcutter"
91
+ def release
92
+ gemspec
93
+ build
94
+ Release.new.tag
95
+ Release.new.gem
96
+ end
97
+ end
98
+
99
+ class Release < Thor
100
+ include GemHelpers
101
+
102
+ desc "tag", "Tag the gem on the origin server"
103
+ def tag
104
+ release_tag = "v#{read_gemspec.version}"
105
+ sh "git tag -a #{release_tag} -m 'Tagging #{release_tag}'"
106
+ sh "git push origin #{release_tag}"
107
+ end
108
+
109
+ desc "gem", "Push the gem to Gemcutter"
110
+ def gem
111
+ sh "gem push pkg/#{read_gemspec.file_name}"
112
+ end
113
+ end
@@ -0,0 +1,83 @@
1
+ require "ipaddr"
2
+ require "digest"
3
+ require "rack"
4
+ require "digest/sha1"
5
+ require "rack/bug/autoloading"
6
+
7
+ class Rack::Bug
8
+ include Options
9
+
10
+ VERSION = "0.3.0"
11
+
12
+ class SecurityError < StandardError
13
+ end
14
+
15
+ def self.enable
16
+ Thread.current["rack-bug.enabled"] = true
17
+ end
18
+
19
+ def self.disable
20
+ Thread.current["rack-bug.enabled"] = false
21
+ end
22
+
23
+ def self.enabled?
24
+ Thread.current["rack-bug.enabled"] == true
25
+ end
26
+
27
+ def initialize(app, options = {}, &block)
28
+ @app = asset_server(app)
29
+ initialize_options options
30
+ instance_eval(&block) if block_given?
31
+
32
+ @toolbar = Toolbar.new(RedirectInterceptor.new(@app))
33
+ end
34
+
35
+
36
+ def call(env)
37
+ env.replace @default_options.merge(env)
38
+ @env = env
39
+ @original_request = Rack::Request.new(@env)
40
+
41
+ if toolbar_requested? && ip_authorized? && password_authorized? && toolbar_xhr?
42
+ @toolbar.call(env)
43
+ else
44
+ @app.call(env)
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def toolbar_xhr?
51
+ !@original_request.xhr? || @original_request.path =~ /^\/__rack_bug__/
52
+ end
53
+
54
+ def asset_server(app)
55
+ RackStaticBugAvoider.new(app, Rack::Static.new(app, :urls => ["/__rack_bug__"], :root => public_path))
56
+ end
57
+
58
+ def public_path
59
+ ::File.expand_path(::File.dirname(__FILE__) + "/bug/public")
60
+ end
61
+
62
+ def toolbar_requested?
63
+ @original_request.cookies["rack_bug_enabled"]
64
+ end
65
+
66
+ def ip_authorized?
67
+ return true unless options["rack-bug.ip_masks"]
68
+
69
+ options["rack-bug.ip_masks"].any? do |ip_mask|
70
+ ip_mask.include?(IPAddr.new(@original_request.ip))
71
+ end
72
+ end
73
+
74
+ def password_authorized?
75
+ return true unless options["rack-bug.password"]
76
+
77
+ expected_sha = Digest::SHA1.hexdigest ["rack_bug", options["rack-bug.password"]].join(":")
78
+ actual_sha = @original_request.cookies["rack_bug_password"]
79
+
80
+ actual_sha == expected_sha
81
+ end
82
+
83
+ end
@@ -0,0 +1,24 @@
1
+ class Rack::Bug
2
+ autoload :FilteredBacktrace, "rack/bug/filtered_backtrace"
3
+ autoload :Options, "rack/bug/options"
4
+ autoload :Panel, "rack/bug/panel"
5
+ autoload :PanelApp, "rack/bug/panel_app"
6
+ autoload :ParamsSignature, "rack/bug/params_signature"
7
+ autoload :RackStaticBugAvoider, "rack/bug/rack_static_bug_avoider"
8
+ autoload :RedirectInterceptor, "rack/bug/redirect_interceptor"
9
+ autoload :Render, "rack/bug/render"
10
+ autoload :Toolbar, "rack/bug/toolbar"
11
+
12
+ # Panels
13
+ autoload :ActiveRecordPanel, "rack/bug/panels/active_record_panel"
14
+ autoload :CachePanel, "rack/bug/panels/cache_panel"
15
+ autoload :LogPanel, "rack/bug/panels/log_panel"
16
+ autoload :MemoryPanel, "rack/bug/panels/memory_panel"
17
+ autoload :RailsInfoPanel, "rack/bug/panels/rails_info_panel"
18
+ autoload :RedisPanel, "rack/bug/panels/redis_panel"
19
+ autoload :RequestVariablesPanel, "rack/bug/panels/request_variables_panel"
20
+ autoload :SQLPanel, "rack/bug/panels/sql_panel"
21
+ autoload :TemplatesPanel, "rack/bug/panels/templates_panel"
22
+ autoload :TimerPanel, "rack/bug/panels/timer_panel"
23
+ autoload :SphinxPanel, "rack/bug/panels/sphinx_panel"
24
+ end
@@ -0,0 +1,38 @@
1
+ module Rack
2
+ class Bug
3
+ module FilteredBacktrace
4
+
5
+ def backtrace
6
+ @backtrace
7
+ end
8
+
9
+ def has_backtrace?
10
+ filtered_backtrace.any?
11
+ end
12
+
13
+ def filtered_backtrace
14
+ @filtered_backtrace ||= @backtrace.map{|l| l.to_s.strip }.select do |line|
15
+ root_for_backtrace_filtering.nil? ||
16
+ (line.index(root_for_backtrace_filtering) == 0) && !(line.index(root_for_backtrace_filtering("vendor")) == 0)
17
+ end
18
+ end
19
+
20
+ def root_for_backtrace_filtering(sub_path = nil)
21
+ if defined?(Rails) && Rails.respond_to?(:root)
22
+ sub_path ? Rails.root.join(sub_path) : Rails.root
23
+ else
24
+ root = if defined?(RAILS_ROOT)
25
+ RAILS_ROOT
26
+ elsif defined?(ROOT)
27
+ ROOT
28
+ elsif defined?(Sinatra::Application)
29
+ Sinatra::Application.root
30
+ else
31
+ nil
32
+ end
33
+ sub_path ? ::File.join(root, sub_path) : root
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end