president
nwb 11 years ago
commit b52690adc2

@ -6,6 +6,7 @@ unless RUBY_PLATFORM =~ /w32/
gem 'rubyzip'
gem 'zip-zip'
end
gem 'seems_rateable', path: 'lib/seems_rateable'
gem "rails", "3.2.13"
gem "jquery-rails", "~> 2.0.2"
@ -15,6 +16,11 @@ gem "fastercsv", "~> 1.5.0", :platforms => [:mri_18, :mingw_18, :jruby]
gem "builder", "3.0.0"
gem 'acts-as-taggable-on'
group :development do
gem 'better_errors', path: 'lib/better_errors'
gem 'rack-mini-profiler', path: 'lib/rack-mini-profiler'
end
# Optional gem for LDAP authentication
group :ldap do
gem "net-ldap", "~> 0.3.1"
@ -70,16 +76,6 @@ else
warn("Please configure your config/database.yml first")
end
group :development do
gem "rdoc", ">= 2.4.2"
if nil
gem 'thin'
gem 'rack-mini-profiler'
end
end
local_gemfile = File.join(File.dirname(__FILE__), "Gemfile.local")
if File.exists?(local_gemfile)
puts "Loading Gemfile.local ..." if $DEBUG # `ruby -d` or `bundle -v`

@ -1,3 +1,16 @@
PATH
remote: lib/better_errors
specs:
better_errors (1.1.0)
coderay (>= 1.0.0)
erubis (>= 2.6.6)
PATH
remote: lib/rack-mini-profiler
specs:
rack-mini-profiler (0.9.1)
rack (>= 1.1.3)
PATH
remote: lib/seems_rateable
specs:
@ -105,6 +118,7 @@ DEPENDENCIES
activerecord-jdbc-adapter (= 1.2.5)
activerecord-jdbcmysql-adapter
acts-as-taggable-on
better_errors!
builder (= 3.0.0)
coderay (~> 1.0.6)
fastercsv (~> 1.5.0)
@ -112,8 +126,8 @@ DEPENDENCIES
jquery-rails (~> 2.0.2)
mysql2 (~> 0.3.11)
net-ldap (~> 0.3.1)
rack-mini-profiler!
rack-openid
rails (= 3.2.13)
rdoc (>= 2.4.2)
ruby-openid (~> 2.1.4)
seems_rateable!

@ -716,6 +716,11 @@ class ProjectsController < ApplicationController
@document = @project.documents.build
#
@base_courses_tag = @project.project_type
#判断能否显示真名(当前用户为课程的教师时显示真名)
if @project.project_type == Project::ProjectType_course
@teachers= searchTeacherAndAssistant(@project)
@canShowRealName = isCourseTeacher(User.current.id)
end
respond_to do |format|
format.html{render :layout => 'base_courses' if @base_courses_tag==1}
format.api

@ -65,7 +65,7 @@ class UsersController < ApplicationController
def user_projects
if User.current.admin?
@memberships = @user.memberships.all
@memberships = @user.memberships.all(conditions: "projects.project_type = #{Project::ProjectType_project}")
else
cond = Project.visible_condition(User.current) + " AND projects.project_type <> 1"
@memberships = @user.memberships.all(:conditions => cond)

@ -58,9 +58,14 @@ module ApplicationHelper
end
# Displays a link to user's account page if active
def link_to_user(user, options={})
def link_to_user(user, canShowRealName = false, options={})
if user.is_a?(User)
name = h(user.name(options[:format]))
if canShowRealName
name = h(user.realname(options[:format]))
else
name = h(user.name(options[:format]))
end
if user.active? || (User.current.admin? && user.logged?)
link_to name, {:controller=> 'users', :action => 'show', id: user.id, host: Setting.user_domain}, :class => user.css_classes
else

@ -22,7 +22,7 @@ class Mailer < ActionMailer::Base
helper :custom_fields
include Redmine::I18n
include CoursesHelper
def self.default_url_options
{ :host => Setting.host_name, :protocol => Setting.protocol }
end
@ -44,6 +44,16 @@ class Mailer < ActionMailer::Base
when :Bid
respond_url(journals_for_message.jour, anchor: "word_li_#{journals_for_message.id}")
when :Project
if journals_for_message.jour.project_type == 1
project = journals_for_message.jour
@teachers = searchTeacherAndAssistant journals_for_message.jour
@recipients ||= []
@teachers.each do |teacher|
@recipients << teacher
end
mail :to => @recipients,
:subject => "您的课程#{journals_for_message.jour.name}中有了新的留言"
end
project_feedback_url(journals_for_message.jour, anchor: "word_li_#{journals_for_message.id}")
when :Contest
show_contest_contest_url(journals_for_message.jour, anchor: "word_li_#{journals_for_message.id}")

@ -18,7 +18,7 @@
<td colspan="2" valign="top">
<strong> <%= h(e.project) if @project.nil? || @project.id != e.project.id %></strong>
<span class="font_lighter">
<%= link_to_user(e.event_author) if e.respond_to?(:event_author) %>
<%= link_to_user(e.event_author,@canShowRealName) if e.respond_to?(:event_author) %>
<%= l(:label_new_activity) %> </span>
<%= link_to "#{eventToLanguageCourse(e.event_type, @project)}: "<< format_activity_title(e.event_title), (e.event_type.eql?("attachment")&&e.container.kind_of?(Project)) ? project_files_path(e.container) : e.event_url %>
@ -46,6 +46,7 @@
<!-- Added by Longjun 在最后一页显示创建信息 -->
<% if format_date(day) == format_date(@date_to - @days) %>
<h1>Test</h1>
<div >
<table width="660">
<tr>
@ -80,7 +81,16 @@
<td colspan="2">
<table width="580">
<tr>
<td > <%= link_to (h @user.try(:name)), user_path(@user) if @user %> <%= l(:label_user_create_project) %> <%= link_to @project.name %><strong> !</strong></td>
<td >
<%
#判断是否显示真名
if @canShowRealName
%>
<%= link_to (h @user.try(:realname)), user_path(@user) if @user %>
<% else %>
<%= link_to (h @user.try(:name)), user_path(@user) if @user %>
<% end %>
<%= l(:label_user_create_project) %> <%= link_to @project.name %><strong> !</strong></td>
</tr>
<tr>
<td class="font_lighter" style="float: right"><%= l :label_update_time %>: <%= format_time(@project.created_on) %>

@ -3,10 +3,9 @@
<hr />
<% users_results.each do |user| %>
<p class="font_description2">
<strong><%= l(:label_tags_user_name) %><%= link_to ("#{user.firstname+user.lastname}"),
<strong><%= l(:label_tags_user_name) %><%= link_to ("#{user.name}"),
:controller => "users",:action => "show",:id => user.id%></strong>
<br />
<!--<strong><%= l(:label_tags_user_mail) %></strong><%= mail_to(h(user.mail)) %>-->
</p>
<div class="line_under"></div>
<% end %>

@ -266,7 +266,7 @@ en:
field_is_public: Public
field_parent: Subproject of
field_is_in_roadmap: Issues displayed in roadmap
field_login: Login
field_login: Account/Email
field_mail_notification: Email notifications
field_admin: Administrator
field_last_login_on: Last connection

@ -284,7 +284,7 @@ zh:
field_is_public: 公开
field_parent: 上级项目
field_is_in_roadmap: 在路线图中显示
field_login: 登录名
field_login: 账户/邮箱
field_mail_notification: 邮件通知
field_admin: 管理员
field_last_login_on: 最后登录

@ -0,0 +1,4 @@
language: ruby
rvm:
- 2.1.0
- 2.0.0

@ -0,0 +1 @@
--markup markdown --no-private

@ -0,0 +1,3 @@
# Changelog
See https://github.com/charliesome/better_errors/releases

@ -0,0 +1,10 @@
source 'https://rubygems.org'
gemspec
gem "rake"
gem "rspec", "2.14.1"
gem "binding_of_caller", platforms: :ruby
gem "pry", "0.9.12"
gem "yard"
gem "kramdown"

@ -0,0 +1,22 @@
Copyright (c) 2014 Charlie Somerville
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

@ -0,0 +1,103 @@
# Better Errors [![Gem Version](http://img.shields.io/gem/v/better_errors.svg)](https://rubygems.org/gems/better_errors) [![Build Status](https://travis-ci.org/charliesome/better_errors.svg)](https://travis-ci.org/charliesome/better_errors) [![Code Climate](http://img.shields.io/codeclimate/github/charliesome/better_errors.svg)](https://codeclimate.com/github/charliesome/better_errors)
Better Errors replaces the standard Rails error page with a much better and more useful error page. It is also usable outside of Rails in any Rack app as Rack middleware.
![image](http://i.imgur.com/6zBGAAb.png)
## Features
* Full stack trace
* Source code inspection for all stack frames (with highlighting)
* Local and instance variable inspection
* Live REPL on every stack frame
## Installation
Add this to your Gemfile:
```ruby
group :development do
gem "better_errors"
end
```
If you would like to use Better Errors' **advanced features** (REPL, local/instance variable inspection, pretty stack frame names), you need to add the [`binding_of_caller`](https://github.com/banister/binding_of_caller) gem by [@banisterfiend](http://twitter.com/banisterfiend) to your Gemfile:
```ruby
gem "binding_of_caller"
```
This is an optional dependency however, and Better Errors will work without it.
_Note: If you discover that Better Errors isn't working - particularly after upgrading from version 0.5.0 or less - be sure to set `config.consider_all_requests_local = true` in `config/environments/development.rb`._
## Security
**NOTE:** It is *critical* you put better\_errors in the **development** section. **Do NOT run better_errors in production, or on Internet facing hosts.**
You will notice that the only machine that gets the Better Errors page is localhost, which means you get the default error page if you are developing on a remote host (or a virtually remote host, such as a Vagrant box). Obviously, the REPL is not something you want to expose to the public, but there may also be other pieces of sensitive information available in the backtrace.
To poke selective holes in this security mechanism, you can add a line like this to your startup (for example, on Rails it would be `config/environments/development.rb`)
```ruby
BetterErrors::Middleware.allow_ip! ENV['TRUSTED_IP'] if ENV['TRUSTED_IP']
```
Then run Rails like this:
```shell
TRUSTED_IP=66.68.96.220 rails s
```
Note that the `allow_ip!` is actually backed by a `Set`, so you can add more than one IP address or subnet.
**Tip:** You can find your apparent IP by hitting the old error page's "Show env dump" and looking at "REMOTE_ADDR".
**VirtualBox:** If you are using VirtualBox and are accessing the guest from your host's browser, you will need to use `allow_ip!` to see the error page.
## Usage
If you're using Rails, there's nothing else you need to do.
If you're not using Rails, you need to insert `BetterErrors::Middleware` into your middleware stack, and optionally set `BetterErrors.application_root` if you'd like Better Errors to abbreviate filenames within your application.
Here's an example using Sinatra:
```ruby
require "sinatra"
require "better_errors"
configure :development do
use BetterErrors::Middleware
BetterErrors.application_root = __dir__
end
get "/" do
raise "oops"
end
```
### Unicorn, Puma, and other multi-worker servers
Better Errors works by leaving a lot of context in server process memory. If
you're using a web server that runs multiple "workers" it's likely that a second
request (as happens when you click on a stack frame) will hit a different
worker. That worker won't have the necessary context in memory, and you'll see
a `Session Expired` message.
If this is the case for you, consider turning the number of workers to one (1)
in `development`. Another option would be to use Webrick, Mongrel, Thin,
or another single-process server as your `rails server`, when you are trying
to troubleshoot an issue in development.
## Get in touch!
If you're using better_errors, I'd love to hear from you. Drop me a line and tell me what you think!
## Contributing
1. Fork it
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create new Pull Request

@ -0,0 +1,13 @@
require "bundler/gem_tasks"
require "rspec/core/rake_task"
namespace :test do
RSpec::Core::RakeTask.new(:with_binding_of_caller)
without_task = RSpec::Core::RakeTask.new(:without_binding_of_caller)
without_task.ruby_opts = "-I spec -r without_binding_of_caller"
task :all => [:with_binding_of_caller, :without_binding_of_caller]
end
task :default => "test:all"

@ -0,0 +1,27 @@
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'better_errors/version'
Gem::Specification.new do |s|
s.name = "better_errors"
s.version = BetterErrors::VERSION
s.authors = ["Charlie Somerville"]
s.email = ["charlie@charliesomerville.com"]
s.description = %q{Provides a better error page for Rails and other Rack apps. Includes source code inspection, a live REPL and local/instance variable inspection for all stack frames.}
s.summary = %q{Better error page for Rails and other Rack apps}
s.homepage = "https://github.com/charliesome/better_errors"
s.license = "MIT"
s.files = `git ls-files`.split($/)
s.test_files = s.files.grep(%r{^(test|spec|features)/})
s.require_paths = ["lib"]
s.required_ruby_version = ">= 2.0.0"
s.add_dependency "erubis", ">= 2.6.6"
s.add_dependency "coderay", ">= 1.0.0"
# optional dependencies:
# s.add_dependency "binding_of_caller"
# s.add_dependency "pry"
end

@ -0,0 +1,146 @@
require "pp"
require "erubis"
require "coderay"
require "uri"
require "better_errors/code_formatter"
require "better_errors/error_page"
require "better_errors/middleware"
require "better_errors/raised_exception"
require "better_errors/repl"
require "better_errors/stack_frame"
require "better_errors/version"
module BetterErrors
POSSIBLE_EDITOR_PRESETS = [
{ symbols: [:emacs, :emacsclient], sniff: /emacs/i, url: "emacs://open?url=file://%{file}&line=%{line}" },
{ symbols: [:macvim, :mvim], sniff: /vim/i, url: proc { |file, line| "mvim://open?url=file://#{file}&line=#{line}" } },
{ symbols: [:sublime, :subl, :st], sniff: /subl/i, url: "subl://open?url=file://%{file}&line=%{line}" },
{ symbols: [:textmate, :txmt, :tm], sniff: /mate/i, url: "txmt://open?url=file://%{file}&line=%{line}" },
]
class << self
# The path to the root of the application. Better Errors uses this property
# to determine if a file in a backtrace should be considered an application
# frame. If you are using Better Errors with Rails, you do not need to set
# this attribute manually.
#
# @return [String]
attr_accessor :application_root
# The logger to use when logging exception details and backtraces. If you
# are using Better Errors with Rails, you do not need to set this attribute
# manually. If this attribute is `nil`, nothing will be logged.
#
# @return [Logger, nil]
attr_accessor :logger
# @private
attr_accessor :binding_of_caller_available
# @private
alias_method :binding_of_caller_available?, :binding_of_caller_available
# The ignored instance variables.
# @return [Array]
attr_accessor :ignored_instance_variables
end
@ignored_instance_variables = []
# Returns a proc, which when called with a filename and line number argument,
# returns a URL to open the filename and line in the selected editor.
#
# Generates TextMate URLs by default.
#
# BetterErrors.editor["/some/file", 123]
# # => txmt://open?url=file:///some/file&line=123
#
# @return [Proc]
def self.editor
@editor
end
# Configures how Better Errors generates open-in-editor URLs.
#
# @overload BetterErrors.editor=(sym)
# Uses one of the preset editor configurations. Valid symbols are:
#
# * `:textmate`, `:txmt`, `:tm`
# * `:sublime`, `:subl`, `:st`
# * `:macvim`
#
# @param [Symbol] sym
#
# @overload BetterErrors.editor=(str)
# Uses `str` as the format string for generating open-in-editor URLs.
#
# Use `%{file}` and `%{line}` as placeholders for the actual values.
#
# @example
# BetterErrors.editor = "my-editor://open?url=%{file}&line=%{line}"
#
# @param [String] str
#
# @overload BetterErrors.editor=(proc)
# Uses `proc` to generate open-in-editor URLs. The proc will be called
# with `file` and `line` parameters when a URL needs to be generated.
#
# Your proc should take care to escape `file` appropriately with
# `URI.encode_www_form_component` (please note that `URI.escape` is **not**
# a suitable substitute.)
#
# @example
# BetterErrors.editor = proc { |file, line|
# "my-editor://open?url=#{URI.encode_www_form_component file}&line=#{line}"
# }
#
# @param [Proc] proc
#
def self.editor=(editor)
POSSIBLE_EDITOR_PRESETS.each do |config|
if config[:symbols].include?(editor)
return self.editor = config[:url]
end
end
if editor.is_a? String
self.editor = proc { |file, line| editor % { file: URI.encode_www_form_component(file), line: line } }
else
if editor.respond_to? :call
@editor = editor
else
raise TypeError, "Expected editor to be a valid editor key, a format string or a callable."
end
end
end
# Enables experimental Pry support in the inline REPL
#
# If you encounter problems while using Pry, *please* file a bug report at
# https://github.com/charliesome/better_errors/issues
def self.use_pry!
REPL::PROVIDERS.unshift const: :Pry, impl: "better_errors/repl/pry"
end
# Automatically sniffs a default editor preset based on the EDITOR
# environment variable.
#
# @return [Symbol]
def self.default_editor
POSSIBLE_EDITOR_PRESETS.detect(-> { {} }) { |config|
ENV["EDITOR"] =~ config[:sniff]
}[:url] || :textmate
end
BetterErrors.editor = default_editor
end
begin
require "binding_of_caller"
require "better_errors/exception_extension"
BetterErrors.binding_of_caller_available = true
rescue LoadError => e
BetterErrors.binding_of_caller_available = false
end
require "better_errors/rails" if defined? Rails::Railtie

@ -0,0 +1,63 @@
module BetterErrors
# @private
class CodeFormatter
require "better_errors/code_formatter/html"
require "better_errors/code_formatter/text"
FILE_TYPES = {
".rb" => :ruby,
"" => :ruby,
".html" => :html,
".erb" => :erb,
".haml" => :haml
}
attr_reader :filename, :line, :context
def initialize(filename, line, context = 5)
@filename = filename
@line = line
@context = context
end
def output
formatted_code
rescue Errno::ENOENT, Errno::EINVAL
source_unavailable
end
def formatted_code
formatted_lines.join
end
def coderay_scanner
ext = File.extname(filename)
FILE_TYPES[ext] || :text
end
def each_line_of(lines, &blk)
line_range.zip(lines).map { |current_line, str|
yield (current_line == line), current_line, str
}
end
def highlighted_lines
CodeRay.scan(context_lines.join, coderay_scanner).div(wrap: nil).lines
end
def context_lines
range = line_range
source_lines[(range.begin - 1)..(range.end - 1)] or raise Errno::EINVAL
end
def source_lines
@source_lines ||= File.readlines(filename)
end
def line_range
min = [line - context, 1].max
max = [line + context, source_lines.count].min
min..max
end
end
end

@ -0,0 +1,26 @@
module BetterErrors
# @private
class CodeFormatter::HTML < CodeFormatter
def source_unavailable
"<p class='unavailable'>Source is not available</p>"
end
def formatted_lines
each_line_of(highlighted_lines) { |highlight, current_line, str|
class_name = highlight ? "highlight" : ""
sprintf '<pre class="%s">%s</pre>', class_name, str
}
end
def formatted_nums
each_line_of(highlighted_lines) { |highlight, current_line, str|
class_name = highlight ? "highlight" : ""
sprintf '<span class="%s">%5d</span>', class_name, current_line
}
end
def formatted_code
%{<div class="code_linenums">#{formatted_nums.join}</div><div class="code">#{super}</div>}
end
end
end

@ -0,0 +1,14 @@
module BetterErrors
# @private
class CodeFormatter::Text < CodeFormatter
def source_unavailable
"# Source is not available"
end
def formatted_lines
each_line_of(context_lines) { |highlight, current_line, str|
sprintf '%s %3d %s', (highlight ? '>' : ' '), current_line, str
}
end
end
end

@ -0,0 +1,110 @@
require "cgi"
require "json"
require "securerandom"
module BetterErrors
# @private
class ErrorPage
def self.template_path(template_name)
File.expand_path("../templates/#{template_name}.erb", __FILE__)
end
def self.template(template_name)
Erubis::EscapedEruby.new(File.read(template_path(template_name)))
end
attr_reader :exception, :env, :repls
def initialize(exception, env)
@exception = RaisedException.new(exception)
@env = env
@start_time = Time.now.to_f
@repls = []
end
def id
@id ||= SecureRandom.hex(8)
end
def render(template_name = "main")
self.class.template(template_name).result binding
end
def do_variables(opts)
index = opts["index"].to_i
@frame = backtrace_frames[index]
@var_start_time = Time.now.to_f
{ html: render("variable_info") }
end
def do_eval(opts)
index = opts["index"].to_i
code = opts["source"]
unless binding = backtrace_frames[index].frame_binding
return { error: "REPL unavailable in this stack frame" }
end
result, prompt, prefilled_input =
(@repls[index] ||= REPL.provider.new(binding)).send_input(code)
{ result: result,
prompt: prompt,
prefilled_input: prefilled_input,
highlighted_input: CodeRay.scan(code, :ruby).div(wrap: nil) }
end
def backtrace_frames
exception.backtrace
end
def application_frames
backtrace_frames.select(&:application?)
end
def first_frame
application_frames.first || backtrace_frames.first
end
private
def editor_url(frame)
BetterErrors.editor[frame.filename, frame.line]
end
def rack_session
env['rack.session']
end
def rails_params
env['action_dispatch.request.parameters']
end
def uri_prefix
env["SCRIPT_NAME"] || ""
end
def request_path
env["PATH_INFO"]
end
def html_formatted_code_block(frame)
CodeFormatter::HTML.new(frame.filename, frame.line).output
end
def text_formatted_code_block(frame)
CodeFormatter::Text.new(frame.filename, frame.line).output
end
def text_heading(char, str)
str + "\n" + char*str.size
end
def inspect_value(obj)
CGI.escapeHTML(obj.inspect)
rescue NoMethodError
"<span class='unsupported'>(object doesn't support inspect)</span>"
rescue Exception => e
"<span class='unsupported'>(exception was raised in inspect)</span>"
end
end
end

@ -0,0 +1,17 @@
module BetterErrors
module ExceptionExtension
prepend_features Exception
def set_backtrace(*)
if caller_locations.none? { |loc| loc.path == __FILE__ }
@__better_errors_bindings_stack = binding.callers.drop(1)
end
super
end
def __better_errors_bindings_stack
@__better_errors_bindings_stack || []
end
end
end

@ -0,0 +1,141 @@
require "json"
require "ipaddr"
require "set"
module BetterErrors
# Better Errors' error handling middleware. Including this in your middleware
# stack will show a Better Errors error page for exceptions raised below this
# middleware.
#
# If you are using Ruby on Rails, you do not need to manually insert this
# middleware into your middleware stack.
#
# @example Sinatra
# require "better_errors"
#
# if development?
# use BetterErrors::Middleware
# end
#
# @example Rack
# require "better_errors"
# if ENV["RACK_ENV"] == "development"
# use BetterErrors::Middleware
# end
#
class Middleware
# The set of IP addresses that are allowed to access Better Errors.
#
# Set to `{ "127.0.0.1/8", "::1/128" }` by default.
ALLOWED_IPS = Set.new
# Adds an address to the set of IP addresses allowed to access Better
# Errors.
def self.allow_ip!(addr)
ALLOWED_IPS << IPAddr.new(addr)
end
allow_ip! "127.0.0.0/8"
allow_ip! "::1/128" rescue nil # windows ruby doesn't have ipv6 support
# A new instance of BetterErrors::Middleware
#
# @param app The Rack app/middleware to wrap with Better Errors
# @param handler The error handler to use.
def initialize(app, handler = ErrorPage)
@app = app
@handler = handler
end
# Calls the Better Errors middleware
#
# @param [Hash] env
# @return [Array]
def call(env)
if allow_ip? env
better_errors_call env
else
@app.call env
end
end
private
def allow_ip?(env)
# REMOTE_ADDR is not in the rack spec, so some application servers do
# not provide it.
return true unless env["REMOTE_ADDR"] and !env["REMOTE_ADDR"].strip.empty?
ip = IPAddr.new env["REMOTE_ADDR"].split("%").first
ALLOWED_IPS.any? { |subnet| subnet.include? ip }
end
def better_errors_call(env)
case env["PATH_INFO"]
when %r{/__better_errors/(?<id>.+?)/(?<method>\w+)\z}
internal_call env, $~
when %r{/__better_errors/?\z}
show_error_page env
else
protected_app_call env
end
end
def protected_app_call(env)
@app.call env
rescue Exception => ex
@error_page = @handler.new ex, env
log_exception
show_error_page(env, ex)
end
def show_error_page(env, exception=nil)
type, content = if @error_page
if text?(env)
[ 'plain', @error_page.render('text') ]
else
[ 'html', @error_page.render ]
end
else
[ 'html', no_errors_page ]
end
status_code = 500
if defined? ActionDispatch::ExceptionWrapper
status_code = ActionDispatch::ExceptionWrapper.new(env, exception).status_code
end
[status_code, { "Content-Type" => "text/#{type}; charset=utf-8" }, [content]]
end
def text?(env)
env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest" ||
!env["HTTP_ACCEPT"].to_s.include?('html')
end
def log_exception
return unless BetterErrors.logger
message = "\n#{@error_page.exception.type} - #{@error_page.exception.message}:\n"
@error_page.backtrace_frames.each do |frame|
message << " #{frame}\n"
end
BetterErrors.logger.fatal message
end
def internal_call(env, opts)
if opts[:id] != @error_page.id
return [200, { "Content-Type" => "text/plain; charset=utf-8" }, [JSON.dump(error: "Session expired")]]
end
env["rack.input"].rewind
response = @error_page.send("do_#{opts[:method]}", JSON.parse(env["rack.input"].read))
[200, { "Content-Type" => "text/plain; charset=utf-8" }, [JSON.dump(response)]]
end
def no_errors_page
"<h1>No errors</h1><p>No errors have been recorded yet.</p><hr>" +
"<code>Better Errors v#{BetterErrors::VERSION}</code>"
end
end
end

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save