diff --git a/.rubocop.yml b/.rubocop.yml
new file mode 100644
index 0000000000..9dccdf7a32
--- /dev/null
+++ b/.rubocop.yml
@@ -0,0 +1,151 @@
+# The behavior of RuboCop can be controlled via the .rubocop.yml
+# configuration file. It makes it possible to enable/disable
+# certain cops (checks) and to alter their behavior if they accept
+# any parameters. The file can be placed either in your home
+# directory or in some project directory.
+#
+# RuboCop will start looking for the configuration file in the directory
+# where the inspected file is and continue its way up to the root directory.
+#
+# See https://github.com/rubocop-hq/rubocop/blob/master/manual/configuration.md
+AllCops:
+ TargetRubyVersion: 2.6
+ SuggestExtensions: false
+ NewCops: enable
+ Exclude:
+ - 'test/**/*'
+ - 'rack-protection/**/*'
+ - 'sinatra-contrib/**/*'
+ - vendor/bundle/**/*
+
+Layout/ExtraSpacing:
+ AllowForAlignment: true
+ AllowBeforeTrailingComments: true
+
+# Temporary disable cops because warnings are fixed
+Style/SingleLineMethods:
+ Enabled: false
+
+Style/MutableConstant:
+ Enabled: false
+
+Lint/AmbiguousBlockAssociation:
+ Enabled: false
+
+Style/CaseEquality:
+ Enabled: false
+
+Style/PerlBackrefs:
+ Enabled: false
+
+Style/Documentation:
+ Enabled: false
+
+Lint/IneffectiveAccessModifier:
+ Enabled: false
+
+Lint/RescueException:
+ Enabled: false
+
+Style/SpecialGlobalVars:
+ Enabled: false
+
+Bundler/DuplicatedGem:
+ Enabled: false
+
+Layout/HeredocIndentation:
+ Enabled: false
+
+Style/FormatStringToken:
+ Enabled: false
+
+Lint/UselessAccessModifier:
+ Enabled: false
+
+Style/ClassVars:
+ Enabled: false
+
+Lint/UselessAssignment:
+ Enabled: false
+
+Style/EmptyLiteral:
+ Enabled: false
+
+Layout/LineLength:
+ Enabled: false
+
+Metrics/MethodLength:
+ Enabled: false
+
+Metrics/AbcSize:
+ Enabled: false
+
+Metrics/CyclomaticComplexity:
+ Enabled: false
+
+Metrics/PerceivedComplexity:
+ Enabled: false
+
+Lint/SuppressedException:
+ Enabled: false
+
+Metrics/ClassLength:
+ Enabled: false
+
+Metrics/BlockLength:
+ Enabled: false
+
+Metrics/ModuleLength:
+ Enabled: false
+
+Lint/AmbiguousRegexpLiteral:
+ Enabled: false
+
+Style/AccessModifierDeclarations:
+ Enabled: false
+
+Style/ClassAndModuleChildren:
+ Enabled: false
+
+Style/EvalWithLocation:
+ Enabled: false
+
+Lint/MissingSuper:
+ Enabled: false
+
+Style/MissingRespondToMissing:
+ Enabled: false
+
+Style/MixinUsage:
+ Enabled: false
+
+Style/MultilineTernaryOperator:
+ Enabled: false
+
+Style/StructInheritance:
+ Enabled: false
+
+Style/SymbolProc:
+ Enabled: false
+
+Style/IfUnlessModifier:
+ Enabled: false
+
+Style/OptionalBooleanParameter:
+ Enabled: false
+
+Style/DocumentDynamicEvalDefinition:
+ Enabled: false
+
+Lint/ToEnumArguments:
+ Enabled: false
+
+Naming/MethodParameterName:
+ Enabled: false
+
+Naming/AccessorMethodName:
+ Enabled: false
+
+Style/SlicingWithRange:
+ Enabled: false
+
diff --git a/Gemfile b/Gemfile
index 3e9072a88c..783f91972c 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Why use bundler?
# Well, not all development dependencies install on all rubies. Moreover, `gem
# install sinatra --development` doesn't work, as it will also try to install
@@ -12,40 +14,40 @@ gemspec
gem 'rake'
rack_version = ENV['rack'].to_s
-rack_version = nil if rack_version.empty? or rack_version == 'stable'
-rack_version = {:github => 'rack/rack'} if rack_version == 'master'
+rack_version = nil if rack_version.empty? || (rack_version == 'stable')
+rack_version = { github: 'rack/rack' } if rack_version == 'master'
gem 'rack', rack_version
+gem 'minitest', '~> 5.0'
gem 'rack-test', github: 'rack/rack-test'
-gem "minitest", "~> 5.0"
+gem 'rubocop', '~> 1.32.0', require: false
gem 'yard'
-gem "rack-protection", path: "rack-protection"
-gem "sinatra-contrib", path: "sinatra-contrib"
-
+gem 'rack-protection', path: 'rack-protection'
+gem 'sinatra-contrib', path: 'sinatra-contrib'
-gem "activesupport", "~> 6.1"
+gem 'activesupport', '~> 6.1'
-gem 'redcarpet', platforms: [ :ruby ]
-gem 'rdiscount', platforms: [ :ruby ]
-gem 'puma'
-gem 'falcon', '~> 0.40', platforms: [ :ruby ]
-gem 'yajl-ruby', platforms: [ :ruby ]
-gem 'nokogiri', '> 1.5.0'
-gem 'rainbows', platforms: [ :mri ] # uses #fork
-gem 'eventmachine'
-gem 'slim', '~> 4'
-gem 'rdoc'
-gem 'kramdown'
-gem 'markaby'
gem 'asciidoctor'
-gem 'liquid'
-gem 'rabl'
gem 'builder'
+gem 'commonmarker', '~> 0.20.0', platforms: [:ruby]
gem 'erubi'
+gem 'eventmachine'
+gem 'falcon', '~> 0.40', platforms: [:ruby]
gem 'haml', '~> 5'
-gem 'commonmarker', '~> 0.20.0', platforms: [ :ruby ]
+gem 'kramdown'
+gem 'liquid'
+gem 'markaby'
+gem 'nokogiri', '> 1.5.0'
gem 'pandoc-ruby', '~> 2.0.2'
+gem 'puma'
+gem 'rabl'
+gem 'rainbows', platforms: [:mri] # uses #fork
+gem 'rdiscount', platforms: [:ruby]
+gem 'rdoc'
+gem 'redcarpet', platforms: [:ruby]
gem 'simplecov', require: false
+gem 'slim', '~> 4'
+gem 'yajl-ruby', platforms: [:ruby]
-gem 'json', platforms: [ :jruby, :mri ]
+gem 'json', platforms: %i[jruby mri]
diff --git a/Rakefile b/Rakefile
index 3fb9707f81..7bbd08ca7a 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,15 +1,17 @@
+# frozen_string_literal: true
+
require 'rake/clean'
require 'rake/testtask'
require 'fileutils'
require 'date'
-task :default => :test
-task :spec => :test
+task default: :test
+task spec: :test
-CLEAN.include "**/*.rbc"
+CLEAN.include '**/*.rbc'
def source_version
- @source_version ||= File.read(File.expand_path("VERSION", __dir__)).strip
+ @source_version ||= File.read(File.expand_path('VERSION', __dir__)).strip
end
def prev_feature
@@ -17,7 +19,8 @@ def prev_feature
end
def prev_version
- return prev_feature + '.0' if source_version.end_with? '.0'
+ return "#{prev_feature}.0" if source_version.end_with? '.0'
+
source_version.gsub(/\d+$/) { |s| s.to_i - 1 }
end
@@ -29,13 +32,15 @@ Rake::TestTask.new(:test) do |t|
t.warning = true
end
-Rake::TestTask.new(:"test:core") do |t|
- core_tests = %w[base delegator encoding extensions filter
- helpers mapped_error middleware rdoc
- readme request response result route_added_hook
- routing server settings sinatra static templates]
- t.test_files = core_tests.map {|n| "test/#{n}_test.rb"}
- t.ruby_opts = ["-r rubygems"] if defined? Gem
+Rake::TestTask.new(:'test:core') do |t|
+ core_tests = %w[
+ base delegator encoding extensions filter
+ helpers mapped_error middleware rdoc
+ readme request response result route_added_hook
+ routing server settings sinatra static templates
+ ]
+ t.test_files = core_tests.map { |n| "test/#{n}_test.rb" }
+ t.ruby_opts = ['-r rubygems'] if defined? Gem
t.warning = true
end
@@ -44,7 +49,7 @@ end
namespace :test do
desc 'Measures test coverage'
task :coverage do
- rm_f "coverage"
+ rm_f 'coverage'
ENV['COVERAGE'] = '1'
Rake::Task['test'].invoke
end
@@ -53,26 +58,26 @@ end
# Website =============================================================
desc 'Generate RDoc under doc/api'
-task 'doc' => ['doc:api']
-task('doc:api') { sh "yardoc -o doc/api" }
+task 'doc' => ['doc:api']
+task('doc:api') { sh 'yardoc -o doc/api' }
CLEAN.include 'doc/api'
# README ===============================================================
-task :add_template, [:name] do |t, args|
+task :add_template, [:name] do |_t, args|
Dir.glob('README.*') do |file|
code = File.read(file)
if code =~ /^===.*#{args.name.capitalize}/
puts "Already covered in #{file}"
else
- template = code[/===[^\n]*Liquid.*index\.liquid<\/tt>[^\n]*/m]
- if !template
- puts "Liquid not found in #{file}"
- else
+ template = code[%r{===[^\n]*Liquid.*index\.liquid[^\n]*}m]
+ if template
puts "Adding section to #{file}"
template = template.gsub(/Liquid/, args.name.capitalize).gsub(/liquid/, args.name.downcase)
code.gsub! /^(\s*===.*CoffeeScript)/, "\n" << template << "\n\\1"
- File.open(file, "w") { |f| f << code }
+ File.open(file, 'w') { |f| f << code }
+ else
+ puts "Liquid not found in #{file}"
end
end
end
@@ -80,29 +85,31 @@ end
# Thanks in announcement ===============================================
-team = ["Ryan Tomayko", "Blake Mizerany", "Simon Rozet", "Konstantin Haase", "Zachary Scott"]
-desc "list of contributors"
-task :thanks, ['release:all', :backports] do |t, a|
- a.with_defaults :release => "#{prev_version}..HEAD",
- :backports => "#{prev_feature}.0..#{prev_feature}.x"
+team = ['Ryan Tomayko', 'Blake Mizerany', 'Simon Rozet', 'Konstantin Haase', 'Zachary Scott']
+desc 'list of contributors'
+task :thanks, ['release:all', :backports] do |_t, a|
+ a.with_defaults release: "#{prev_version}..HEAD",
+ backports: "#{prev_feature}.0..#{prev_feature}.x"
+
included = `git log --format=format:"%aN\t%s" #{a.release}`.lines.map { |l| l.force_encoding('binary') }
excluded = `git log --format=format:"%aN\t%s" #{a.backports}`.lines.map { |l| l.force_encoding('binary') }
commits = (included - excluded).group_by { |c| c[/^[^\t]+/] }
authors = commits.keys.sort_by { |n| - commits[n].size } - team
- puts authors[0..-2].join(', ') << " and " << authors.last,
- "(based on commits included in #{a.release}, but not in #{a.backports})"
+ puts authors[0..-2].join(', ') << ' and ' << authors.last,
+ "(based on commits included in #{a.release}, but not in #{a.backports})"
end
-desc "list of authors"
-task :authors, [:commit_range, :format, :sep] do |t, a|
- a.with_defaults :format => "%s (%d)", :sep => ", ", :commit_range => '--all'
+desc 'list of authors'
+task :authors, [:commit_range, :format, :sep] do |_t, a|
+ a.with_defaults format: '%s (%d)', sep: ', ', commit_range: '--all'
authors = Hash.new(0)
- blake = "Blake Mizerany"
+ blake = 'Blake Mizerany'
overall = 0
mapping = {
- "blake.mizerany@gmail.com" => blake, "bmizerany" => blake,
- "a_user@mac.com" => blake, "ichverstehe" => "Harry Vangberg",
- "Wu Jiang (nouse)" => "Wu Jiang" }
+ 'blake.mizerany@gmail.com' => blake, 'bmizerany' => blake,
+ 'a_user@mac.com' => blake, 'ichverstehe' => 'Harry Vangberg',
+ 'Wu Jiang (nouse)' => 'Wu Jiang'
+ }
`git shortlog -s #{a.commit_range}`.lines.map do |line|
line = line.force_encoding 'binary' if line.respond_to? :force_encoding
num, name = line.split("\t", 2).map(&:strip)
@@ -110,18 +117,18 @@ task :authors, [:commit_range, :format, :sep] do |t, a|
overall += num.to_i
end
puts "#{overall} commits by #{authors.count} authors:"
- puts authors.sort_by { |n,c| -c }.map { |e| a.format % e }.join(a.sep)
+ puts authors.sort_by { |_n, c| -c }.map { |e| a.format % e }.join(a.sep)
end
-desc "generates TOC"
-task :toc, [:readme] do |t, a|
- a.with_defaults :readme => 'README.md'
+desc 'generates TOC'
+task :toc, [:readme] do |_t, a|
+ a.with_defaults readme: 'README.md'
def self.link(title)
title.downcase.gsub(/(?!-)\W /, '-').gsub(' ', '-').gsub(/(?!-)\W/, '')
end
- puts "* [Sinatra](#sinatra)"
+ puts '* [Sinatra](#sinatra)'
title = Regexp.new('(?<=\* )(.*)') # so Ruby 1.8 doesn't complain
File.binread(a.readme).scan(/^##.*/) do |line|
puts line.gsub(/#(?=#)/, ' ').gsub('#', '*').gsub(title) { "[#{$1}](##{link($1)})" }
@@ -132,12 +139,12 @@ end
if defined?(Gem)
GEMS_AND_ROOT_DIRECTORIES = {
- "sinatra" => ".",
- "sinatra-contrib" => "./sinatra-contrib",
- "rack-protection" => "./rack-protection"
- }
+ 'sinatra' => '.',
+ 'sinatra-contrib' => './sinatra-contrib',
+ 'rack-protection' => './rack-protection'
+ }.freeze
- def package(gem, ext='')
+ def package(gem, ext = '')
"pkg/#{gem}-#{source_version}" + ext
end
@@ -145,12 +152,12 @@ if defined?(Gem)
CLOBBER.include('pkg')
GEMS_AND_ROOT_DIRECTORIES.each do |gem, directory|
- file package(gem, '.gem') => ["pkg/", "#{directory + '/' + gem}.gemspec"] do |f|
+ file package(gem, '.gem') => ['pkg/', "#{"#{directory}/#{gem}"}.gemspec"] do |f|
sh "cd #{directory} && gem build #{gem}.gemspec"
- mv directory + "/" + File.basename(f.name), f.name
+ mv "#{directory}/#{File.basename(f.name)}", f.name
end
- file package(gem, '.tar.gz') => ["pkg/"] do |f|
+ file package(gem, '.tar.gz') => ['pkg/'] do |f|
sh <<-SH
git archive \
--prefix=#{gem}-#{source_version}/ \
@@ -161,29 +168,29 @@ if defined?(Gem)
end
namespace :package do
- GEMS_AND_ROOT_DIRECTORIES.each do |gem, directory|
+ GEMS_AND_ROOT_DIRECTORIES.each do |gem, _directory|
desc "Build #{gem} packages"
task gem => %w[.gem .tar.gz].map { |e| package(gem, e) }
end
- desc "Build all packages"
- task :all => GEMS_AND_ROOT_DIRECTORIES.keys
+ desc 'Build all packages'
+ task all: GEMS_AND_ROOT_DIRECTORIES.keys
end
namespace :install do
- GEMS_AND_ROOT_DIRECTORIES.each do |gem, directory|
+ GEMS_AND_ROOT_DIRECTORIES.each do |gem, _directory|
desc "Build and install #{gem} as local gem"
task gem => package(gem, '.gem') do
sh "gem install #{package(gem, '.gem')}"
end
end
- desc "Build and install all of the gems as local gems"
- task :all => GEMS_AND_ROOT_DIRECTORIES.keys
+ desc 'Build and install all of the gems as local gems'
+ task all: GEMS_AND_ROOT_DIRECTORIES.keys
end
namespace :release do
- GEMS_AND_ROOT_DIRECTORIES.each do |gem, directory|
+ GEMS_AND_ROOT_DIRECTORIES.each do |gem, _directory|
desc "Release #{gem} as a package"
task gem => "package:#{gem}" do
sh <<-SH
@@ -193,7 +200,7 @@ if defined?(Gem)
end
end
- desc "Commits the version to github repository"
+ desc 'Commits the version to github repository'
task :commit_version do
%w[
lib/sinatra
@@ -212,7 +219,7 @@ if defined?(Gem)
SH
end
- desc "Release all gems as packages"
- task :all => [:test, :commit_version] + GEMS_AND_ROOT_DIRECTORIES.keys
+ desc 'Release all gems as packages'
+ task all: %i[test commit_version] + GEMS_AND_ROOT_DIRECTORIES.keys
end
end
diff --git a/examples/chat.rb b/examples/chat.rb
index 4d03bd1c42..9e5f8f895d 100755
--- a/examples/chat.rb
+++ b/examples/chat.rb
@@ -1,16 +1,18 @@
#!/usr/bin/env ruby -I ../lib -I lib
-# coding: utf-8
+# frozen_string_literal: true
+
require_relative 'rainbows'
+
require 'sinatra'
set :server, :rainbows
connections = []
get '/' do
halt erb(:login) unless params[:user]
- erb :chat, :locals => { :user => params[:user].gsub(/\W/, '') }
+ erb :chat, locals: { user: params[:user].gsub(/\W/, '') }
end
-get '/stream', :provides => 'text/event-stream' do
+get '/stream', provides: 'text/event-stream' do
stream :keep_open do |out|
connections << out
out.callback { connections.delete(out) }
diff --git a/examples/rainbows.rb b/examples/rainbows.rb
index 895e19a2be..4dab26444e 100644
--- a/examples/rainbows.rb
+++ b/examples/rainbows.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'rainbows'
module Rack
@@ -8,7 +10,7 @@ def self.run(app, **options)
listeners: ["#{options[:Host]}:#{options[:Port]}"],
worker_processes: 1,
timeout: 30,
- config_file: ::File.expand_path('rainbows.conf', __dir__),
+ config_file: ::File.expand_path('rainbows.conf', __dir__)
}
::Rainbows::HttpServer.new(app, rainbows_options).start.join
diff --git a/examples/simple.rb b/examples/simple.rb
index 2697f94bf2..31b3fcf96a 100755
--- a/examples/simple.rb
+++ b/examples/simple.rb
@@ -1,3 +1,5 @@
#!/usr/bin/env ruby -I ../lib -I lib
+# frozen_string_literal: true
+
require 'sinatra'
get('/') { 'this is a simple app' }
diff --git a/examples/stream.ru b/examples/stream.ru
index 74af0a6148..ee1615d65e 100644
--- a/examples/stream.ru
+++ b/examples/stream.ru
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# this example does *not* work properly with WEBrick
#
# run *one* of these:
diff --git a/lib/sinatra.rb b/lib/sinatra.rb
index 68261380cb..fabdc41b57 100644
--- a/lib/sinatra.rb
+++ b/lib/sinatra.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'sinatra/main'
enable :inline_templates
diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb
index 4947e6b15b..63a90cd4e4 100644
--- a/lib/sinatra/base.rb
+++ b/lib/sinatra/base.rb
@@ -1,4 +1,3 @@
-# coding: utf-8
# frozen_string_literal: true
# external dependencies
@@ -10,7 +9,6 @@
require 'mustermann/regular'
# stdlib dependencies
-require 'thread'
require 'time'
require 'uri'
@@ -23,19 +21,20 @@ module Sinatra
# The request object. See Rack::Request for more info:
# http://rubydoc.info/github/rack/rack/master/Rack/Request
class Request < Rack::Request
- HEADER_PARAM = /\s*[\w.]+=(?:[\w.]+|"(?:[^"\\]|\\.)*")?\s*/
- HEADER_VALUE_WITH_PARAMS = /(?:(?:\w+|\*)\/(?:\w+(?:\.|\-|\+)?|\*)*)\s*(?:;#{HEADER_PARAM})*/
+ HEADER_PARAM = /\s*[\w.]+=(?:[\w.]+|"(?:[^"\\]|\\.)*")?\s*/.freeze
+ HEADER_VALUE_WITH_PARAMS = %r{(?:(?:\w+|\*)/(?:\w+(?:\.|-|\+)?|\*)*)\s*(?:;#{HEADER_PARAM})*}.freeze
# Returns an array of acceptable media types for the response
def accept
- @env['sinatra.accept'] ||= begin
- if @env.include? 'HTTP_ACCEPT' and @env['HTTP_ACCEPT'].to_s != ''
- @env['HTTP_ACCEPT'].to_s.scan(HEADER_VALUE_WITH_PARAMS).
- map! { |e| AcceptEntry.new(e) }.sort
- else
- [AcceptEntry.new('*/*')]
- end
- end
+ @env['sinatra.accept'] ||= if @env.include?('HTTP_ACCEPT') && (@env['HTTP_ACCEPT'].to_s != '')
+ @env['HTTP_ACCEPT']
+ .to_s
+ .scan(HEADER_VALUE_WITH_PARAMS)
+ .map! { |e| AcceptEntry.new(e) }
+ .sort
+ else
+ [AcceptEntry.new('*/*')]
+ end
end
def accept?(type)
@@ -44,8 +43,10 @@ def accept?(type)
def preferred_type(*types)
return accept.first if types.empty?
+
types.flatten!
return types.first if accept.empty?
+
accept.detect do |accept_header|
type = types.detect { |t| MimeTypeEntry.new(t).accepts?(accept_header) }
return type if type
@@ -55,23 +56,23 @@ def preferred_type(*types)
alias secure? ssl?
def forwarded?
- @env.include? "HTTP_X_FORWARDED_HOST"
+ @env.include? 'HTTP_X_FORWARDED_HOST'
end
def safe?
- get? or head? or options? or trace?
+ get? || head? || options? || trace?
end
def idempotent?
- safe? or put? or delete? or link? or unlink?
+ safe? || put? || delete? || link? || unlink?
end
def link?
- request_method == "LINK"
+ request_method == 'LINK'
end
def unlink?
- request_method == "UNLINK"
+ request_method == 'UNLINK'
end
def params
@@ -95,17 +96,17 @@ def initialize(entry)
@entry = entry
@type = entry[/[^;]+/].delete(' ')
- @params = Hash[params]
+ @params = params.to_h
@q = @params.delete('q') { 1.0 }.to_f
end
def <=>(other)
- other.priority <=> self.priority
+ other.priority <=> priority
end
def priority
# We sort in descending order; better matches should be higher.
- [ @q, -@type.count('*'), @params.size ]
+ [@q, -@type.count('*'), @params.size]
end
def to_str
@@ -117,7 +118,7 @@ def to_s(full = false)
end
def respond_to?(*args)
- super or to_str.respond_to?(*args)
+ super || to_str.respond_to?(*args)
end
def method_missing(*args, &block)
@@ -136,7 +137,7 @@ def initialize(entry)
end
@type = entry[/[^;]+/].delete(' ')
- @params = Hash[params]
+ @params = params.to_h
end
def accepts?(entry)
@@ -150,7 +151,7 @@ def to_str
def matches_params?(params)
return true if @params.empty?
- params.all? { |k,v| !@params.has_key?(k) || @params[k] == v }
+ params.all? { |k, v| !@params.key?(k) || @params[k] == v }
end
end
end
@@ -160,7 +161,7 @@ def matches_params?(params)
# http://rubydoc.info/github/rack/rack/master/Rack/Response
# http://rubydoc.info/github/rack/rack/master/Rack/Response/Helpers
class Response < Rack::Response
- DROP_BODY_RESPONSES = [204, 304]
+ DROP_BODY_RESPONSES = [204, 304].freeze
def body=(value)
value = value.body while Rack::Response === value
@@ -175,8 +176,8 @@ def finish
result = body
if drop_content_info?
- headers.delete "Content-Length"
- headers.delete "Content-Type"
+ headers.delete 'Content-Length'
+ headers.delete 'Content-Type'
end
if drop_body?
@@ -187,7 +188,7 @@ def finish
if calculate_content_length?
# if some other code has already set Content-Length, don't muck with it
# currently, this would be the static file-handler
- headers["Content-Length"] = body.map(&:bytesize).reduce(0, :+).to_s
+ headers['Content-Length'] = body.map(&:bytesize).reduce(0, :+).to_s
end
[status, headers, result]
@@ -196,11 +197,11 @@ def finish
private
def calculate_content_length?
- headers["Content-Type"] and not headers["Content-Length"] and Array === body
+ headers['Content-Type'] && !headers['Content-Length'] && (Array === body)
end
def drop_content_info?
- informational? or drop_body?
+ informational? || drop_body?
end
def drop_body?
@@ -215,8 +216,10 @@ def drop_body?
# still be able to run.
class ExtendedRack < Struct.new(:app)
def call(env)
- result, callback = app.call(env), env['async.callback']
- return result unless callback and async?(*result)
+ result = app.call(env)
+ callback = env['async.callback']
+ return result unless callback && async?(*result)
+
after_response { callback.call result }
setup_close(env, *result)
throw :async
@@ -224,20 +227,23 @@ def call(env)
private
- def setup_close(env, status, headers, body)
- return unless body.respond_to? :close and env.include? 'async.close'
+ def setup_close(env, _status, _headers, body)
+ return unless body.respond_to?(:close) && env.include?('async.close')
+
env['async.close'].callback { body.close }
env['async.close'].errback { body.close }
end
def after_response(&block)
- raise NotImplementedError, "only supports EventMachine at the moment" unless defined? EventMachine
+ raise NotImplementedError, 'only supports EventMachine at the moment' unless defined? EventMachine
+
EventMachine.next_tick(&block)
end
- def async?(status, headers, body)
+ def async?(status, _headers, body)
return true if status == -1
- body.respond_to? :callback and body.respond_to? :errback
+
+ body.respond_to?(:callback) && body.respond_to?(:errback)
end
end
@@ -249,7 +255,7 @@ def call(env)
end
superclass.class_eval do
- alias call_without_check call unless method_defined? :call_without_check
+ alias_method :call_without_check, :call unless method_defined? :call_without_check
def call(env)
env['sinatra.commonlogger'] = true
call_without_check(env)
@@ -257,14 +263,14 @@ def call(env)
end
end
- class Error < StandardError #:nodoc:
+ class Error < StandardError # :nodoc:
end
- class BadRequest < Error #:nodoc:
+ class BadRequest < Error # :nodoc:
def http_status; 400 end
end
- class NotFound < Error #:nodoc:
+ class NotFound < Error # :nodoc:
def http_status; 404 end
end
@@ -296,7 +302,7 @@ def block.each; yield(call) end
# Halt processing and redirect to the URI provided.
def redirect(uri, *args)
- if env['HTTP_VERSION'] == 'HTTP/1.1' and env["REQUEST_METHOD"] != 'GET'
+ if (env['HTTP_VERSION'] == 'HTTP/1.1') && (env['REQUEST_METHOD'] != 'GET')
status 303
else
status 302
@@ -311,18 +317,19 @@ def redirect(uri, *args)
# Generates the absolute URI for a given path in the app.
# Takes Rack routers and reverse proxies into account.
def uri(addr = nil, absolute = true, add_script_name = true)
- return addr if addr =~ /\A[a-z][a-z0-9\+\.\-]*:/i
+ return addr if addr =~ /\A[a-z][a-z0-9+.\-]*:/i
+
uri = [host = String.new]
if absolute
host << "http#{'s' if request.secure?}://"
- if request.forwarded? or request.port != (request.secure? ? 443 : 80)
- host << request.host_with_port
- else
- host << request.host
- end
+ host << if request.forwarded? || (request.port != (request.secure? ? 443 : 80))
+ request.host_with_port
+ else
+ request.host
+ end
end
uri << request.script_name.to_s if add_script_name
- uri << (addr ? addr : request.path_info).to_s
+ uri << (addr || request.path_info).to_s
File.join uri
end
@@ -331,7 +338,10 @@ def uri(addr = nil, absolute = true, add_script_name = true)
# Halt processing and return the error status provided.
def error(code, body = nil)
- code, body = 500, code.to_str if code.respond_to? :to_str
+ if code.respond_to? :to_str
+ body = code.to_str
+ code = 500
+ end
response.body = body unless body.nil?
halt code
end
@@ -366,11 +376,13 @@ def mime_type(type)
# extension.
def content_type(type = nil, params = {})
return response['Content-Type'] unless type
+
default = params.delete :default
mime_type = mime_type(type) || default
- fail "Unknown media type: %p" % type if mime_type.nil?
+ raise format('Unknown media type: %p', type) if mime_type.nil?
+
mime_type = mime_type.dup
- unless params.include? :charset or settings.add_charset.all? { |p| not p === mime_type }
+ unless params.include?(:charset) || settings.add_charset.all? { |p| !(p === mime_type) }
params[:charset] = params.delete('charset') || settings.default_encoding
end
params.delete :charset if mime_type.include? 'charset'
@@ -388,23 +400,23 @@ def content_type(type = nil, params = {})
# instructing the user agents to prompt to save.
def attachment(filename = nil, disposition = :attachment)
response['Content-Disposition'] = disposition.to_s.dup
- if filename
- params = '; filename="%s"' % File.basename(filename)
- response['Content-Disposition'] << params
- ext = File.extname(filename)
- content_type(ext) unless response['Content-Type'] or ext.empty?
- end
+ return unless filename
+
+ params = format('; filename="%s"', File.basename(filename))
+ response['Content-Disposition'] << params
+ ext = File.extname(filename)
+ content_type(ext) unless response['Content-Type'] || ext.empty?
end
# Use the contents of the file at +path+ as the response body.
def send_file(path, opts = {})
- if opts[:type] or not response['Content-Type']
- content_type opts[:type] || File.extname(path), :default => 'application/octet-stream'
+ if opts[:type] || !response['Content-Type']
+ content_type opts[:type] || File.extname(path), default: 'application/octet-stream'
end
disposition = opts[:disposition]
filename = opts[:filename]
- disposition = :attachment if disposition.nil? and filename
+ disposition = :attachment if disposition.nil? && filename
filename = path if filename.nil?
attachment(filename, disposition) if disposition
@@ -413,7 +425,7 @@ def send_file(path, opts = {})
file = Rack::File.new(File.dirname(settings.app_file))
result = file.serving(request, path)
- result[1].each { |k,v| headers[k] ||= v }
+ result[1].each { |k, v| headers[k] ||= v }
headers['Content-Length'] = result[1]['Content-Length']
opts[:status] &&= Integer(opts[:status])
halt (opts[:status] || result[0]), result[2]
@@ -434,12 +446,16 @@ def self.schedule(*) yield end
def self.defer(*) yield end
def initialize(scheduler = self.class, keep_open = false, &back)
- @back, @scheduler, @keep_open = back.to_proc, scheduler, keep_open
- @callbacks, @closed = [], false
+ @back = back.to_proc
+ @scheduler = scheduler
+ @keep_open = keep_open
+ @callbacks = []
+ @closed = false
end
def close
return if closed?
+
@closed = true
@scheduler.schedule { @callbacks.each { |c| c.call } }
end
@@ -463,6 +479,7 @@ def <<(data)
def callback(&block)
return yield if closed?
+
@callbacks << block
end
@@ -496,18 +513,18 @@ def stream(keep_open = false)
# See RFC 2616 / 14.9 for more on standard cache control directives:
# http://tools.ietf.org/html/rfc2616#section-14.9.1
def cache_control(*values)
- if values.last.kind_of?(Hash)
+ if values.last.is_a?(Hash)
hash = values.pop
- hash.reject! { |k, v| v == false }
+ hash.reject! { |_k, v| v == false }
hash.reject! { |k, v| values << k if v == true }
else
hash = {}
end
- values.map! { |value| value.to_s.tr('_','-') }
+ values.map! { |value| value.to_s.tr('_', '-') }
hash.each do |key, value|
key = key.to_s.tr('_', '-')
- value = value.to_i if ['max-age', 's-maxage'].include? key
+ value = value.to_i if %w[max-age s-maxage].include? key
values << "#{key}=#{value}"
end
@@ -524,7 +541,7 @@ def cache_control(*values)
# => Expires: Mon, 08 Jun 2009 08:50:17 GMT
#
def expires(amount, *values)
- values << {} unless values.last.kind_of?(Hash)
+ values << {} unless values.last.is_a?(Hash)
if amount.is_a? Integer
time = Time.now + amount.to_i
@@ -534,7 +551,7 @@ def expires(amount, *values)
max_age = time - Time.now
end
- values.last.merge!(:max_age => max_age) { |key, v1, v2| v1 || v2 }
+ values.last.merge!(max_age: max_age) { |_key, v1, v2| v1 || v2 }
cache_control(*values)
response['Expires'] = time.httpdate
@@ -549,17 +566,18 @@ def expires(amount, *values)
# with a '304 Not Modified' response.
def last_modified(time)
return unless time
+
time = time_for time
response['Last-Modified'] = time.httpdate
return if env['HTTP_IF_NONE_MATCH']
- if status == 200 and env['HTTP_IF_MODIFIED_SINCE']
+ if (status == 200) && env['HTTP_IF_MODIFIED_SINCE']
# compare based on seconds since epoch
since = Time.httpdate(env['HTTP_IF_MODIFIED_SINCE']).to_i
halt 304 if since >= time.to_i
end
- if (success? or status == 412) and env['HTTP_IF_UNMODIFIED_SINCE']
+ if (success? || (status == 412)) && env['HTTP_IF_UNMODIFIED_SINCE']
# compare based on seconds since epoch
since = Time.httpdate(env['HTTP_IF_UNMODIFIED_SINCE']).to_i
halt 412 if since < time.to_i
@@ -567,7 +585,7 @@ def last_modified(time)
rescue ArgumentError
end
- ETAG_KINDS = [:strong, :weak]
+ ETAG_KINDS = %i[strong weak].freeze
# Set the response entity tag (HTTP 'ETag' header) and halt if conditional
# GET matches. The +value+ argument is an identifier that uniquely
# identifies the current version of the resource. The +kind+ argument
@@ -579,27 +597,31 @@ def last_modified(time)
# GET or HEAD, a '304 Not Modified' response is sent.
def etag(value, options = {})
# Before touching this code, please double check RFC 2616 14.24 and 14.26.
- options = {:kind => options} unless Hash === options
+ options = { kind: options } unless Hash === options
kind = options[:kind] || :strong
new_resource = options.fetch(:new_resource) { request.post? }
unless ETAG_KINDS.include?(kind)
- raise ArgumentError, ":strong or :weak expected"
+ raise ArgumentError, ':strong or :weak expected'
end
- value = '"%s"' % value
+ value = format('"%s"', value)
value = "W/#{value}" if kind == :weak
response['ETag'] = value
- if success? or status == 304
- if etag_matches? env['HTTP_IF_NONE_MATCH'], new_resource
- halt(request.safe? ? 304 : 412)
- end
+ return unless success? || status == 304
- if env['HTTP_IF_MATCH']
- halt 412 unless etag_matches? env['HTTP_IF_MATCH'], new_resource
- end
+ if etag_matches?(env['HTTP_IF_NONE_MATCH'], new_resource)
+ halt(request.safe? ? 304 : 412)
+ end
+
+ if env['HTTP_IF_MATCH']
+ return if etag_matches?(env['HTTP_IF_MATCH'], new_resource)
+
+ halt 412
end
+
+ nil
end
# Sugar for redirect (example: redirect back)
@@ -652,8 +674,8 @@ def time_for(value)
else
value.to_time
end
- rescue ArgumentError => boom
- raise boom
+ rescue ArgumentError => e
+ raise e
rescue Exception
raise ArgumentError, "unable to convert #{value.inspect} to a Time object"
end
@@ -663,11 +685,13 @@ def time_for(value)
# Helper method checking if a ETag value list includes the current ETag.
def etag_matches?(list, new_resource = request.post?)
return !new_resource if list == '*'
+
list.to_s.split(/\s*,\s*/).include? response['ETag']
end
def with_params(temp_params)
- original, @params = @params, temp_params
+ original = @params
+ @params = temp_params
yield
ensure
@params = original if original
@@ -770,24 +794,27 @@ def find_template(views, name, engine)
# logic shared between builder and nokogiri
def render_ruby(engine, template, options = {}, locals = {}, &block)
- options, template = template, nil if template.is_a?(Hash)
- template = Proc.new { block } if template.nil?
+ if template.is_a?(Hash)
+ options = template
+ template = nil
+ end
+ template = proc { block } if template.nil?
render engine, template, options, locals
end
def render(engine, data, options = {}, locals = {}, &block)
# merge app-level options
engine_options = settings.respond_to?(engine) ? settings.send(engine) : {}
- options.merge!(engine_options) { |key, v1, v2| v1 }
+ options.merge!(engine_options) { |_key, v1, _v2| v1 }
# extract generic options
locals = options.delete(:locals) || locals || {}
- views = options.delete(:views) || settings.views || "./views"
+ views = options.delete(:views) || settings.views || './views'
layout = options[:layout]
layout = false if layout.nil? && options.include?(:layout)
eat_errors = layout.nil?
- layout = engine_options[:layout] if layout.nil? or (layout == true && engine_options[:layout] != false)
- layout = @default_layout if layout.nil? or layout == true
+ layout = engine_options[:layout] if layout.nil? || (layout == true && engine_options[:layout] != false)
+ layout = @default_layout if layout.nil? || (layout == true)
layout_options = options.delete(:layout_options) || {}
content_type = options.delete(:default_content_type)
content_type = options.delete(:content_type) || content_type
@@ -812,8 +839,9 @@ def render(engine, data, options = {}, locals = {}, &block)
# render layout
if layout
- options = options.merge(:views => views, :layout => false, :eat_errors => eat_errors, :scope => scope).
- merge!(layout_options)
+ extra_options = { views: views, layout: false, eat_errors: eat_errors, scope: scope }
+ options = options.merge(extra_options).merge!(layout_options)
+
catch(:layout_missing) { return render(layout_engine, layout, options, locals) { output } }
end
@@ -838,12 +866,13 @@ def compile_template(engine, data, options, views)
@preferred_extension = engine.to_s
find_template(views, data, template) do |file|
path ||= file # keep the initial path rather than the last one
- if found = File.exist?(file)
+ found = File.exist?(file)
+ if found
path = file
break
end
end
- throw :layout_missing if eat_errors and not found
+ throw :layout_missing if eat_errors && !found
template.new(path, 1, options)
end
end
@@ -860,7 +889,8 @@ def compile_template(engine, data, options, views)
def compile_block_template(template, options, &body)
first_location = caller_locations.first
- path, line = first_location.path, first_location.lineno
+ path = first_location.path
+ line = first_location.lineno
path = options[:path] || path
line = options[:line] || line
template.new(path, line.to_i, options, &body)
@@ -878,7 +908,7 @@ class Base
attr_accessor :app, :env, :request, :response, :params
attr_reader :template_cache
- def initialize(app = nil, **kwargs)
+ def initialize(app = nil, **_kwargs)
super()
@app = app
@template_cache = Tilt::Cache.new
@@ -905,7 +935,7 @@ def call!(env) # :nodoc:
unless @response['Content-Type']
if Array === body && body[0].respond_to?(:content_type)
content_type body[0].content_type
- elsif default = settings.default_content_type
+ elsif (default = settings.default_content_type)
content_type default
end
end
@@ -939,7 +969,8 @@ def pass(&block)
# Forward the request to the downstream app -- middleware only.
def forward
- fail "downstream app not set" unless @app.respond_to? :call
+ raise 'downstream app not set' unless @app.respond_to? :call
+
status, headers, body = @app.call env
@response.status = status
@response.body = body
@@ -961,18 +992,18 @@ def filter!(type, base = settings, &block)
# Run routes defined on the class and all superclasses.
def route!(base = settings, pass_block = nil)
- if routes = base.routes[@request.request_method]
- routes.each do |pattern, conditions, block|
- response.delete_header('Content-Type') unless @pinned_response
+ routes = base.routes[@request.request_method]
- returned_pass_block = process_route(pattern, conditions) do |*args|
- env['sinatra.route'] = "#{@request.request_method} #{pattern}"
- route_eval { block[*args] }
- end
+ routes&.each do |pattern, conditions, block|
+ response.delete_header('Content-Type') unless @pinned_response
- # don't wipe out pass_block in superclass
- pass_block = returned_pass_block if returned_pass_block
+ returned_pass_block = process_route(pattern, conditions) do |*args|
+ env['sinatra.route'] = "#{@request.request_method} #{pattern}"
+ route_eval { block[*args] }
end
+
+ # don't wipe out pass_block in superclass
+ pass_block = returned_pass_block if returned_pass_block
end
# Run routes defined in superclass.
@@ -996,15 +1027,17 @@ def route_eval
# Returns pass block.
def process_route(pattern, conditions, block = nil, values = [])
route = @request.path_info
- route = '/' if route.empty? and not settings.empty_path_info?
+ route = '/' if route.empty? && !settings.empty_path_info?
route = route[0..-2] if !settings.strict_paths? && route != '/' && route.end_with?('/')
- return unless params = pattern.params(route)
- params.delete("ignore") # TODO: better params handling, maybe turn it into "smart" object or detect changes
+ params = pattern.params(route)
+ return unless params
+
+ params.delete('ignore') # TODO: better params handling, maybe turn it into "smart" object or detect changes
force_encoding(params)
- @params = @params.merge(params) { |k, v1, v2| v2 || v1 } if params.any?
+ @params = @params.merge(params) { |_k, v1, v2| v2 || v1 } if params.any?
- regexp_exists = pattern.is_a?(Mustermann::Regular) || (pattern.respond_to?(:patterns) && pattern.patterns.any? {|subpattern| subpattern.is_a?(Mustermann::Regular)} )
+ regexp_exists = pattern.is_a?(Mustermann::Regular) || (pattern.respond_to?(:patterns) && pattern.patterns.any? { |subpattern| subpattern.is_a?(Mustermann::Regular) })
if regexp_exists
captures = pattern.match(route).captures.map { |c| URI_INSTANCE.unescape(c) if c }
values += captures
@@ -1017,7 +1050,7 @@ def process_route(pattern, conditions, block = nil, values = [])
conditions.each { |c| throw :pass if c.bind(self).call == false }
block ? block[self, values] : yield(self, values)
end
- rescue
+ rescue StandardError
@env['sinatra.error.params'] = @params
raise
ensure
@@ -1031,35 +1064,35 @@ def process_route(pattern, conditions, block = nil, values = [])
# a NotFound exception. Subclasses can override this method to perform
# custom route miss logic.
def route_missing
- if @app
- forward
- else
- raise NotFound
- end
+ raise NotFound unless @app
+
+ forward
end
# Attempt to serve static files from public directory. Throws :halt when
# a matching file is found, returns nil otherwise.
def static!(options = {})
return if (public_dir = settings.public_folder).nil?
+
path = "#{public_dir}#{URI_INSTANCE.unescape(request.path_info)}"
return unless valid_path?(path)
path = File.expand_path(path)
- return unless path.start_with?(File.expand_path(public_dir) + '/')
+ return unless path.start_with?("#{File.expand_path(public_dir)}/")
+
return unless File.file?(path)
env['sinatra.static_file'] = path
cache_control(*settings.static_cache_control) if settings.static_cache_control?
- send_file path, options.merge(:disposition => nil)
+ send_file path, options.merge(disposition: nil)
end
# Run the block with 'throw :halt' support and apply result to the response.
- def invoke
- res = catch(:halt) { yield }
+ def invoke(&block)
+ res = catch(:halt, &block)
- res = [res] if Integer === res or String === res
- if Array === res and Integer === res.first
+ res = [res] if (Integer === res) || (String === res)
+ if (Array === res) && (Integer === res.first)
res = res.dup
status(res.shift)
body(res.pop)
@@ -1075,6 +1108,7 @@ def dispatch!
# Avoid passing frozen string in force_encoding
@params.merge!(@request.params).each do |key, val|
next unless val.respond_to?(:force_encoding)
+
val = val.dup if val.frozen?
@params[key] = force_encoding(val)
end
@@ -1086,42 +1120,43 @@ def dispatch!
end
route!
end
- rescue ::Exception => boom
- invoke { handle_exception!(boom) }
+ rescue ::Exception => e
+ invoke { handle_exception!(e) }
ensure
begin
filter! :after unless env['sinatra.static_file']
- rescue ::Exception => boom
- invoke { handle_exception!(boom) } unless @env['sinatra.error']
+ rescue ::Exception => e
+ invoke { handle_exception!(e) } unless @env['sinatra.error']
end
end
# Error handling during requests.
def handle_exception!(boom)
- if error_params = @env['sinatra.error.params']
- @params = @params.merge(error_params)
- end
+ error_params = @env['sinatra.error.params']
+
+ @params = @params.merge(error_params) if error_params
+
@env['sinatra.error'] = boom
- http_status = if boom.kind_of? Sinatra::Error
- if boom.respond_to? :http_status
- boom.http_status
- elsif settings.use_code? && boom.respond_to?(:code)
- boom.code
- end
- end
+ http_status = if boom.is_a? Sinatra::Error
+ if boom.respond_to? :http_status
+ boom.http_status
+ elsif settings.use_code? && boom.respond_to?(:code)
+ boom.code
+ end
+ end
- http_status = 500 unless http_status && http_status.between?(400, 599)
+ http_status = 500 unless http_status&.between?(400, 599)
status(http_status)
if server_error?
dump_errors! boom if settings.dump_errors?
- raise boom if settings.show_exceptions? and settings.show_exceptions != :after_handler
+ raise boom if settings.show_exceptions? && (settings.show_exceptions != :after_handler)
elsif not_found?
headers['X-Cascade'] = 'pass' if settings.x_cascade?
end
- if res = error_block!(boom.class, boom) || error_block!(status, boom)
+ if (res = error_block!(boom.class, boom) || error_block!(status, boom))
return res
end
@@ -1130,12 +1165,14 @@ def handle_exception!(boom)
body Rack::Utils.escape_html(boom.message)
else
content_type 'text/html'
- body '
' + (not_found? ? 'Not Found' : 'Bad Request') + '
'
+ body "#{not_found? ? 'Not Found' : 'Bad Request'}
"
end
end
return unless server_error?
- raise boom if settings.raise_errors? or settings.show_exceptions?
+
+ raise boom if settings.raise_errors? || settings.show_exceptions?
+
error_block! Exception, boom
end
@@ -1143,7 +1180,10 @@ def handle_exception!(boom)
def error_block!(key, *block_params)
base = settings
while base.respond_to?(:errors)
- next base = base.superclass unless args_array = base.errors[key]
+ args_array = base.errors[key]
+
+ next base = base.superclass unless args_array
+
args_array.reverse_each do |args|
first = args == args_array.first
args += [block_params]
@@ -1151,25 +1191,26 @@ def error_block!(key, *block_params)
return resp unless resp.nil? && !first
end
end
- return false unless key.respond_to? :superclass and key.superclass < Exception
+ return false unless key.respond_to?(:superclass) && (key.superclass < Exception)
+
error_block!(key.superclass, *block_params)
end
def dump_errors!(boom)
- msg = ["#{Time.now.strftime("%Y-%m-%d %H:%M:%S")} - #{boom.class} - #{boom.message}:", *boom.backtrace].join("\n\t")
+ msg = ["#{Time.now.strftime('%Y-%m-%d %H:%M:%S')} - #{boom.class} - #{boom.message}:", *boom.backtrace].join("\n\t")
@env['rack.errors'].puts(msg)
end
class << self
CALLERS_TO_IGNORE = [ # :nodoc:
- /\/sinatra(\/(base|main|show_exceptions))?\.rb$/, # all sinatra code
- /lib\/tilt.*\.rb$/, # all tilt code
+ %r{/sinatra(/(base|main|show_exceptions))?\.rb$}, # all sinatra code
+ %r{lib/tilt.*\.rb$}, # all tilt code
/^\(.*\)$/, # generated code
- /rubygems\/(custom|core_ext\/kernel)_require\.rb$/, # rubygems require hacks
+ %r{rubygems/(custom|core_ext/kernel)_require\.rb$}, # rubygems require hacks
/active_support/, # active_support require hacks
- /bundler(\/(?:runtime|inline))?\.rb/, # bundler require hacks
+ %r{bundler(/(?:runtime|inline))?\.rb}, # bundler require hacks
/= 1.9.2
- ]
+ ].freeze
attr_reader :routes, :filters, :templates, :errors
@@ -1178,17 +1219,17 @@ class << self
def reset!
@conditions = []
@routes = {}
- @filters = {:before => [], :after => []}
+ @filters = { before: [], after: [] }
@errors = {}
@middleware = []
@prototype = nil
@extensions = []
- if superclass.respond_to?(:templates)
- @templates = Hash.new { |hash, key| superclass.templates[key] }
- else
- @templates = {}
- end
+ @templates = if superclass.respond_to?(:templates)
+ Hash.new { |_hash, key| superclass.templates[key] }
+ else
+ {}
+ end
end
# Extension modules registered on this class and all superclasses.
@@ -1212,16 +1253,21 @@ def middleware
# Sets an option to the given value. If the value is a proc,
# the proc will be called every time the option is accessed.
def set(option, value = (not_set = true), ignore_setter = false, &block)
- raise ArgumentError if block and !not_set
- value, not_set = block, false if block
+ raise ArgumentError if block && !not_set
+
+ if block
+ value = block
+ not_set = false
+ end
if not_set
raise ArgumentError unless option.respond_to?(:each)
- option.each { |k,v| set(k, v) }
+
+ option.each { |k, v| set(k, v) }
return self
end
- if respond_to?("#{option}=") and not ignore_setter
+ if respond_to?("#{option}=") && !ignore_setter
return __send__("#{option}=", value)
end
@@ -1260,7 +1306,7 @@ def disable(*opts)
# class, or an HTTP status code to specify which errors should be
# handled.
def error(*codes, &block)
- args = compile! "ERROR", /.*/, block
+ args = compile! 'ERROR', /.*/, block
codes = codes.flat_map(&method(:Array))
codes << Exception if codes.empty?
codes << Sinatra::NotFound if codes.include?(404)
@@ -1286,7 +1332,7 @@ def layout(name = :layout, &block)
# Load embedded templates from the file; uses the caller's __FILE__
# when no file is specified.
def inline_templates=(file = nil)
- file = (file.nil? || file == true) ? (caller_files.first || File.expand_path($0)) : file
+ file = (caller_files.first || File.expand_path($0)) if file.nil? || file == true
begin
io = ::IO.respond_to?(:binread) ? ::IO.binread(file) : ::IO.read(file)
@@ -1295,23 +1341,24 @@ def inline_templates=(file = nil)
app, data = nil
end
- if data
- if app and app =~ /([^\n]*\n)?#[^\n]*coding: *(\S+)/m
- encoding = $2
- else
- encoding = settings.default_encoding
- end
- lines = app.count("\n") + 1
- template = nil
- force_encoding data, encoding
- data.each_line do |line|
- lines += 1
- if line =~ /^@@\s*(.*\S)\s*$/
- template = force_encoding(String.new, encoding)
- templates[$1.to_sym] = [template, file, lines]
- elsif template
- template << line
- end
+ return unless data
+
+ encoding = if app && app =~ /([^\n]*\n)?#[^\n]*coding: *(\S+)/m
+ $2
+ else
+ settings.default_encoding
+ end
+
+ lines = app.count("\n") + 1
+ template = nil
+ force_encoding data, encoding
+ data.each_line do |line|
+ lines += 1
+ if line =~ /^@@\s*(.*\S)\s*$/
+ template = force_encoding(String.new, encoding)
+ templates[$1.to_sym] = [template, file, lines]
+ elsif template
+ template << line
end
end
end
@@ -1320,8 +1367,10 @@ def inline_templates=(file = nil)
def mime_type(type, value = nil)
return type if type.nil?
return type.to_s if type.to_s.include?('/')
- type = ".#{type}" unless type.to_s[0] == ?.
+
+ type = ".#{type}" unless type.to_s[0] == '.'
return Rack::Mime.mime_type(type, nil) unless value
+
Rack::Mime::MIME_TYPES[type] = value
end
@@ -1330,7 +1379,7 @@ def mime_type(type, value = nil)
# mime_types :js # => ['application/javascript', 'text/javascript']
def mime_types(type)
type = mime_type type
- type =~ /^application\/(xml|javascript)$/ ? [type, "text/#$1"] : [type]
+ type =~ %r{^application/(xml|javascript)$} ? [type, "text/#{$1}"] : [type]
end
# Define a before filter; runs before all requests within the same
@@ -1359,7 +1408,7 @@ def condition(name = "#{caller.first[/`.*'/]} condition", &block)
end
def public=(value)
- warn ":public is no longer used to avoid overloading Module#public, use :public_folder or :public_dir instead"
+ warn ':public is no longer used to avoid overloading Module#public, use :public_folder or :public_dir instead'
set(:public_folder, value)
end
@@ -1381,14 +1430,21 @@ def get(path, opts = {}, &block)
route('HEAD', path, opts, &block)
end
- def put(path, opts = {}, &bk) route 'PUT', path, opts, &bk end
- def post(path, opts = {}, &bk) route 'POST', path, opts, &bk end
- def delete(path, opts = {}, &bk) route 'DELETE', path, opts, &bk end
- def head(path, opts = {}, &bk) route 'HEAD', path, opts, &bk end
- def options(path, opts = {}, &bk) route 'OPTIONS', path, opts, &bk end
- def patch(path, opts = {}, &bk) route 'PATCH', path, opts, &bk end
- def link(path, opts = {}, &bk) route 'LINK', path, opts, &bk end
- def unlink(path, opts = {}, &bk) route 'UNLINK', path, opts, &bk end
+ def put(path, opts = {}, &block) route 'PUT', path, opts, &block end
+
+ def post(path, opts = {}, &block) route 'POST', path, opts, &block end
+
+ def delete(path, opts = {}, &block) route 'DELETE', path, opts, &block end
+
+ def head(path, opts = {}, &block) route 'HEAD', path, opts, &block end
+
+ def options(path, opts = {}, &block) route 'OPTIONS', path, opts, &block end
+
+ def patch(path, opts = {}, &block) route 'PATCH', path, opts, &block end
+
+ def link(path, opts = {}, &block) route 'LINK', path, opts, &block end
+
+ def unlink(path, opts = {}, &block) route 'UNLINK', path, opts, &block end
# Makes the methods defined in the block and in the Modules given
# in `extensions` available to the handlers and templates
@@ -1428,37 +1484,39 @@ def use(middleware, *args, &block)
# Stop the self-hosted server if running.
def quit!
return unless running?
+
# Use Thin's hard #stop! if available, otherwise just #stop.
running_server.respond_to?(:stop!) ? running_server.stop! : running_server.stop
- $stderr.puts "== Sinatra has ended his set (crowd applauds)" unless suppress_messages?
+ warn '== Sinatra has ended his set (crowd applauds)' unless suppress_messages?
set :running_server, nil
set :handler_name, nil
end
- alias_method :stop!, :quit!
+ alias stop! quit!
# Run the Sinatra app as a self-hosted server using
# Puma, Falcon, Mongrel, or WEBrick (in that order). If given a block, will call
# with the constructed handler once we have taken the stage.
def run!(options = {}, &block)
return if running?
+
set options
handler = Rack::Handler.pick(server)
handler_name = handler.name.gsub(/.*::/, '')
server_settings = settings.respond_to?(:server_settings) ? settings.server_settings : {}
- server_settings.merge!(:Port => port, :Host => bind)
+ server_settings.merge!(Port: port, Host: bind)
begin
start_server(handler, server_settings, handler_name, &block)
rescue Errno::EADDRINUSE
- $stderr.puts "== Someone is already performing on port #{port}!"
+ warn "== Someone is already performing on port #{port}!"
raise
ensure
quit!
end
end
- alias_method :start!, :run!
+ alias start! run!
# Check whether the self-hosted server is running or not.
def running?
@@ -1476,8 +1534,8 @@ def prototype
# Create a new instance of the class fronted by its middleware
# pipeline. The object is guaranteed to respond to #call but may not be
# an instance of the class new was called on.
- def new(*args, &bk)
- instance = new!(*args, &bk)
+ def new(*args, &block)
+ instance = new!(*args, &block)
Wrapper.new(build(instance).to_app, instance)
end
ruby2_keywords :new if respond_to?(:ruby2_keywords, true)
@@ -1512,7 +1570,7 @@ def start_server(handler, server_settings, handler_name)
# Run the instance we created:
handler.run(self, **server_settings) do |server|
unless suppress_messages?
- $stderr.puts "== Sinatra (v#{Sinatra::VERSION}) has taken the stage on #{port} for #{environment} with backup from #{handler_name}"
+ warn "== Sinatra (v#{Sinatra::VERSION}) has taken the stage on #{port} for #{environment} with backup from #{handler_name}"
end
setup_traps
@@ -1529,18 +1587,18 @@ def suppress_messages?
end
def setup_traps
- if traps?
- at_exit { quit! }
+ return unless traps?
- [:INT, :TERM].each do |signal|
- old_handler = trap(signal) do
- quit!
- old_handler.call if old_handler.respond_to?(:call)
- end
- end
+ at_exit { quit! }
- set :traps, false
+ %i[INT TERM].each do |signal|
+ old_handler = trap(signal) do
+ quit!
+ old_handler.call if old_handler.respond_to?(:call)
+ end
end
+
+ set :traps, false
end
# Dynamically defines a method on settings.
@@ -1568,18 +1626,21 @@ def user_agent(pattern)
end
end
end
- alias_method :agent, :user_agent
+ alias agent user_agent
# Condition for matching mimetypes. Accepts file extensions.
def provides(*types)
types.map! { |t| mime_types(t) }
types.flatten!
condition do
- if type = response['Content-Type']
- types.include? type or types.include? type[/^[^;]+/]
- elsif type = request.preferred_type(types)
- params = (type.respond_to?(:params) ? type.params : {})
- content_type(type, params)
+ response_content_type = response['Content-Type']
+ preferred_type = request.preferred_type(types)
+
+ if response_content_type
+ types.include?(response_content_type) || types.include?(response_content_type[/^[^;]+/])
+ elsif preferred_type
+ params = (preferred_type.respond_to?(:params) ? preferred_type.params : {})
+ content_type(preferred_type, params)
true
else
false
@@ -1588,7 +1649,7 @@ def provides(*types)
end
def route(verb, path, options = {}, &block)
- enable :empty_path_info if path == "" and empty_path_info.nil?
+ enable :empty_path_info if path == '' && empty_path_info.nil?
signature = compile!(verb, path, block, **options)
(@routes[verb] ||= []) << signature
invoke_hook(:route_added, verb, path, block)
@@ -1617,12 +1678,13 @@ def compile!(verb, path, block, **options)
pattern = compile(path, route_mustermann_opts)
method_name = "#{verb} #{path}"
unbound_method = generate_method(method_name, &block)
- conditions, @conditions = @conditions, []
- wrapper = block.arity != 0 ?
- proc { |a, p| unbound_method.bind(a).call(*p) } :
- proc { |a, p| unbound_method.bind(a).call }
+ conditions = @conditions
+ @conditions = []
+ wrapper = block.arity.zero? ?
+ proc { |a, _p| unbound_method.bind(a).call } :
+ proc { |a, p| unbound_method.bind(a).call(*p) }
- [ pattern, conditions, wrapper ]
+ [pattern, conditions, wrapper]
end
def compile(path, route_mustermann_opts = {})
@@ -1640,7 +1702,7 @@ def setup_default_middleware(builder)
end
def setup_middleware(builder)
- middleware.each { |c,a,b| builder.use(c, *a, &b) }
+ middleware.each { |c, a, b| builder.use(c, *a, &b) }
end
def setup_logging(builder)
@@ -1670,9 +1732,10 @@ def setup_custom_logger(builder)
def setup_protection(builder)
return unless protection?
+
options = Hash === protection ? protection.dup : {}
options = {
- img_src: "'self' data:",
+ img_src: "'self' data:",
font_src: "'self'"
}.merge options
@@ -1686,6 +1749,7 @@ def setup_protection(builder)
def setup_sessions(builder)
return unless sessions?
+
options = {}
options[:secret] = session_secret if session_secret?
options.merge! sessions.to_hash if sessions.respond_to? :to_hash
@@ -1714,9 +1778,9 @@ def warn(message)
# Like Kernel#caller but excluding certain magic entries
def cleaned_caller(keep = 3)
- caller(1).
- map! { |line| line.split(/:(?=\d|in )/, 3)[0,keep] }.
- reject { |file, *_| CALLERS_TO_IGNORE.any? { |pattern| file =~ pattern } }
+ caller(1)
+ .map! { |line| line.split(/:(?=\d|in )/, 3)[0, keep] }
+ .reject { |file, *_| CALLERS_TO_IGNORE.any? { |pattern| file =~ pattern } }
end
end
@@ -1724,6 +1788,7 @@ def cleaned_caller(keep = 3)
# which is UTF-8 by default
def self.force_encoding(data, encoding = default_encoding)
return if data == settings || data.is_a?(Tempfile)
+
if data.respond_to? :force_encoding
data.force_encoding(encoding).encode!
elsif data.respond_to? :each_value
@@ -1734,24 +1799,26 @@ def self.force_encoding(data, encoding = default_encoding)
data
end
- def force_encoding(*args) settings.force_encoding(*args) end
+ def force_encoding(*args)
+ settings.force_encoding(*args)
+ end
reset!
set :environment, (ENV['APP_ENV'] || ENV['RACK_ENV'] || :development).to_sym
- set :raise_errors, Proc.new { test? }
- set :dump_errors, Proc.new { !test? }
- set :show_exceptions, Proc.new { development? }
+ set :raise_errors, proc { test? }
+ set :dump_errors, proc { !test? }
+ set :show_exceptions, proc { development? }
set :sessions, false
set :session_store, Rack::Protection::EncryptedCookie
set :logging, false
set :protection, true
set :method_override, false
set :use_code, false
- set :default_encoding, "utf-8"
+ set :default_encoding, 'utf-8'
set :x_cascade, true
set :add_charset, %w[javascript xml xhtml+xml].map { |t| "application/#{t}" }
- settings.add_charset << /^text\//
+ settings.add_charset << %r{^text/}
set :mustermann_opts, {}
set :default_content_type, 'text/html'
@@ -1761,12 +1828,12 @@ def force_encoding(*args) settings.force_encoding(*args) end
set :session_secret, SecureRandom.hex(64)
rescue LoadError, NotImplementedError
# SecureRandom raises a NotImplementedError if no random device is available
- set :session_secret, "%064x" % Kernel.rand(2**256-1)
+ set :session_secret, format('%064x', Kernel.rand((2**256) - 1))
end
class << self
- alias_method :methodoverride?, :method_override?
- alias_method :methodoverride=, :method_override=
+ alias methodoverride? method_override?
+ alias methodoverride= method_override=
end
set :run, false # start server via at-exit hook?
@@ -1774,7 +1841,7 @@ class << self
set :handler_name, nil
set :traps, true
set :server, %w[HTTP webrick]
- set :bind, Proc.new { development? ? 'localhost' : '0.0.0.0' }
+ set :bind, proc { development? ? 'localhost' : '0.0.0.0' }
set :port, Integer(ENV['PORT'] && !ENV['PORT'].empty? ? ENV['PORT'] : 4567)
set :quiet, false
@@ -1792,14 +1859,14 @@ class << self
set :strict_paths, true
set :app_file, nil
- set :root, Proc.new { app_file && File.expand_path(File.dirname(app_file)) }
- set :views, Proc.new { root && File.join(root, 'views') }
- set :reload_templates, Proc.new { development? }
+ set :root, proc { app_file && File.expand_path(File.dirname(app_file)) }
+ set :views, proc { root && File.join(root, 'views') }
+ set :reload_templates, proc { development? }
set :lock, false
set :threaded, true
- set :public_folder, Proc.new { root && File.join(root, 'public') }
- set :static, Proc.new { public_folder && File.exist?(public_folder) }
+ set :public_folder, proc { root && File.join(root, 'public') }
+ set :static, proc { public_folder && File.exist?(public_folder) }
set :static_cache_control, false
error ::Exception do
@@ -1818,7 +1885,7 @@ class << self
error NotFound do
content_type 'text/html'
- if self.class == Sinatra::Application
+ if instance_of?(Sinatra::Application)
code = <<-RUBY.gsub(/^ {12}/, '')
#{request.request_method.downcase} '#{request.path_info}' do
"Hello World"
@@ -1833,11 +1900,11 @@ class #{self.class}
end
RUBY
- file = settings.app_file.to_s.sub(settings.root.to_s, '').sub(/^\//, '')
+ file = settings.app_file.to_s.sub(settings.root.to_s, '').sub(%r{^/}, '')
code = "# in #{file}\n#{code}" unless file.empty?
end
- (<<-HTML).gsub(/^ {10}/, '')
+ <<-HTML.gsub(/^ {10}/, '')
@@ -1849,7 +1916,7 @@ class #{self.class}
Sinatra doesn’t know this ditty.
-
+
Try this:
#{Rack::Utils.escape_html(code)}
@@ -1869,12 +1936,12 @@ class #{self.class}
# top-level. Subclassing Sinatra::Base is highly recommended for
# modular applications.
class Application < Base
- set :logging, Proc.new { !test? }
+ set :logging, proc { !test? }
set :method_override, true
- set :run, Proc.new { !test? }
+ set :run, proc { !test? }
set :app_file, nil
- def self.register(*extensions, &block) #:nodoc:
+ def self.register(*extensions, &block) # :nodoc:
added_methods = extensions.flat_map(&:public_instance_methods)
Delegator.delegate(*added_methods)
super(*extensions, &block)
@@ -1884,11 +1951,12 @@ def self.register(*extensions, &block) #:nodoc:
# Sinatra delegation mixin. Mixing this module into an object causes all
# methods to be delegated to the Sinatra::Application class. Used primarily
# at the top-level.
- module Delegator #:nodoc:
+ module Delegator # :nodoc:
def self.delegate(*methods)
methods.each do |method_name|
define_method(method_name) do |*args, &block|
return super(*args, &block) if respond_to? method_name
+
Delegator.target.send(method_name, *args, &block)
end
# ensure keyword argument passing is compatible with ruby >= 2.7
@@ -1911,7 +1979,8 @@ class << self
class Wrapper
def initialize(stack, instance)
- @stack, @instance = stack, instance
+ @stack = stack
+ @instance = instance
end
def settings
diff --git a/lib/sinatra/indifferent_hash.rb b/lib/sinatra/indifferent_hash.rb
index 6951527472..a3a96d5c4d 100644
--- a/lib/sinatra/indifferent_hash.rb
+++ b/lib/sinatra/indifferent_hash.rb
@@ -79,7 +79,7 @@ def []=(key, value)
super(convert_key(key), convert_value(value))
end
- alias_method :store, :[]=
+ alias store []=
def key(value)
super(convert_value(value))
@@ -89,20 +89,21 @@ def key?(key)
super(convert_key(key))
end
- alias_method :has_key?, :key?
- alias_method :include?, :key?
- alias_method :member?, :key?
+ alias has_key? key?
+ alias include? key?
+ alias member? key?
def value?(value)
super(convert_value(value))
end
- alias_method :has_value?, :value?
+ alias has_value? value?
def delete(key)
super(convert_key(key))
end
+ # Added in Ruby 2.3
def dig(key, *other_keys)
super(convert_key(key), *other_keys)
end
@@ -141,7 +142,7 @@ def merge!(*other_hashes)
self
end
- alias_method :update, :merge!
+ alias update merge!
def merge(*other_hashes, &block)
dup.merge!(*other_hashes, &block)
@@ -171,17 +172,19 @@ def transform_keys!
def select(*args, &block)
return to_enum(:select) unless block_given?
+
dup.tap { |hash| hash.select!(*args, &block) }
end
def reject(*args, &block)
return to_enum(:reject) unless block_given?
+
dup.tap { |hash| hash.reject!(*args, &block) }
end
def compact
dup.tap(&:compact!)
- end if method_defined?(:compact) # Added in Ruby 2.4
+ end
private
diff --git a/lib/sinatra/main.rb b/lib/sinatra/main.rb
index e4231c30f9..bca7fc1a09 100644
--- a/lib/sinatra/main.rb
+++ b/lib/sinatra/main.rb
@@ -1,47 +1,49 @@
+# frozen_string_literal: true
+
module Sinatra
- ParamsConfig = {}
+ PARAMS_CONFIG = {}
if ARGV.any?
require 'optparse'
- parser = OptionParser.new { |op|
- op.on('-p port', 'set the port (default is 4567)') { |val| ParamsConfig[:port] = Integer(val) }
- op.on('-s server', 'specify rack server/handler') { |val| ParamsConfig[:server] = val }
- op.on('-q', 'turn on quiet mode (default is off)') { ParamsConfig[:quiet] = true }
- op.on('-x', 'turn on the mutex lock (default is off)') { ParamsConfig[:lock] = true }
+ parser = OptionParser.new do |op|
+ op.on('-p port', 'set the port (default is 4567)') { |val| PARAMS_CONFIG[:port] = Integer(val) }
+ op.on('-s server', 'specify rack server/handler') { |val| PARAMS_CONFIG[:server] = val }
+ op.on('-q', 'turn on quiet mode (default is off)') { PARAMS_CONFIG[:quiet] = true }
+ op.on('-x', 'turn on the mutex lock (default is off)') { PARAMS_CONFIG[:lock] = true }
op.on('-e env', 'set the environment (default is development)') do |val|
ENV['RACK_ENV'] = val
- ParamsConfig[:environment] = val.to_sym
+ PARAMS_CONFIG[:environment] = val.to_sym
end
op.on('-o addr', "set the host (default is (env == 'development' ? 'localhost' : '0.0.0.0'))") do |val|
- ParamsConfig[:bind] = val
+ PARAMS_CONFIG[:bind] = val
end
- }
+ end
begin
parser.parse!(ARGV.dup)
- rescue => evar
- ParamsConfig[:optparse_error] = evar
+ rescue StandardError => e
+ PARAMS_CONFIG[:optparse_error] = e
end
end
require 'sinatra/base'
class Application < Base
-
# we assume that the first file that requires 'sinatra' is the
# app_file. all other path related options are calculated based
# on this path by default.
set :app_file, caller_files.first || $0
- set :run, Proc.new { File.expand_path($0) == File.expand_path(app_file) }
+ set :run, proc { File.expand_path($0) == File.expand_path(app_file) }
if run? && ARGV.any?
- error = ParamsConfig.delete(:optparse_error)
+ error = PARAMS_CONFIG.delete(:optparse_error)
raise error if error
- ParamsConfig.each { |k, v| set k, v }
+
+ PARAMS_CONFIG.each { |k, v| set k, v }
end
end
- remove_const(:ParamsConfig)
+ remove_const(:PARAMS_CONFIG)
at_exit { Application.run! if $!.nil? && Application.run? }
end
diff --git a/lib/sinatra/show_exceptions.rb b/lib/sinatra/show_exceptions.rb
index de468c0a4f..db847ffab7 100644
--- a/lib/sinatra/show_exceptions.rb
+++ b/lib/sinatra/show_exceptions.rb
@@ -12,6 +12,7 @@ module Sinatra
class ShowExceptions < Rack::ShowExceptions
@@eats_errors = Object.new
def @@eats_errors.flush(*) end
+
def @@eats_errors.puts(*) end
def initialize(app)
@@ -21,23 +22,24 @@ def initialize(app)
def call(env)
@app.call(env)
rescue Exception => e
- errors, env["rack.errors"] = env["rack.errors"], @@eats_errors
+ errors = env['rack.errors']
+ env['rack.errors'] = @@eats_errors
if prefers_plain_text?(env)
- content_type = "text/plain"
+ content_type = 'text/plain'
body = dump_exception(e)
else
- content_type = "text/html"
+ content_type = 'text/html'
body = pretty(env, e)
end
- env["rack.errors"] = errors
+ env['rack.errors'] = errors
[
500,
{
- "Content-Type" => content_type,
- "Content-Length" => body.bytesize.to_s
+ 'Content-Type' => content_type,
+ 'Content-Length' => body.bytesize.to_s
},
[body]
]
@@ -49,27 +51,27 @@ def template
private
- def bad_request?(e)
- Sinatra::BadRequest === e
+ def bad_request?(exception)
+ Sinatra::BadRequest === exception
end
def prefers_plain_text?(env)
- !(Request.new(env).preferred_type("text/plain","text/html") == "text/html") &&
- [/curl/].index { |item| item =~ env["HTTP_USER_AGENT"] }
+ Request.new(env).preferred_type('text/plain', 'text/html') != 'text/html' &&
+ [/curl/].index { |item| item =~ env['HTTP_USER_AGENT'] }
end
def frame_class(frame)
if frame.filename =~ %r{lib/sinatra.*\.rb}
- "framework"
+ 'framework'
elsif (defined?(Gem) && frame.filename.include?(Gem.dir)) ||
frame.filename =~ %r{/bin/(\w+)\z}
- "system"
+ 'system'
else
- "app"
+ 'app'
end
end
-TEMPLATE = ERB.new <<-HTML # :nodoc:
+ TEMPLATE = ERB.new <<-HTML # :nodoc:
@@ -357,6 +359,6 @@ def frame_class(frame)
-HTML
+ HTML
end
end
diff --git a/lib/sinatra/version.rb b/lib/sinatra/version.rb
index 2c1a44fad8..7a21c28c89 100644
--- a/lib/sinatra/version.rb
+++ b/lib/sinatra/version.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Sinatra
VERSION = '3.0.0'
end
diff --git a/rack-protection/Gemfile b/rack-protection/Gemfile
index f9631bf2fc..293cdb402d 100644
--- a/rack-protection/Gemfile
+++ b/rack-protection/Gemfile
@@ -1,11 +1,13 @@
-source "https://rubygems.org"
+# frozen_string_literal: true
+
+source 'https://rubygems.org'
# encoding: utf-8
gem 'rake'
rack_version = ENV['rack'].to_s
-rack_version = nil if rack_version.empty? or rack_version == 'stable'
-rack_version = {:github => 'rack/rack'} if rack_version == 'master'
+rack_version = nil if rack_version.empty? || (rack_version == 'stable')
+rack_version = { github: 'rack/rack' } if rack_version == 'master'
gem 'rack', rack_version
gem 'sinatra', path: '..'
diff --git a/rack-protection/Rakefile b/rack-protection/Rakefile
index 5737bad66d..4ccffd5ffa 100644
--- a/rack-protection/Rakefile
+++ b/rack-protection/Rakefile
@@ -1,14 +1,15 @@
-# encoding: utf-8
+# frozen_string_literal: true
+
$LOAD_PATH.unshift File.expand_path('lib', __dir__)
begin
require 'bundler'
Bundler::GemHelper.install_tasks
rescue LoadError => e
- $stderr.puts e
+ warn e
end
-desc "run specs"
+desc 'run specs'
task(:spec) { ruby '-S rspec' }
namespace :doc do
@@ -16,38 +17,39 @@ namespace :doc do
Dir.glob 'lib/rack/protection/*.rb' do |file|
excluded_files = %w[lib/rack/protection/base.rb lib/rack/protection/version.rb]
next if excluded_files.include?(file)
+
doc = File.read(file)[/^ module Protection(\n)+( #[^\n]*\n)*/m].scan(/^ *#(?!#) ?(.*)\n/).join("\n")
- file = "doc/#{file[4..-4].tr("/_", "-")}.rdoc"
- Dir.mkdir "doc" unless File.directory? "doc"
+ file = "doc/#{file[4..-4].tr('/_', '-')}.rdoc"
+ Dir.mkdir 'doc' unless File.directory? 'doc'
puts "writing #{file}"
- File.open(file, "w") { |f| f << doc }
+ File.open(file, 'w') { |f| f << doc }
end
end
task :index do
- doc = File.read("README.md")
- file = "doc/rack-protection-readme.md"
- Dir.mkdir "doc" unless File.directory? "doc"
+ doc = File.read('README.md')
+ file = 'doc/rack-protection-readme.md'
+ Dir.mkdir 'doc' unless File.directory? 'doc'
puts "writing #{file}"
- File.open(file, "w") { |f| f << doc }
+ File.open(file, 'w') { |f| f << doc }
end
- task :all => [:readmes, :index]
+ task all: %i[readmes index]
end
-desc "generate documentation"
-task :doc => 'doc:all'
+desc 'generate documentation'
+task doc: 'doc:all'
-desc "generate gemspec"
+desc 'generate gemspec'
task 'rack-protection.gemspec' do
require 'rack/protection/version'
content = File.binread 'rack-protection.gemspec'
# fetch data
fields = {
- :authors => `git shortlog -sn`.force_encoding('utf-8').scan(/[^\d\s].*/),
- :email => ["mail@zzak.io", "konstantin.haase@gmail.com"],
- :files => %w(License README.md Rakefile Gemfile rack-protection.gemspec) + Dir['lib/**/*']
+ authors: `git shortlog -sn`.force_encoding('utf-8').scan(/[^\d\s].*/),
+ email: ['mail@zzak.io', 'konstantin.haase@gmail.com'],
+ files: %w[License README.md Rakefile Gemfile rack-protection.gemspec] + Dir['lib/**/*']
}
# insert data
@@ -67,6 +69,6 @@ task 'rack-protection.gemspec' do
File.open('rack-protection.gemspec', 'w') { |f| f << content }
end
-task :gemspec => 'rack-protection.gemspec'
-task :default => :spec
-task :test => :spec
+task gemspec: 'rack-protection.gemspec'
+task default: :spec
+task test: :spec
diff --git a/rack-protection/lib/rack-protection.rb b/rack-protection/lib/rack-protection.rb
deleted file mode 100644
index 1633086e2c..0000000000
--- a/rack-protection/lib/rack-protection.rb
+++ /dev/null
@@ -1 +0,0 @@
-require "rack/protection"
diff --git a/rack-protection/lib/rack/protection.rb b/rack-protection/lib/rack/protection.rb
index 1be25e4870..21d2ec9268 100644
--- a/rack-protection/lib/rack/protection.rb
+++ b/rack-protection/lib/rack/protection.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'rack/protection/version'
require 'rack'
@@ -29,7 +31,7 @@ def self.new(app, options = {})
use_these = Array options[:use]
if options.fetch(:without_session, false)
- except += [:session_hijacking, :remote_token]
+ except += %i[session_hijacking remote_token]
end
Rack::Builder.new do
diff --git a/rack-protection/lib/rack/protection/authenticity_token.rb b/rack-protection/lib/rack/protection/authenticity_token.rb
index c55a40654c..f5c8db62d4 100644
--- a/rack-protection/lib/rack/protection/authenticity_token.rb
+++ b/rack-protection/lib/rack/protection/authenticity_token.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'rack/protection'
require 'securerandom'
require 'openssl'
@@ -19,7 +21,7 @@ module Protection
#
# It is not OOTB-compatible with the {rack-csrf}[https://rubygems.org/gems/rack_csrf] gem.
# For that, the following patch needs to be applied:
- #
+ #
# Rack::Protection::AuthenticityToken.default_options(key: "csrf.token", authenticity_param: "_csrf")
#
# == Options
@@ -95,12 +97,12 @@ module Protection
class AuthenticityToken < Base
TOKEN_LENGTH = 32
- default_options :authenticity_param => 'authenticity_token',
- :key => :csrf,
- :allow_if => nil
+ default_options authenticity_param: 'authenticity_token',
+ key: :csrf,
+ allow_if: nil
def self.token(session, path: nil, method: :post)
- self.new(nil).mask_authenticity_token(session, path: path, method: method)
+ new(nil).mask_authenticity_token(session, path: path, method: method)
end
def self.random_token
@@ -114,8 +116,8 @@ def accepts?(env)
safe?(env) ||
valid_token?(env, env['HTTP_X_CSRF_TOKEN']) ||
valid_token?(env, Request.new(env).params[options[:authenticity_param]]) ||
- ( options[:allow_if] && options[:allow_if].call(env) )
- rescue
+ options[:allow_if]&.call(env)
+ rescue StandardError
false
end
@@ -123,10 +125,10 @@ def mask_authenticity_token(session, path: nil, method: :post)
set_token(session)
token = if path && method
- per_form_token(session, path, method)
- else
- global_token(session)
- end
+ per_form_token(session, path, method)
+ else
+ global_token(session)
+ end
mask_token(token)
end
@@ -185,7 +187,7 @@ def unmask_token(masked_token)
# value and decrypt it
token_length = masked_token.length / 2
one_time_pad = masked_token[0...token_length]
- encrypted_token = masked_token[token_length..-1]
+ encrypted_token = masked_token[token_length..]
xor_byte_strings(one_time_pad, encrypted_token)
end
@@ -207,8 +209,7 @@ def compare_with_global_token(token, session)
def compare_with_per_form_token(token, session, request)
secure_compare(token,
- per_form_token(session, request.path.chomp('/'), request.request_method)
- )
+ per_form_token(session, request.path.chomp('/'), request.request_method))
end
def real_token(session)
@@ -233,7 +234,7 @@ def decode_token(token)
def token_hmac(session, identifier)
OpenSSL::HMAC.digest(
- OpenSSL::Digest::SHA256.new,
+ OpenSSL::Digest.new('SHA256'),
real_token(session),
identifier
)
diff --git a/rack-protection/lib/rack/protection/base.rb b/rack-protection/lib/rack/protection/base.rb
index 83e724069b..a1314adb82 100644
--- a/rack-protection/lib/rack/protection/base.rb
+++ b/rack-protection/lib/rack/protection/base.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'rack/protection'
require 'rack/utils'
require 'digest'
@@ -8,12 +10,12 @@ module Rack
module Protection
class Base
DEFAULT_OPTIONS = {
- :reaction => :default_reaction, :logging => true,
- :message => 'Forbidden', :encryptor => Digest::SHA1,
- :session_key => 'rack.session', :status => 403,
- :allow_empty_referrer => true,
- :report_key => "protection.failed",
- :html_types => %w[text/html application/xhtml text/xml application/xml]
+ reaction: :default_reaction, logging: true,
+ message: 'Forbidden', encryptor: Digest::SHA1,
+ session_key: 'rack.session', status: 403,
+ allow_empty_referrer: true,
+ report_key: 'protection.failed',
+ html_types: %w[text/html application/xhtml text/xml application/xml]
}
attr_reader :app, :options
@@ -31,7 +33,8 @@ def default_options
end
def initialize(app, options = {})
- @app, @options = app, default_options.merge(options)
+ @app = app
+ @options = default_options.merge(options)
end
def safe?(env)
@@ -52,24 +55,26 @@ def call(env)
def react(env)
result = send(options[:reaction], env)
- result if Array === result and result.size == 3
+ result if (Array === result) && (result.size == 3)
end
def warn(env, message)
return unless options[:logging]
+
l = options[:logger] || env['rack.logger'] || ::Logger.new(env['rack.errors'])
l.warn(message)
end
def instrument(env)
- return unless i = options[:instrumenter]
+ return unless (i = options[:instrumenter])
+
env['rack.protection.attack'] = self.class.name.split('::').last.downcase
i.instrument('rack.protection', env)
end
def deny(env)
warn env, "attack prevented by #{self.class}"
- [options[:status], {'Content-Type' => 'text/plain'}, [options[:message]]]
+ [options[:status], { 'Content-Type' => 'text/plain' }, [options[:message]]]
end
def report(env)
@@ -83,7 +88,8 @@ def session?(env)
def session(env)
return env[options[:session_key]] if session? env
- fail "you need to set up a session middleware *before* #{self.class}"
+
+ raise "you need to set up a session middleware *before* #{self.class}"
end
def drop_session(env)
@@ -92,7 +98,8 @@ def drop_session(env)
def referrer(env)
ref = env['HTTP_REFERER'].to_s
- return if !options[:allow_empty_referrer] and ref.empty?
+ return if !options[:allow_empty_referrer] && ref.empty?
+
URI.parse(ref).host || Request.new(env).host
rescue URI::InvalidURIError
end
@@ -102,7 +109,7 @@ def origin(env)
end
def random_string(secure = defined? SecureRandom)
- secure ? SecureRandom.hex(16) : "%032x" % rand(2**128-1)
+ secure ? SecureRandom.hex(16) : '%032x' % rand((2**128) - 1)
rescue NotImplementedError
random_string false
end
@@ -118,8 +125,9 @@ def secure_compare(a, b)
alias default_reaction deny
def html?(headers)
- return false unless header = headers.detect { |k,v| k.downcase == 'content-type' }
- options[:html_types].include? header.last[/^\w+\/\w+/]
+ return false unless (header = headers.detect { |k, _v| k.downcase == 'content-type' })
+
+ options[:html_types].include? header.last[%r{^\w+/\w+}]
end
end
end
diff --git a/rack-protection/lib/rack/protection/content_security_policy.rb b/rack-protection/lib/rack/protection/content_security_policy.rb
index 91eea925ef..32d8ac70fa 100644
--- a/rack-protection/lib/rack/protection/content_security_policy.rb
+++ b/rack-protection/lib/rack/protection/content_security_policy.rb
@@ -1,4 +1,5 @@
-# -*- coding: utf-8 -*-
+# frozen_string_literal: true
+
require 'rack/protection'
module Rack
@@ -38,16 +39,16 @@ module Protection
class ContentSecurityPolicy < Base
default_options default_src: "'self'", report_only: false
- DIRECTIVES = %i(base_uri child_src connect_src default_src
+ DIRECTIVES = %i[base_uri child_src connect_src default_src
font_src form_action frame_ancestors frame_src
img_src manifest_src media_src object_src
plugin_types referrer reflected_xss report_to
report_uri require_sri_for sandbox script_src
style_src worker_src webrtc_src navigate_to
- prefetch_src).freeze
+ prefetch_src].freeze
- NO_ARG_DIRECTIVES = %i(block_all_mixed_content disown_opener
- upgrade_insecure_requests).freeze
+ NO_ARG_DIRECTIVES = %i[block_all_mixed_content disown_opener
+ upgrade_insecure_requests].freeze
def csp_policy
directives = []
diff --git a/rack-protection/lib/rack/protection/cookie_tossing.rb b/rack-protection/lib/rack/protection/cookie_tossing.rb
index 10614d5261..0a7f43ad54 100644
--- a/rack-protection/lib/rack/protection/cookie_tossing.rb
+++ b/rack-protection/lib/rack/protection/cookie_tossing.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'rack/protection'
require 'pathname'
@@ -29,9 +31,8 @@ def accepts?(env)
cookie_header = env['HTTP_COOKIE']
cookies = Rack::Utils.parse_query(cookie_header, ';,') { |s| s }
cookies.each do |k, v|
- if k == session_key && Array(v).size > 1
- bad_cookies << k
- elsif k != session_key && Rack::Utils.unescape(k) == session_key
+ if (k == session_key && Array(v).size > 1) ||
+ (k != session_key && Rack::Utils.unescape(k) == session_key)
bad_cookies << k
end
end
@@ -40,6 +41,7 @@ def accepts?(env)
def remove_bad_cookies(request, response)
return if bad_cookies.empty?
+
paths = cookie_paths(request.path)
bad_cookies.each do |name|
paths.each { |path| response.set_cookie name, empty_cookie(request.host, path) }
@@ -49,7 +51,7 @@ def remove_bad_cookies(request, response)
def redirect(env)
request = Request.new(env)
warn env, "attack prevented by #{self.class}"
- [302, {'Content-Type' => 'text/html', 'Location' => request.path}, []]
+ [302, { 'Content-Type' => 'text/html', 'Location' => request.path }, []]
end
def bad_cookies
@@ -64,7 +66,7 @@ def cookie_paths(path)
end
def empty_cookie(host, path)
- {:value => '', :domain => host, :path => path, :expires => Time.at(0)}
+ { value: '', domain: host, path: path, expires: Time.at(0) }
end
def session_key
diff --git a/rack-protection/lib/rack/protection/encrypted_cookie.rb b/rack-protection/lib/rack/protection/encrypted_cookie.rb
index bb0dbacb38..bf682312a9 100644
--- a/rack-protection/lib/rack/protection/encrypted_cookie.rb
+++ b/rack-protection/lib/rack/protection/encrypted_cookie.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'openssl'
require 'zlib'
require 'json'
@@ -67,7 +69,7 @@ def encode(str)
end
def decode(str)
- str.unpack('m').first
+ str.unpack1('m')
end
# Encode session cookies as Marshaled Base64 data
@@ -78,7 +80,12 @@ def encode(str)
def decode(str)
return unless str
- ::Marshal.load(super(str)) rescue nil
+
+ begin
+ ::Marshal.load(super(str))
+ rescue StandardError
+ nil
+ end
end
end
@@ -91,7 +98,12 @@ def encode(obj)
def decode(str)
return unless str
- ::JSON.parse(super(str)) rescue nil
+
+ begin
+ ::JSON.parse(super(str))
+ rescue StandardError
+ nil
+ end
end
end
@@ -102,8 +114,9 @@ def encode(obj)
def decode(str)
return unless str
+
::JSON.parse(Zlib::Inflate.inflate(super(str)))
- rescue
+ rescue StandardError
nil
end
end
@@ -127,12 +140,12 @@ def decode(str)
attr_reader :coder
- def initialize(app, options={})
+ def initialize(app, options = {})
# Assume keys are hex strings and convert them to raw byte strings for
# actual key material
- @secrets = options.values_at(:secret, :old_secret).compact.map { |secret|
+ @secrets = options.values_at(:secret, :old_secret).compact.map do |secret|
[secret].pack('H*')
- }
+ end
warn <<-MSG unless secure?(options)
SECURITY WARNING: No secret option provided to Rack::Protection::EncryptedCookie.
@@ -154,7 +167,7 @@ def initialize(app, options={})
Called from: #{caller[0]}.
MSG
- if options.has_key?(:legacy_hmac_secret)
+ if options.key?(:legacy_hmac_secret)
@legacy_hmac = options.fetch(:legacy_hmac, OpenSSL::Digest::SHA1)
# Multiply the :digest_length: by 2 because this value is the length of
@@ -173,19 +186,19 @@ def initialize(app, options={})
# If no encryption is used, rely on the previous default (Base64::Marshal)
@coder = (options[:coder] ||= (@secrets.any? ? Marshal.new : Base64::Marshal.new))
- super(app, options.merge!(:cookie_only => true))
+ super(app, options.merge!(cookie_only: true))
end
private
- def find_session(req, sid)
+ def find_session(req, _sid)
data = unpacked_cookie_data(req)
data = persistent_session_id!(data)
- [data["session_id"], data]
+ [data['session_id'], data]
end
def extract_session_id(request)
- unpacked_cookie_data(request)["session_id"]
+ unpacked_cookie_data(request)['session_id']
end
def unpacked_cookie_data(request)
@@ -214,14 +227,14 @@ def unpacked_cookie_data(request)
end
end
- def persistent_session_id!(data, sid=nil)
+ def persistent_session_id!(data, sid = nil)
data ||= {}
- data["session_id"] ||= sid || generate_sid
+ data['session_id'] ||= sid || generate_sid
data
end
- def write_session(req, session_id, session, options)
- session = session.merge("session_id" => session_id)
+ def write_session(req, session_id, session, _options)
+ session = session.merge('session_id' => session_id)
session_data = coder.encode(session)
unless @secrets.empty?
@@ -229,14 +242,14 @@ def write_session(req, session_id, session, options)
end
if session_data.size > (4096 - @key.size)
- req.get_header(RACK_ERRORS).puts("Warning! Rack::Protection::EncryptedCookie data size exceeds 4K.")
+ req.get_header(RACK_ERRORS).puts('Warning! Rack::Protection::EncryptedCookie data size exceeds 4K.')
nil
else
session_data
end
end
- def delete_session(req, session_id, options)
+ def delete_session(_req, _session_id, options)
# Nothing to do here, data is in the client
generate_sid unless options[:drop]
end
@@ -253,7 +266,7 @@ def generate_hmac(data)
def secure?(options)
@secrets.size >= 1 ||
- (options[:coder] && options[:let_coder_handle_secure_encoding])
+ (options[:coder] && options[:let_coder_handle_secure_encoding])
end
end
end
diff --git a/rack-protection/lib/rack/protection/encryptor.rb b/rack-protection/lib/rack/protection/encryptor.rb
index e55c6aa96b..ded10c7e8e 100644
--- a/rack-protection/lib/rack/protection/encryptor.rb
+++ b/rack-protection/lib/rack/protection/encryptor.rb
@@ -1,21 +1,23 @@
+# frozen_string_literal: true
+
require 'openssl'
module Rack
module Protection
module Encryptor
- CIPHER = 'aes-256-gcm'.freeze
- DELIMITER = '--'.freeze
+ CIPHER = 'aes-256-gcm'
+ DELIMITER = '--'
def self.base64_encode(str)
[str].pack('m0')
end
def self.base64_decode(str)
- str.unpack('m0').first
+ str.unpack1('m0')
end
def self.encrypt_message(data, secret, auth_data = '')
- raise ArgumentError, "data cannot be nil" if data.nil?
+ raise ArgumentError, 'data cannot be nil' if data.nil?
cipher = OpenSSL::Cipher.new(CIPHER)
cipher.encrypt
@@ -52,7 +54,6 @@ def self.decrypt_message(data, secret)
decrypted_data = cipher.update(cipher_text)
decrypted_data << cipher.final
decrypted_data
-
rescue OpenSSL::Cipher::CipherError, TypeError, ArgumentError
nil
end
diff --git a/rack-protection/lib/rack/protection/escaped_params.rb b/rack-protection/lib/rack/protection/escaped_params.rb
index 6706d1030e..17b35d5292 100644
--- a/rack-protection/lib/rack/protection/escaped_params.rb
+++ b/rack-protection/lib/rack/protection/escaped_params.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'rack/protection'
require 'rack/utils'
require 'tempfile'
@@ -29,8 +31,8 @@ class << self
public :escape_html
end
- default_options :escape => :html,
- :escaper => defined?(EscapeUtils) ? EscapeUtils : self
+ default_options escape: :html,
+ escaper: defined?(EscapeUtils) ? EscapeUtils : self
def initialize(*)
super
@@ -41,15 +43,19 @@ def initialize(*)
@javascript = modes.include? :javascript
@url = modes.include? :url
- if @javascript and not @escaper.respond_to? :escape_javascript
- fail("Use EscapeUtils for JavaScript escaping.")
- end
+ return unless @javascript && (!@escaper.respond_to? :escape_javascript)
+
+ raise('Use EscapeUtils for JavaScript escaping.')
end
def call(env)
request = Request.new(env)
get_was = handle(request.GET)
- post_was = handle(request.POST) rescue nil
+ post_was = begin
+ handle(request.POST)
+ rescue StandardError
+ nil
+ end
app.call env
ensure
request.GET.replace get_was if get_was
@@ -68,13 +74,12 @@ def escape(object)
when Array then object.map { |o| escape(o) }
when String then escape_string(object)
when Tempfile then object
- else nil
end
end
def escape_hash(hash)
hash = hash.dup
- hash.each { |k,v| hash[k] = escape(v) }
+ hash.each { |k, v| hash[k] = escape(v) }
hash
end
diff --git a/rack-protection/lib/rack/protection/form_token.rb b/rack-protection/lib/rack/protection/form_token.rb
index 994740f83f..7e01976171 100644
--- a/rack-protection/lib/rack/protection/form_token.rb
+++ b/rack-protection/lib/rack/protection/form_token.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'rack/protection'
module Rack
@@ -16,7 +18,7 @@ module Protection
# Compatible with rack-csrf.
class FormToken < AuthenticityToken
def accepts?(env)
- env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest" or super
+ env['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest' or super
end
end
end
diff --git a/rack-protection/lib/rack/protection/frame_options.rb b/rack-protection/lib/rack/protection/frame_options.rb
index bce75c47e9..c159d4a985 100644
--- a/rack-protection/lib/rack/protection/frame_options.rb
+++ b/rack-protection/lib/rack/protection/frame_options.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'rack/protection'
module Rack
@@ -17,7 +19,7 @@ module Protection
# frame. Use :deny to forbid any embedding, :sameorigin
# to allow embedding from the same origin (default).
class FrameOptions < Base
- default_options :frame_options => :sameorigin
+ default_options frame_options: :sameorigin
def frame_options
@frame_options ||= begin
diff --git a/rack-protection/lib/rack/protection/http_origin.rb b/rack-protection/lib/rack/protection/http_origin.rb
index 834a185525..8eb25bbbd9 100644
--- a/rack-protection/lib/rack/protection/http_origin.rb
+++ b/rack-protection/lib/rack/protection/http_origin.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'rack/protection'
module Rack
@@ -19,7 +21,7 @@ module Protection
class HttpOrigin < Base
DEFAULT_PORTS = { 'http' => 80, 'https' => 443, 'coffee' => 80 }
default_reaction :deny
- default_options :allow_if => nil
+ default_options allow_if: nil
def base_url(env)
request = Rack::Request.new(env)
@@ -29,14 +31,13 @@ def base_url(env)
def accepts?(env)
return true if safe? env
- return true unless origin = env['HTTP_ORIGIN']
+ return true unless (origin = env['HTTP_ORIGIN'])
return true if base_url(env) == origin
- return true if options[:allow_if] && options[:allow_if].call(env)
+ return true if options[:allow_if]&.call(env)
permitted_origins = options[:permitted_origins]
Array(permitted_origins).include? origin
end
-
end
end
end
diff --git a/rack-protection/lib/rack/protection/ip_spoofing.rb b/rack-protection/lib/rack/protection/ip_spoofing.rb
index 3e404ad7c0..a8799324b6 100644
--- a/rack-protection/lib/rack/protection/ip_spoofing.rb
+++ b/rack-protection/lib/rack/protection/ip_spoofing.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'rack/protection'
module Rack
@@ -13,9 +15,11 @@ class IPSpoofing < Base
def accepts?(env)
return true unless env.include? 'HTTP_X_FORWARDED_FOR'
+
ips = env['HTTP_X_FORWARDED_FOR'].split(/\s*,\s*/)
- return false if env.include? 'HTTP_CLIENT_IP' and not ips.include? env['HTTP_CLIENT_IP']
- return false if env.include? 'HTTP_X_REAL_IP' and not ips.include? env['HTTP_X_REAL_IP']
+ return false if env.include?('HTTP_CLIENT_IP') && (!ips.include? env['HTTP_CLIENT_IP'])
+ return false if env.include?('HTTP_X_REAL_IP') && (!ips.include? env['HTTP_X_REAL_IP'])
+
true
end
end
diff --git a/rack-protection/lib/rack/protection/json_csrf.rb b/rack-protection/lib/rack/protection/json_csrf.rb
index 5ec826b719..9f6817ac18 100644
--- a/rack-protection/lib/rack/protection/json_csrf.rb
+++ b/rack-protection/lib/rack/protection/json_csrf.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'rack/protection'
module Rack
@@ -17,7 +19,7 @@ module Protection
#
# The `:allow_if` option can be set to a proc to use custom allow/deny logic.
class JsonCsrf < Base
- default_options :allow_if => nil
+ default_options allow_if: nil
alias react deny
@@ -36,8 +38,9 @@ def call(env)
def has_vector?(request, headers)
return false if request.xhr?
- return false if options[:allow_if] && options[:allow_if].call(request.env)
- return false unless headers['Content-Type'].to_s.split(';', 2).first =~ /^\s*application\/json\s*$/
+ return false if options[:allow_if]&.call(request.env)
+ return false unless headers['Content-Type'].to_s.split(';', 2).first =~ %r{^\s*application/json\s*$}
+
origin(request.env).nil? and referrer(request.env) != request.host
end
diff --git a/rack-protection/lib/rack/protection/path_traversal.rb b/rack-protection/lib/rack/protection/path_traversal.rb
index 4e8c94ca95..34e2e36eb5 100644
--- a/rack-protection/lib/rack/protection/path_traversal.rb
+++ b/rack-protection/lib/rack/protection/path_traversal.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'rack/protection'
module Rack
@@ -11,11 +13,11 @@ module Protection
# Thus GET /foo/%2e%2e%2fbar becomes GET /bar.
class PathTraversal < Base
def call(env)
- path_was = env["PATH_INFO"]
- env["PATH_INFO"] = cleanup path_was if path_was && !path_was.empty?
+ path_was = env['PATH_INFO']
+ env['PATH_INFO'] = cleanup path_was if path_was && !path_was.empty?
app.call env
ensure
- env["PATH_INFO"] = path_was
+ env['PATH_INFO'] = path_was
end
def cleanup(path)
@@ -29,12 +31,13 @@ def cleanup(path)
unescaped = unescaped.gsub(backslash, slash)
unescaped.split(slash).each do |part|
- next if part.empty? or part == dot
+ next if part.empty? || (part == dot)
+
part == '..' ? parts.pop : parts << part
end
cleaned = slash + parts.join(slash)
- cleaned << slash if parts.any? and unescaped =~ %r{/\.{0,2}$}
+ cleaned << slash if parts.any? && unescaped =~ (%r{/\.{0,2}$})
cleaned
end
end
diff --git a/rack-protection/lib/rack/protection/referrer_policy.rb b/rack-protection/lib/rack/protection/referrer_policy.rb
index f977d72e4b..eaff7020f9 100644
--- a/rack-protection/lib/rack/protection/referrer_policy.rb
+++ b/rack-protection/lib/rack/protection/referrer_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'rack/protection'
module Rack
@@ -13,7 +15,7 @@ module Protection
# Options:
# referrer_policy:: The policy to use (default: 'strict-origin-when-cross-origin')
class ReferrerPolicy < Base
- default_options :referrer_policy => 'strict-origin-when-cross-origin'
+ default_options referrer_policy: 'strict-origin-when-cross-origin'
def call(env)
status, headers, body = @app.call(env)
diff --git a/rack-protection/lib/rack/protection/remote_referrer.rb b/rack-protection/lib/rack/protection/remote_referrer.rb
index 5375ebc3d2..f882567fa1 100644
--- a/rack-protection/lib/rack/protection/remote_referrer.rb
+++ b/rack-protection/lib/rack/protection/remote_referrer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'rack/protection'
module Rack
diff --git a/rack-protection/lib/rack/protection/remote_token.rb b/rack-protection/lib/rack/protection/remote_token.rb
index f3922c8739..ef4f6e5744 100644
--- a/rack-protection/lib/rack/protection/remote_token.rb
+++ b/rack-protection/lib/rack/protection/remote_token.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'rack/protection'
module Rack
diff --git a/rack-protection/lib/rack/protection/session_hijacking.rb b/rack-protection/lib/rack/protection/session_hijacking.rb
index 55f4aad871..555121d8b9 100644
--- a/rack-protection/lib/rack/protection/session_hijacking.rb
+++ b/rack-protection/lib/rack/protection/session_hijacking.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'rack/protection'
module Rack
@@ -13,14 +15,14 @@ module Protection
# spoofed, too, this will not prevent determined hijacking attempts.
class SessionHijacking < Base
default_reaction :drop_session
- default_options :tracking_key => :tracking,
- :track => %w[HTTP_USER_AGENT]
+ default_options tracking_key: :tracking,
+ track: %w[HTTP_USER_AGENT]
def accepts?(env)
session = session env
key = options[:tracking_key]
if session.include? key
- session[key].all? { |k,v| v == encode(env[k]) }
+ session[key].all? { |k, v| v == encode(env[k]) }
else
session[key] = {}
options[:track].each { |k| session[key][k] = encode(env[k]) }
diff --git a/rack-protection/lib/rack/protection/strict_transport.rb b/rack-protection/lib/rack/protection/strict_transport.rb
index b4283bab36..05fe4ae6a8 100644
--- a/rack-protection/lib/rack/protection/strict_transport.rb
+++ b/rack-protection/lib/rack/protection/strict_transport.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'rack/protection'
module Rack
@@ -18,11 +20,11 @@ module Protection
# preload:: Allow this domain to be included in browsers HSTS preload list. See https://hstspreload.appspot.com/
class StrictTransport < Base
- default_options :max_age => 31_536_000, :include_subdomains => false, :preload => false
+ default_options max_age: 31_536_000, include_subdomains: false, preload: false
def strict_transport
@strict_transport ||= begin
- strict_transport = 'max-age=' + options[:max_age].to_s
+ strict_transport = "max-age=#{options[:max_age]}"
strict_transport += '; includeSubDomains' if options[:include_subdomains]
strict_transport += '; preload' if options[:preload]
strict_transport.to_str
diff --git a/rack-protection/lib/rack/protection/version.rb b/rack-protection/lib/rack/protection/version.rb
index 5c581ee170..ac5d076b97 100644
--- a/rack-protection/lib/rack/protection/version.rb
+++ b/rack-protection/lib/rack/protection/version.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rack
module Protection
VERSION = '3.0.0'
diff --git a/rack-protection/lib/rack/protection/xss_header.rb b/rack-protection/lib/rack/protection/xss_header.rb
index eb6f92fe36..14c679d9fb 100644
--- a/rack-protection/lib/rack/protection/xss_header.rb
+++ b/rack-protection/lib/rack/protection/xss_header.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'rack/protection'
module Rack
@@ -12,7 +14,7 @@ module Protection
# Options:
# xss_mode:: How the browser should prevent the attack (default: :block)
class XSSHeader < Base
- default_options :xss_mode => :block, :nosniff => true
+ default_options xss_mode: :block, nosniff: true
def call(env)
status, headers, body = @app.call(env)
diff --git a/rack-protection/lib/rack_protection.rb b/rack-protection/lib/rack_protection.rb
new file mode 100644
index 0000000000..fa36f00450
--- /dev/null
+++ b/rack-protection/lib/rack_protection.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+require 'rack/protection'
diff --git a/rack-protection/rack-protection.gemspec b/rack-protection/rack-protection.gemspec
index c8e0f2d1b8..354a66b227 100644
--- a/rack-protection/rack-protection.gemspec
+++ b/rack-protection/rack-protection.gemspec
@@ -1,40 +1,45 @@
-version = File.read(File.expand_path("../VERSION", __dir__)).strip
+# frozen_string_literal: true
+
+version = File.read(File.expand_path('../VERSION', __dir__)).strip
Gem::Specification.new do |s|
# general infos
- s.name = "rack-protection"
+ s.name = 'rack-protection'
s.version = version
- s.description = "Protect against typical web attacks, works with all Rack apps, including Rails."
- s.homepage = "http://sinatrarb.com/protection/"
+ s.description = 'Protect against typical web attacks, works with all Rack apps, including Rails.'
+ s.homepage = 'http://sinatrarb.com/protection/'
s.summary = s.description
s.license = 'MIT'
- s.authors = ["https://github.com/sinatra/sinatra/graphs/contributors"]
- s.email = "sinatrarb@googlegroups.com"
- s.files = Dir["lib/**/*.rb"] + [
- "License",
- "README.md",
- "Rakefile",
- "Gemfile",
- "rack-protection.gemspec"
+ s.authors = ['https://github.com/sinatra/sinatra/graphs/contributors']
+ s.email = 'sinatrarb@googlegroups.com'
+ s.files = Dir['lib/**/*.rb'] + [
+ 'License',
+ 'README.md',
+ 'Rakefile',
+ 'Gemfile',
+ 'rack-protection.gemspec'
]
- if s.respond_to?(:metadata)
- s.metadata = {
- 'source_code_uri' => 'https://github.com/sinatra/sinatra/tree/master/rack-protection',
- 'homepage_uri' => 'http://sinatrarb.com/protection/',
- 'documentation_uri' => 'https://www.rubydoc.info/gems/rack-protection'
- }
- else
- raise <<-EOF
+ unless s.respond_to?(:metadata)
+ raise <<-WARN
RubyGems 2.0 or newer is required to protect against public gem pushes. You can update your rubygems version by running:
gem install rubygems-update
update_rubygems:
gem update --system
-EOF
+ WARN
end
+ s.metadata = {
+ 'source_code_uri' => 'https://github.com/sinatra/sinatra/tree/master/rack-protection',
+ 'homepage_uri' => 'http://sinatrarb.com/protection/',
+ 'documentation_uri' => 'https://www.rubydoc.info/gems/rack-protection',
+ 'rubygems_mfa_required' => 'true'
+ }
+
+ s.required_ruby_version = '>= 2.6.0'
+
# dependencies
- s.add_dependency "rack"
- s.add_development_dependency "rack-test", "~> 2"
- s.add_development_dependency "rspec", "~> 3"
+ s.add_dependency 'rack'
+ s.add_development_dependency 'rack-test', '~> 2'
+ s.add_development_dependency 'rspec', '~> 3'
end
diff --git a/rack-protection/spec/lib/rack/protection/authenticity_token_spec.rb b/rack-protection/spec/lib/rack/protection/authenticity_token_spec.rb
index 433e6ea58f..e589726be5 100644
--- a/rack-protection/spec/lib/rack/protection/authenticity_token_spec.rb
+++ b/rack-protection/spec/lib/rack/protection/authenticity_token_spec.rb
@@ -1,68 +1,70 @@
+# frozen_string_literal: true
+
RSpec.describe Rack::Protection::AuthenticityToken do
let(:token) { described_class.random_token }
let(:masked_token) { described_class.token(session) }
- let(:bad_token) { Base64.strict_encode64("badtoken") }
- let(:session) { {:csrf => token} }
+ let(:bad_token) { Base64.strict_encode64('badtoken') }
+ let(:session) { { csrf: token } }
- it_behaves_like "any rack application"
+ it_behaves_like 'any rack application'
- it "denies post requests without any token" do
+ it 'denies post requests without any token' do
expect(post('/')).not_to be_ok
end
- it "accepts post requests with correct X-CSRF-Token header" do
+ it 'accepts post requests with correct X-CSRF-Token header' do
post('/', {}, 'rack.session' => session, 'HTTP_X_CSRF_TOKEN' => token)
expect(last_response).to be_ok
end
- it "accepts post requests with masked X-CSRF-Token header" do
+ it 'accepts post requests with masked X-CSRF-Token header' do
post('/', {}, 'rack.session' => session, 'HTTP_X_CSRF_TOKEN' => masked_token)
expect(last_response).to be_ok
end
- it "denies post requests with wrong X-CSRF-Token header" do
+ it 'denies post requests with wrong X-CSRF-Token header' do
post('/', {}, 'rack.session' => session, 'HTTP_X_CSRF_TOKEN' => bad_token)
expect(last_response).not_to be_ok
end
- it "accepts post form requests with correct authenticity_token field" do
- post('/', {"authenticity_token" => token}, 'rack.session' => session)
+ it 'accepts post form requests with correct authenticity_token field' do
+ post('/', { 'authenticity_token' => token }, 'rack.session' => session)
expect(last_response).to be_ok
end
- it "accepts post form requests with masked authenticity_token field" do
- post('/', {"authenticity_token" => masked_token}, 'rack.session' => session)
+ it 'accepts post form requests with masked authenticity_token field' do
+ post('/', { 'authenticity_token' => masked_token }, 'rack.session' => session)
expect(last_response).to be_ok
end
- it "denies post form requests with wrong authenticity_token field" do
- post('/', {"authenticity_token" => bad_token}, 'rack.session' => session)
+ it 'denies post form requests with wrong authenticity_token field' do
+ post('/', { 'authenticity_token' => bad_token }, 'rack.session' => session)
expect(last_response).not_to be_ok
end
- it "accepts post form requests with a valid per form token" do
+ it 'accepts post form requests with a valid per form token' do
token = Rack::Protection::AuthenticityToken.token(session, path: '/foo')
- post('/foo', {"authenticity_token" => token}, 'rack.session' => session)
+ post('/foo', { 'authenticity_token' => token }, 'rack.session' => session)
expect(last_response).to be_ok
end
- it "denies post form requests with an invalid per form token" do
+ it 'denies post form requests with an invalid per form token' do
token = Rack::Protection::AuthenticityToken.token(session, path: '/foo')
- post('/bar', {"authenticity_token" => token}, 'rack.session' => session)
+ post('/bar', { 'authenticity_token' => token }, 'rack.session' => session)
expect(last_response).not_to be_ok
end
- it "prevents ajax requests without a valid token" do
- expect(post('/', {}, "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest")).not_to be_ok
+ it 'prevents ajax requests without a valid token' do
+ expect(post('/', {}, 'HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest')).not_to be_ok
end
- it "allows for a custom authenticity token param" do
+ it 'allows for a custom authenticity token param' do
mock_app do
- use Rack::Protection::AuthenticityToken, :authenticity_param => 'csrf_param'
- run proc { |e| [200, {'Content-Type' => 'text/plain'}, ['hi']] }
+ use Rack::Protection::AuthenticityToken, authenticity_param: 'csrf_param'
+ run proc { |_e| [200, { 'Content-Type' => 'text/plain' }, ['hi']] }
end
- post('/', {"csrf_param" => token}, 'rack.session' => {:csrf => token})
+ post('/', { 'csrf_param' => token }, 'rack.session' => { csrf: token })
expect(last_response).to be_ok
end
@@ -71,10 +73,10 @@
expect(env['rack.session'][:csrf]).not_to be_nil
end
- it "allows for a custom token session key" do
+ it 'allows for a custom token session key' do
mock_app do
- use Rack::Session::Cookie, :key => 'rack.session'
- use Rack::Protection::AuthenticityToken, :key => :_csrf
+ use Rack::Session::Cookie, key: 'rack.session'
+ use Rack::Protection::AuthenticityToken, key: :_csrf
run DummyApp
end
@@ -82,12 +84,12 @@
expect(env['rack.session'][:_csrf]).not_to be_nil
end
- describe ".token" do
- it "returns a unique masked version of the authenticity token" do
+ describe '.token' do
+ it 'returns a unique masked version of the authenticity token' do
expect(Rack::Protection::AuthenticityToken.token(session)).not_to eq(masked_token)
end
- it "sets a session authenticity token if one does not exist" do
+ it 'sets a session authenticity token if one does not exist' do
session = {}
allow(Rack::Protection::AuthenticityToken).to receive(:random_token).and_return(token)
allow_any_instance_of(Rack::Protection::AuthenticityToken).to receive(:mask_token).and_return(masked_token)
@@ -96,8 +98,8 @@
end
end
- describe ".random_token" do
- it "generates a base64 encoded 32 character string" do
+ describe '.random_token' do
+ it 'generates a base64 encoded 32 character string' do
expect(Base64.urlsafe_decode64(token).length).to eq(32)
end
end
diff --git a/rack-protection/spec/lib/rack/protection/base_spec.rb b/rack-protection/spec/lib/rack/protection/base_spec.rb
index 46a4d901d4..c4b8f8c86e 100644
--- a/rack-protection/spec/lib/rack/protection/base_spec.rb
+++ b/rack-protection/spec/lib/rack/protection/base_spec.rb
@@ -1,37 +1,38 @@
-RSpec.describe Rack::Protection::Base do
+# frozen_string_literal: true
- subject { described_class.new(lambda {}) }
+RSpec.describe Rack::Protection::Base do
+ subject { described_class.new(-> {}) }
- describe "#random_string" do
- it "outputs a string of 32 characters" do
+ describe '#random_string' do
+ it 'outputs a string of 32 characters' do
expect(subject.random_string.length).to eq(32)
end
end
- describe "#referrer" do
- it "Reads referrer from Referer header" do
- env = {"HTTP_HOST" => "foo.com", "HTTP_REFERER" => "http://bar.com/valid"}
- expect(subject.referrer(env)).to eq("bar.com")
+ describe '#referrer' do
+ it 'Reads referrer from Referer header' do
+ env = { 'HTTP_HOST' => 'foo.com', 'HTTP_REFERER' => 'http://bar.com/valid' }
+ expect(subject.referrer(env)).to eq('bar.com')
end
- it "Reads referrer from Host header when Referer header is relative" do
- env = {"HTTP_HOST" => "foo.com", "HTTP_REFERER" => "/valid"}
- expect(subject.referrer(env)).to eq("foo.com")
+ it 'Reads referrer from Host header when Referer header is relative' do
+ env = { 'HTTP_HOST' => 'foo.com', 'HTTP_REFERER' => '/valid' }
+ expect(subject.referrer(env)).to eq('foo.com')
end
- it "Reads referrer from Host header when Referer header is missing" do
- env = {"HTTP_HOST" => "foo.com"}
- expect(subject.referrer(env)).to eq("foo.com")
+ it 'Reads referrer from Host header when Referer header is missing' do
+ env = { 'HTTP_HOST' => 'foo.com' }
+ expect(subject.referrer(env)).to eq('foo.com')
end
- it "Returns nil when Referer header is missing and allow_empty_referrer is false" do
- env = {"HTTP_HOST" => "foo.com"}
+ it 'Returns nil when Referer header is missing and allow_empty_referrer is false' do
+ env = { 'HTTP_HOST' => 'foo.com' }
subject.options[:allow_empty_referrer] = false
expect(subject.referrer(env)).to be_nil
end
- it "Returns nil when Referer header is invalid" do
- env = {"HTTP_HOST" => "foo.com", "HTTP_REFERER" => "http://bar.com/bad|uri"}
+ it 'Returns nil when Referer header is invalid' do
+ env = { 'HTTP_HOST' => 'foo.com', 'HTTP_REFERER' => 'http://bar.com/bad|uri' }
expect(subject.referrer(env)).to be_nil
end
end
diff --git a/rack-protection/spec/lib/rack/protection/content_security_policy_spec.rb b/rack-protection/spec/lib/rack/protection/content_security_policy_spec.rb
index 0df46ba83b..35a26ad837 100644
--- a/rack-protection/spec/lib/rack/protection/content_security_policy_spec.rb
+++ b/rack-protection/spec/lib/rack/protection/content_security_policy_spec.rb
@@ -1,66 +1,68 @@
+# frozen_string_literal: true
+
RSpec.describe Rack::Protection::ContentSecurityPolicy do
- it_behaves_like "any rack application"
+ it_behaves_like 'any rack application'
it 'should set the Content Security Policy' do
expect(
- get('/', {}, 'wants' => 'text/html').headers["Content-Security-Policy"]
+ get('/', {}, 'wants' => 'text/html').headers['Content-Security-Policy']
).to eq("default-src 'self'")
end
it 'should not set the Content Security Policy for other content types' do
headers = get('/', {}, 'wants' => 'text/foo').headers
- expect(headers["Content-Security-Policy"]).to be_nil
- expect(headers["Content-Security-Policy-Report-Only"]).to be_nil
+ expect(headers['Content-Security-Policy']).to be_nil
+ expect(headers['Content-Security-Policy-Report-Only']).to be_nil
end
it 'should allow changing the protection settings' do
mock_app do
- use Rack::Protection::ContentSecurityPolicy, :default_src => 'none', :script_src => 'https://cdn.mybank.net', :style_src => 'https://cdn.mybank.net', :img_src => 'https://cdn.mybank.net', :connect_src => 'https://api.mybank.com', :frame_src => 'self', :font_src => 'https://cdn.mybank.net', :object_src => 'https://cdn.mybank.net', :media_src => 'https://cdn.mybank.net', :report_uri => '/my_amazing_csp_report_parser', :sandbox => 'allow-scripts'
+ use Rack::Protection::ContentSecurityPolicy, default_src: 'none', script_src: 'https://cdn.mybank.net', style_src: 'https://cdn.mybank.net', img_src: 'https://cdn.mybank.net', connect_src: 'https://api.mybank.com', frame_src: 'self', font_src: 'https://cdn.mybank.net', object_src: 'https://cdn.mybank.net', media_src: 'https://cdn.mybank.net', report_uri: '/my_amazing_csp_report_parser', sandbox: 'allow-scripts'
run DummyApp
end
headers = get('/', {}, 'wants' => 'text/html').headers
- expect(headers["Content-Security-Policy"]).to eq("connect-src https://api.mybank.com; default-src none; font-src https://cdn.mybank.net; frame-src self; img-src https://cdn.mybank.net; media-src https://cdn.mybank.net; object-src https://cdn.mybank.net; report-uri /my_amazing_csp_report_parser; sandbox allow-scripts; script-src https://cdn.mybank.net; style-src https://cdn.mybank.net")
- expect(headers["Content-Security-Policy-Report-Only"]).to be_nil
+ expect(headers['Content-Security-Policy']).to eq('connect-src https://api.mybank.com; default-src none; font-src https://cdn.mybank.net; frame-src self; img-src https://cdn.mybank.net; media-src https://cdn.mybank.net; object-src https://cdn.mybank.net; report-uri /my_amazing_csp_report_parser; sandbox allow-scripts; script-src https://cdn.mybank.net; style-src https://cdn.mybank.net')
+ expect(headers['Content-Security-Policy-Report-Only']).to be_nil
end
it 'should allow setting CSP3 no arg directives' do
mock_app do
- use Rack::Protection::ContentSecurityPolicy, :block_all_mixed_content => true, :disown_opener => true, :upgrade_insecure_requests => true
+ use Rack::Protection::ContentSecurityPolicy, block_all_mixed_content: true, disown_opener: true, upgrade_insecure_requests: true
run DummyApp
end
headers = get('/', {}, 'wants' => 'text/html').headers
- expect(headers["Content-Security-Policy"]).to eq("block-all-mixed-content; default-src 'self'; disown-opener; upgrade-insecure-requests")
+ expect(headers['Content-Security-Policy']).to eq("block-all-mixed-content; default-src 'self'; disown-opener; upgrade-insecure-requests")
end
it 'should ignore CSP3 no arg directives unless they are set to true' do
mock_app do
- use Rack::Protection::ContentSecurityPolicy, :block_all_mixed_content => false, :disown_opener => 'false', :upgrade_insecure_requests => 'foo'
+ use Rack::Protection::ContentSecurityPolicy, block_all_mixed_content: false, disown_opener: 'false', upgrade_insecure_requests: 'foo'
run DummyApp
end
headers = get('/', {}, 'wants' => 'text/html').headers
- expect(headers["Content-Security-Policy"]).to eq("default-src 'self'")
+ expect(headers['Content-Security-Policy']).to eq("default-src 'self'")
end
it 'should allow changing report only' do
# I have no clue what other modes are available
mock_app do
- use Rack::Protection::ContentSecurityPolicy, :report_uri => '/my_amazing_csp_report_parser', :report_only => true
+ use Rack::Protection::ContentSecurityPolicy, report_uri: '/my_amazing_csp_report_parser', report_only: true
run DummyApp
end
headers = get('/', {}, 'wants' => 'text/html').headers
- expect(headers["Content-Security-Policy"]).to be_nil
- expect(headers["Content-Security-Policy-Report-Only"]).to eq("default-src 'self'; report-uri /my_amazing_csp_report_parser")
+ expect(headers['Content-Security-Policy']).to be_nil
+ expect(headers['Content-Security-Policy-Report-Only']).to eq("default-src 'self'; report-uri /my_amazing_csp_report_parser")
end
it 'should not override the header if already set' do
- mock_app with_headers("Content-Security-Policy" => "default-src: none")
- expect(get('/', {}, 'wants' => 'text/html').headers["Content-Security-Policy"]).to eq("default-src: none")
+ mock_app with_headers('Content-Security-Policy' => 'default-src: none')
+ expect(get('/', {}, 'wants' => 'text/html').headers['Content-Security-Policy']).to eq('default-src: none')
end
end
diff --git a/rack-protection/spec/lib/rack/protection/cookie_tossing_spec.rb b/rack-protection/spec/lib/rack/protection/cookie_tossing_spec.rb
index af6d888296..f24b9920b6 100644
--- a/rack-protection/spec/lib/rack/protection/cookie_tossing_spec.rb
+++ b/rack-protection/spec/lib/rack/protection/cookie_tossing_spec.rb
@@ -1,5 +1,7 @@
+# frozen_string_literal: true
+
RSpec.describe Rack::Protection::CookieTossing do
- it_behaves_like "any rack application"
+ it_behaves_like 'any rack application'
context 'with default reaction' do
before(:each) do
@@ -34,7 +36,7 @@
rack.session=; domain=example.org; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT
rack.session=; domain=example.org; path=/some; expires=Thu, 01 Jan 1970 00:00:00 GMT
rack.session=; domain=example.org; path=/some/path; expires=Thu, 01 Jan 1970 00:00:00 GMT
-END
+ END
expect(last_response.headers['Set-Cookie']).to eq(expected_header)
end
end
@@ -42,7 +44,7 @@
context 'with redirect reaction' do
before(:each) do
mock_app do
- use Rack::Protection::CookieTossing, :reaction => :redirect
+ use Rack::Protection::CookieTossing, reaction: :redirect
run DummyApp
end
end
@@ -63,7 +65,7 @@
context 'with custom session key' do
it 'denies requests with duplicate session cookies' do
mock_app do
- use Rack::Protection::CookieTossing, :session_key => '_session'
+ use Rack::Protection::CookieTossing, session_key: '_session'
run DummyApp
end
diff --git a/rack-protection/spec/lib/rack/protection/encrypted_cookie_spec.rb b/rack-protection/spec/lib/rack/protection/encrypted_cookie_spec.rb
index 726687c2ac..de2a659bc1 100644
--- a/rack-protection/spec/lib/rack/protection/encrypted_cookie_spec.rb
+++ b/rack-protection/spec/lib/rack/protection/encrypted_cookie_spec.rb
@@ -1,77 +1,79 @@
+# frozen_string_literal: true
+
RSpec.describe Rack::Protection::EncryptedCookie do
let(:incrementor) do
lambda do |env|
- env["rack.session"]["counter"] ||= 0
- env["rack.session"]["counter"] += 1
- hash = env["rack.session"].dup
- hash.delete("session_id")
+ env['rack.session']['counter'] ||= 0
+ env['rack.session']['counter'] += 1
+ hash = env['rack.session'].dup
+ hash.delete('session_id')
Rack::Response.new(hash.inspect).to_a
end
end
let(:session_id) do
lambda do |env|
- Rack::Response.new(env["rack.session"].to_hash.inspect).to_a
+ Rack::Response.new(env['rack.session'].to_hash.inspect).to_a
end
end
let(:session_option) do
lambda do |opt|
lambda do |env|
- Rack::Response.new(env["rack.session.options"][opt].inspect).to_a
+ Rack::Response.new(env['rack.session.options'][opt].inspect).to_a
end
end
end
let(:nothing) do
- lambda do |env|
- Rack::Response.new("Nothing").to_a
+ lambda do |_env|
+ Rack::Response.new('Nothing').to_a
end
end
let(:renewer) do
lambda do |env|
- env["rack.session.options"][:renew] = true
- Rack::Response.new("Nothing").to_a
+ env['rack.session.options'][:renew] = true
+ Rack::Response.new('Nothing').to_a
end
end
let(:only_session_id) do
lambda do |env|
- Rack::Response.new(env["rack.session"]["session_id"].to_s).to_a
+ Rack::Response.new(env['rack.session']['session_id'].to_s).to_a
end
end
let(:bigcookie) do
lambda do |env|
- env["rack.session"]["cookie"] = "big" * 3000
- Rack::Response.new(env["rack.session"].inspect).to_a
+ env['rack.session']['cookie'] = 'big' * 3000
+ Rack::Response.new(env['rack.session'].inspect).to_a
end
end
let(:destroy_session) do
lambda do |env|
- env["rack.session"].destroy
- Rack::Response.new("Nothing").to_a
+ env['rack.session'].destroy
+ Rack::Response.new('Nothing').to_a
end
end
- def response_for(options={})
+ def response_for(options = {})
request_options = options.fetch(:request, {})
cookie = if options[:cookie].is_a?(Rack::Response)
- options[:cookie]["Set-Cookie"]
- else
- options[:cookie]
- end
- request_options["HTTP_COOKIE"] = cookie || ""
+ options[:cookie]['Set-Cookie']
+ else
+ options[:cookie]
+ end
+ request_options['HTTP_COOKIE'] = cookie || ''
app_with_cookie = Rack::Protection::EncryptedCookie.new(*options[:app])
app_with_cookie = Rack::Lint.new(app_with_cookie)
- Rack::MockRequest.new(app_with_cookie).get("/", request_options)
+ Rack::MockRequest.new(app_with_cookie).get('/', request_options)
end
def random_cipher_secret
- OpenSSL::Cipher.new('aes-256-gcm').random_key.unpack('H*').first
+ OpenSSL::Cipher.new('aes-256-gcm').random_key.unpack1('H*')
end
let(:secret) { random_cipher_secret }
@@ -99,7 +101,7 @@ def random_cipher_secret
it 'uses base64 to decode' do
coder = Rack::Protection::EncryptedCookie::Base64.new
str = ['fuuuuu'].pack('m0')
- expect(coder.decode(str)).to eq(str.unpack('m0').first)
+ expect(coder.decode(str)).to eq(str.unpack1('m0'))
end
it 'handles non-strict base64 encoding' do
@@ -118,7 +120,7 @@ def random_cipher_secret
it 'marshals and base64 decodes' do
coder = Rack::Protection::EncryptedCookie::Base64::Marshal.new
str = [::Marshal.dump('fuuuuu')].pack('m0')
- expect(coder.decode(str)).to eq(::Marshal.load(str.unpack('m0').first))
+ expect(coder.decode(str)).to eq(::Marshal.load(str.unpack1('m0')))
end
it 'rescues failures on decode' do
@@ -137,7 +139,7 @@ def random_cipher_secret
it 'JSON and base64 decodes' do
coder = Rack::Protection::EncryptedCookie::Base64::JSON.new
str = [::JSON.dump(%w[fuuuuu])].pack('m0')
- expect(coder.decode(str)).to eq(::JSON.parse(str.unpack('m0').first))
+ expect(coder.decode(str)).to eq(::JSON.parse(str.unpack1('m0')))
end
it 'rescues failures on decode' do
@@ -169,7 +171,7 @@ def random_cipher_secret
end
end
- it "warns if no secret is given" do
+ it 'warns if no secret is given' do
Rack::Protection::EncryptedCookie.new(incrementor)
expect(warnings.first).to match(/no secret/i)
warnings.clear
@@ -178,7 +180,7 @@ def random_cipher_secret
end
it 'warns if secret is to short' do
- Rack::Protection::EncryptedCookie.new(incrementor, secret: secret[0,16])
+ Rack::Protection::EncryptedCookie.new(incrementor, secret: secret[0, 16])
expect(warnings.first).to match(/secret is not long enough/i)
warnings.clear
Rack::Protection::EncryptedCookie.new(incrementor, secret: secret)
@@ -192,38 +194,46 @@ def random_cipher_secret
expect(warnings).to be_empty
end
- it "still warns if coder is not set" do
+ it 'still warns if coder is not set' do
Rack::Protection::EncryptedCookie.new(
incrementor,
- let_coder_handle_secure_encoding: true)
+ let_coder_handle_secure_encoding: true
+ )
expect(warnings.first).to match(/no secret/i)
end
it 'uses a coder' do
- identity = Class.new {
+ identity = Class.new do
attr_reader :calls
def initialize
@calls = []
end
- def encode(str); @calls << :encode; str; end
- def decode(str); @calls << :decode; str; end
- }.new
+ def encode(str)
+ @calls << :encode
+ str
+ end
+
+ def decode(str)
+ @calls << :decode
+ str
+ end
+ end.new
response = response_for(app: [incrementor, { coder: identity }])
- expect(response["Set-Cookie"]).to include("rack.session=")
+ expect(response['Set-Cookie']).to include('rack.session=')
expect(response.body).to eq('{"counter"=>1}')
- expect(identity.calls).to eq([:decode, :encode])
+ expect(identity.calls).to eq(%i[decode encode])
end
- it "creates a new cookie" do
+ it 'creates a new cookie' do
response = response_for(app: incrementor)
- expect(response["Set-Cookie"]).to include("rack.session=")
+ expect(response['Set-Cookie']).to include('rack.session=')
expect(response.body).to eq('{"counter"=>1}')
end
- it "loads from a cookie" do
+ it 'loads from a cookie' do
response = response_for(app: incrementor)
response = response_for(app: incrementor, cookie: response)
@@ -233,58 +243,58 @@ def decode(str); @calls << :decode; str; end
expect(response.body).to eq('{"counter"=>3}')
end
- it "renew session id" do
+ it 'renew session id' do
response = response_for(app: incrementor)
cookie = response['Set-Cookie']
response = response_for(app: only_session_id, cookie: cookie)
cookie = response['Set-Cookie'] if response['Set-Cookie']
- expect(response.body).to_not eq("")
+ expect(response.body).to_not eq('')
old_session_id = response.body
response = response_for(app: renewer, cookie: cookie)
cookie = response['Set-Cookie'] if response['Set-Cookie']
response = response_for(app: only_session_id, cookie: cookie)
- expect(response.body).to_not eq("")
+ expect(response.body).to_not eq('')
expect(response.body).to_not eq(old_session_id)
end
- it "destroys session" do
+ it 'destroys session' do
response = response_for(app: incrementor)
response = response_for(app: only_session_id, cookie: response)
- expect(response.body).to_not eq("")
+ expect(response.body).to_not eq('')
old_session_id = response.body
response = response_for(app: destroy_session, cookie: response)
response = response_for(app: only_session_id, cookie: response)
- expect(response.body).to_not eq("")
+ expect(response.body).to_not eq('')
expect(response.body).to_not eq(old_session_id)
end
- it "survives broken cookies" do
+ it 'survives broken cookies' do
response = response_for(
app: incrementor,
- cookie: "rack.session=blarghfasel"
+ cookie: 'rack.session=blarghfasel'
)
expect(response.body).to eq('{"counter"=>1}')
response = response_for(
app: [incrementor, { secret: secret }],
- cookie: "rack.session="
+ cookie: 'rack.session='
)
expect(response.body).to eq('{"counter"=>1}')
end
- it "barks on too big cookies" do
- expect {
+ it 'barks on too big cookies' do
+ expect do
response_for(app: bigcookie, request: { fatal: true })
- }.to raise_error Rack::MockRequest::FatalWarning
+ end.to raise_error Rack::MockRequest::FatalWarning
end
- it "loads from a cookie with integrity hash" do
+ it 'loads from a cookie with integrity hash' do
app = [incrementor, { secret: secret }]
response = response_for(app: app)
@@ -300,7 +310,7 @@ def decode(str); @calls << :decode; str; end
expect(response.body).to eq('{"counter"=>1}')
end
- it "loads from a cookie with accept-only integrity hash for graceful key rotation" do
+ it 'loads from a cookie with accept-only integrity hash for graceful key rotation' do
response = response_for(app: [incrementor, { secret: secret }])
new_secret = random_cipher_secret
@@ -319,7 +329,7 @@ def decode(str); @calls << :decode; str; end
it 'loads from a legacy hmac cookie' do
legacy_session = Rack::Protection::EncryptedCookie::Base64::Marshal.new.encode({ 'counter' => 1, 'session_id' => 'abcdef' })
legacy_secret = 'test legacy secret'
- legacy_digest = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, legacy_secret, legacy_session)
+ legacy_digest = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('SHA1'), legacy_secret, legacy_session)
legacy_cookie = "rack.session=#{legacy_session}--#{legacy_digest}; path=/; HttpOnly"
@@ -328,7 +338,7 @@ def decode(str); @calls << :decode; str; end
expect(response.body).to eq('{"counter"=>2}')
end
- it "ignores tampered with session cookies" do
+ it 'ignores tampered with session cookies' do
app = [incrementor, { secret: secret }]
response = response_for(app: app)
expect(response.body).to eq('{"counter"=>1}')
@@ -336,7 +346,7 @@ def decode(str); @calls << :decode; str; end
response = response_for(app: app, cookie: response)
expect(response.body).to eq('{"counter"=>2}')
- ctxt, iv, auth_tag = response["Set-Cookie"].split("--", 3)
+ ctxt, iv, auth_tag = response['Set-Cookie'].split('--', 3)
tampered_with_cookie = [ctxt, iv, auth_tag.reverse].join('--')
response = response_for(app: app, cookie: tampered_with_cookie)
@@ -346,7 +356,7 @@ def decode(str); @calls << :decode; str; end
it 'ignores tampered with legacy hmac cookie' do
legacy_session = Rack::Protection::EncryptedCookie::Base64::Marshal.new.encode({ 'counter' => 1, 'session_id' => 'abcdef' })
legacy_secret = 'test legacy secret'
- legacy_digest = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, legacy_secret, legacy_session).reverse
+ legacy_digest = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('SHA1'), legacy_secret, legacy_session).reverse
legacy_cookie = "rack.session=#{legacy_session}--#{legacy_digest}; path=/; HttpOnly"
@@ -355,7 +365,7 @@ def decode(str); @calls << :decode; str; end
expect(response.body).to eq('{"counter"=>1}')
end
- it "supports either of secret or old_secret" do
+ it 'supports either of secret or old_secret' do
app = [incrementor, { secret: secret }]
response = response_for(app: app)
expect(response.body).to eq('{"counter"=>1}')
@@ -371,7 +381,7 @@ def decode(str); @calls << :decode; str; end
expect(response.body).to eq('{"counter"=>2}')
end
- it "supports custom digest class for legacy hmac cookie" do
+ it 'supports custom digest class for legacy hmac cookie' do
legacy_hmac = OpenSSL::Digest::SHA256
legacy_session = Rack::Protection::EncryptedCookie::Base64::Marshal.new.encode({ 'counter' => 1, 'session_id' => 'abcdef' })
legacy_secret = 'test legacy secret'
@@ -379,7 +389,8 @@ def decode(str); @calls << :decode; str; end
legacy_cookie = "rack.session=#{Rack::Utils.escape legacy_session}--#{legacy_digest}; path=/; HttpOnly"
app = [incrementor, {
- secret: secret, legacy_hmac_secret: legacy_secret, legacy_hmac: legacy_hmac }]
+ secret: secret, legacy_hmac_secret: legacy_secret, legacy_hmac: legacy_hmac
+ }]
response = response_for(app: app, cookie: legacy_cookie)
expect(response.body).to eq('{"counter"=>2}')
@@ -388,7 +399,7 @@ def decode(str); @calls << :decode; str; end
expect(response.body).to eq('{"counter"=>3}')
end
- it "can handle Rack::Lint middleware" do
+ it 'can handle Rack::Lint middleware' do
response = response_for(app: incrementor)
lint = Rack::Lint.new(session_id)
@@ -396,11 +407,12 @@ def decode(str); @calls << :decode; str; end
expect(response.body).to_not be_nil
end
- it "can handle middleware that inspects the env" do
+ it 'can handle middleware that inspects the env' do
class TestEnvInspector
def initialize(app)
@app = app
end
+
def call(env)
env.inspect
@app.call(env)
@@ -414,7 +426,7 @@ def call(env)
expect(response.body).to_not be_nil
end
- it "returns the session id in the session hash" do
+ it 'returns the session id in the session hash' do
response = response_for(app: incrementor)
expect(response.body).to eq('{"counter"=>1}')
@@ -423,38 +435,38 @@ def call(env)
expect(response.body).to match(/"counter"=>1/)
end
- it "does not return a cookie if set to secure but not using ssl" do
+ it 'does not return a cookie if set to secure but not using ssl' do
app = [incrementor, { secure: true }]
response = response_for(app: app)
- expect(response["Set-Cookie"]).to be_nil
+ expect(response['Set-Cookie']).to be_nil
- response = response_for(app: app, request: { "HTTPS" => "on" })
- expect(response["Set-Cookie"]).to_not be_nil
- expect(response["Set-Cookie"]).to match(/secure/)
+ response = response_for(app: app, request: { 'HTTPS' => 'on' })
+ expect(response['Set-Cookie']).to_not be_nil
+ expect(response['Set-Cookie']).to match(/secure/)
end
- it "does not return a cookie if cookie was not read/written" do
+ it 'does not return a cookie if cookie was not read/written' do
response = response_for(app: nothing)
- expect(response["Set-Cookie"]).to be_nil
+ expect(response['Set-Cookie']).to be_nil
end
- it "does not return a cookie if cookie was not written (only read)" do
+ it 'does not return a cookie if cookie was not written (only read)' do
response = response_for(app: session_id)
- expect(response["Set-Cookie"]).to be_nil
+ expect(response['Set-Cookie']).to be_nil
end
- it "returns even if not read/written if :expire_after is set" do
+ it 'returns even if not read/written if :expire_after is set' do
app = [nothing, { expire_after: 3600 }]
- request = { "rack.session" => { "not" => "empty" }}
+ request = { 'rack.session' => { 'not' => 'empty' } }
response = response_for(app: app, request: request)
- expect(response["Set-Cookie"]).to_not be_nil
+ expect(response['Set-Cookie']).to_not be_nil
end
- it "returns no cookie if no data was written and no session was created previously, even if :expire_after is set" do
+ it 'returns no cookie if no data was written and no session was created previously, even if :expire_after is set' do
app = [nothing, { expire_after: 3600 }]
response = response_for(app: app)
- expect(response["Set-Cookie"]).to be_nil
+ expect(response['Set-Cookie']).to be_nil
end
it "exposes :secret in env['rack.session.option']" do
@@ -472,14 +484,14 @@ def call(env)
expect(response.body).to match(/Marshal/)
end
- it "allows passing in a hash with session data from middleware in front" do
- request = { 'rack.session' => { foo: 'bar' }}
+ it 'allows passing in a hash with session data from middleware in front' do
+ request = { 'rack.session' => { foo: 'bar' } }
response = response_for(app: session_id, request: request)
expect(response.body).to match(/foo/)
end
- it "allows modifying session data with session data from middleware in front" do
- request = { 'rack.session' => { foo: 'bar' }}
+ it 'allows modifying session data with session data from middleware in front' do
+ request = { 'rack.session' => { foo: 'bar' } }
response = response_for(app: incrementor, request: request)
expect(response.body).to match(/counter/)
expect(response.body).to match(/foo/)
@@ -488,41 +500,42 @@ def call(env)
it "allows more than one '--' in the cookie when calculating legacy digests" do
@counter = 0
app = lambda do |env|
- env["rack.session"]["message"] ||= ""
- env["rack.session"]["message"] << "#{(@counter += 1).to_s}--"
- hash = env["rack.session"].dup
- hash.delete("session_id")
- Rack::Response.new(hash["message"]).to_a
+ env['rack.session']['message'] ||= ''
+ env['rack.session']['message'] << "#{@counter += 1}--"
+ hash = env['rack.session'].dup
+ hash.delete('session_id')
+ Rack::Response.new(hash['message']).to_a
end
# another example of an unsafe coder is Base64.urlsafe_encode64
- unsafe_coder = Class.new {
+ unsafe_coder = Class.new do
def encode(hash); hash.inspect end
def decode(str); eval(str) if str; end
- }.new
+ end.new
legacy_session = unsafe_coder.encode('message' => "#{@counter += 1}--#{@counter += 1}--", 'session_id' => 'abcdef')
legacy_secret = 'test legacy secret'
- legacy_digest = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, legacy_secret, legacy_session)
+ legacy_digest = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('SHA1'), legacy_secret, legacy_session)
legacy_cookie = "rack.session=#{Rack::Utils.escape legacy_session}--#{legacy_digest}; path=/; HttpOnly"
- _app = [ app, {
+ _app = [app, {
secret: secret, legacy_hmac_secret: legacy_secret,
- legacy_hmac_coder: unsafe_coder } ]
+ legacy_hmac_coder: unsafe_coder
+ }]
response = response_for(app: _app, cookie: legacy_cookie)
- expect(response.body).to eq("1--2--3--")
+ expect(response.body).to eq('1--2--3--')
end
it 'allows for non-strict encoded cookie' do
long_session_app = lambda do |env|
env['rack.session']['value'] = 'A' * 256
env['rack.session']['counter'] = 1
- hash = env["rack.session"].dup
- hash.delete("session_id")
+ hash = env['rack.session'].dup
+ hash.delete('session_id')
Rack::Response.new(hash.inspect).to_a
end
- non_strict_coder = Class.new {
+ non_strict_coder = Class.new do
def encode(str)
[Marshal.dump(str)].pack('m')
end
@@ -530,19 +543,19 @@ def encode(str)
def decode(str)
return unless str
- Marshal.load(str.unpack('m').first)
+ Marshal.load(str.unpack1('m'))
end
- }.new
+ end.new
non_strict_response = response_for(app: [
- long_session_app, { coder: non_strict_coder }
- ])
+ long_session_app, { coder: non_strict_coder }
+ ])
response = response_for(app: [
- incrementor
- ], cookie: non_strict_response)
+ incrementor
+ ], cookie: non_strict_response)
- expect(response.body).to match(%Q["value"=>"#{'A' * 256}"])
+ expect(response.body).to match(%("value"=>"#{'A' * 256}"))
expect(response.body).to match('"counter"=>2')
expect(response.body).to match(/\A{[^}]+}\z/)
end
diff --git a/rack-protection/spec/lib/rack/protection/encryptor_spec.rb b/rack-protection/spec/lib/rack/protection/encryptor_spec.rb
index ce11d75e74..cb7a2a401f 100644
--- a/rack-protection/spec/lib/rack/protection/encryptor_spec.rb
+++ b/rack-protection/spec/lib/rack/protection/encryptor_spec.rb
@@ -1,7 +1,9 @@
+# frozen_string_literal: true
+
RSpec.describe Rack::Protection::Encryptor do
- let(:secret) {
+ let(:secret) do
OpenSSL::Cipher.new(Rack::Protection::Encryptor::CIPHER).random_key
- }
+ end
it 'encrypted message contains ciphertext iv and auth_tag' do
msg = Rack::Protection::Encryptor.encrypt_message('hello world', secret)
diff --git a/rack-protection/spec/lib/rack/protection/escaped_params_spec.rb b/rack-protection/spec/lib/rack/protection/escaped_params_spec.rb
index 01c4f3a672..d6b1727b3e 100644
--- a/rack-protection/spec/lib/rack/protection/escaped_params_spec.rb
+++ b/rack-protection/spec/lib/rack/protection/escaped_params_spec.rb
@@ -1,37 +1,39 @@
+# frozen_string_literal: true
+
RSpec.describe Rack::Protection::EscapedParams do
- it_behaves_like "any rack application"
+ it_behaves_like 'any rack application'
context 'escaping' do
it 'escapes html entities' do
mock_app do |env|
request = Rack::Request.new(env)
- [200, {'Content-Type' => 'text/plain'}, [request.params['foo']]]
+ [200, { 'Content-Type' => 'text/plain' }, [request.params['foo']]]
end
- get '/', :foo => ""
+ get '/', foo: ''
expect(body).to eq('<bar>')
end
it 'leaves normal params untouched' do
mock_app do |env|
request = Rack::Request.new(env)
- [200, {'Content-Type' => 'text/plain'}, [request.params['foo']]]
+ [200, { 'Content-Type' => 'text/plain' }, [request.params['foo']]]
end
- get '/', :foo => "bar"
+ get '/', foo: 'bar'
expect(body).to eq('bar')
end
it 'copes with nested arrays' do
mock_app do |env|
request = Rack::Request.new(env)
- [200, {'Content-Type' => 'text/plain'}, [request.params['foo']['bar']]]
+ [200, { 'Content-Type' => 'text/plain' }, [request.params['foo']['bar']]]
end
- get '/', :foo => {:bar => ""}
+ get '/', foo: { bar: '' }
expect(body).to eq('<bar>')
end
it 'leaves cache-breaker params untouched' do
- mock_app do |env|
- [200, {'Content-Type' => 'text/plain'}, ['hi']]
+ mock_app do |_env|
+ [200, { 'Content-Type' => 'text/plain' }, ['hi']]
end
get '/?95df8d9bf5237ad08df3115ee74dcb10'
@@ -41,9 +43,7 @@
it 'leaves TempFiles untouched' do
mock_app do |env|
request = Rack::Request.new(env)
- [200, {'Content-Type' => 'text/plain'}, [request.params['file'][:filename] + "\n" + \
- request.params['file'][:tempfile].read + "\n" + \
- request.params['other']]]
+ [200, { 'Content-Type' => 'text/plain' }, ["#{request.params['file'][:filename]}\n#{request.params['file'][:tempfile].read}\n#{request.params['other']}"]]
end
temp_file = File.open('_escaped_params_tmp_file', 'w')
@@ -51,7 +51,7 @@
temp_file.write('hello world')
temp_file.close
- post '/', :file => Rack::Test::UploadedFile.new(temp_file.path), :other => ''
+ post '/', file: Rack::Test::UploadedFile.new(temp_file.path), other: ''
expect(body).to eq("_escaped_params_tmp_file\nhello world\n<bar>")
ensure
File.unlink(temp_file.path)
diff --git a/rack-protection/spec/lib/rack/protection/form_token_spec.rb b/rack-protection/spec/lib/rack/protection/form_token_spec.rb
index b9a340da8a..b85923a99f 100644
--- a/rack-protection/spec/lib/rack/protection/form_token_spec.rb
+++ b/rack-protection/spec/lib/rack/protection/form_token_spec.rb
@@ -1,46 +1,48 @@
+# frozen_string_literal: true
+
RSpec.describe Rack::Protection::FormToken do
let(:token) { described_class.random_token }
let(:masked_token) { described_class.token(session) }
- let(:bad_token) { Base64.strict_encode64("badtoken") }
- let(:session) { {:csrf => token} }
+ let(:bad_token) { Base64.strict_encode64('badtoken') }
+ let(:session) { { csrf: token } }
- it_behaves_like "any rack application"
+ it_behaves_like 'any rack application'
- it "denies post requests without any token" do
+ it 'denies post requests without any token' do
expect(post('/')).not_to be_ok
end
- it "accepts post requests with correct X-CSRF-Token header" do
+ it 'accepts post requests with correct X-CSRF-Token header' do
post('/', {}, 'rack.session' => session, 'HTTP_X_CSRF_TOKEN' => token)
expect(last_response).to be_ok
end
- it "accepts post requests with masked X-CSRF-Token header" do
+ it 'accepts post requests with masked X-CSRF-Token header' do
post('/', {}, 'rack.session' => session, 'HTTP_X_CSRF_TOKEN' => masked_token)
expect(last_response).to be_ok
end
- it "denies post requests with wrong X-CSRF-Token header" do
+ it 'denies post requests with wrong X-CSRF-Token header' do
post('/', {}, 'rack.session' => session, 'HTTP_X_CSRF_TOKEN' => bad_token)
expect(last_response).not_to be_ok
end
- it "accepts post form requests with correct authenticity_token field" do
- post('/', {"authenticity_token" => token}, 'rack.session' => session)
+ it 'accepts post form requests with correct authenticity_token field' do
+ post('/', { 'authenticity_token' => token }, 'rack.session' => session)
expect(last_response).to be_ok
end
- it "accepts post form requests with masked authenticity_token field" do
- post('/', {"authenticity_token" => masked_token}, 'rack.session' => session)
+ it 'accepts post form requests with masked authenticity_token field' do
+ post('/', { 'authenticity_token' => masked_token }, 'rack.session' => session)
expect(last_response).to be_ok
end
- it "denies post form requests with wrong authenticity_token field" do
- post('/', {"authenticity_token" => bad_token}, 'rack.session' => session)
+ it 'denies post form requests with wrong authenticity_token field' do
+ post('/', { 'authenticity_token' => bad_token }, 'rack.session' => session)
expect(last_response).not_to be_ok
end
- it "accepts ajax requests without a valid token" do
- expect(post('/', {}, "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest")).to be_ok
+ it 'accepts ajax requests without a valid token' do
+ expect(post('/', {}, 'HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest')).to be_ok
end
end
diff --git a/rack-protection/spec/lib/rack/protection/frame_options_spec.rb b/rack-protection/spec/lib/rack/protection/frame_options_spec.rb
index 367bf36414..23ba8f11ce 100644
--- a/rack-protection/spec/lib/rack/protection/frame_options_spec.rb
+++ b/rack-protection/spec/lib/rack/protection/frame_options_spec.rb
@@ -1,37 +1,38 @@
+# frozen_string_literal: true
+
RSpec.describe Rack::Protection::FrameOptions do
- it_behaves_like "any rack application"
+ it_behaves_like 'any rack application'
it 'should set the X-Frame-Options' do
- expect(get('/', {}, 'wants' => 'text/html').headers["X-Frame-Options"]).to eq("SAMEORIGIN")
+ expect(get('/', {}, 'wants' => 'text/html').headers['X-Frame-Options']).to eq('SAMEORIGIN')
end
it 'should not set the X-Frame-Options for other content types' do
- expect(get('/', {}, 'wants' => 'text/foo').headers["X-Frame-Options"]).to be_nil
+ expect(get('/', {}, 'wants' => 'text/foo').headers['X-Frame-Options']).to be_nil
end
it 'should allow changing the protection mode' do
# I have no clue what other modes are available
mock_app do
- use Rack::Protection::FrameOptions, :frame_options => :deny
+ use Rack::Protection::FrameOptions, frame_options: :deny
run DummyApp
end
- expect(get('/', {}, 'wants' => 'text/html').headers["X-Frame-Options"]).to eq("DENY")
+ expect(get('/', {}, 'wants' => 'text/html').headers['X-Frame-Options']).to eq('DENY')
end
-
it 'should allow changing the protection mode to a string' do
# I have no clue what other modes are available
mock_app do
- use Rack::Protection::FrameOptions, :frame_options => "ALLOW-FROM foo"
+ use Rack::Protection::FrameOptions, frame_options: 'ALLOW-FROM foo'
run DummyApp
end
- expect(get('/', {}, 'wants' => 'text/html').headers["X-Frame-Options"]).to eq("ALLOW-FROM foo")
+ expect(get('/', {}, 'wants' => 'text/html').headers['X-Frame-Options']).to eq('ALLOW-FROM foo')
end
it 'should not override the header if already set' do
- mock_app with_headers("X-Frame-Options" => "allow")
- expect(get('/', {}, 'wants' => 'text/html').headers["X-Frame-Options"]).to eq("allow")
+ mock_app with_headers('X-Frame-Options' => 'allow')
+ expect(get('/', {}, 'wants' => 'text/html').headers['X-Frame-Options']).to eq('allow')
end
end
diff --git a/rack-protection/spec/lib/rack/protection/http_origin_spec.rb b/rack-protection/spec/lib/rack/protection/http_origin_spec.rb
index bc8a77239b..0471dd85aa 100644
--- a/rack-protection/spec/lib/rack/protection/http_origin_spec.rb
+++ b/rack-protection/spec/lib/rack/protection/http_origin_spec.rb
@@ -1,5 +1,7 @@
+# frozen_string_literal: true
+
RSpec.describe Rack::Protection::HttpOrigin do
- it_behaves_like "any rack application"
+ it_behaves_like 'any rack application'
before(:each) do
mock_app do
@@ -8,29 +10,29 @@
end
end
- %w(GET HEAD POST PUT DELETE).each do |method|
+ %w[GET HEAD POST PUT DELETE].each do |method|
it "accepts #{method} requests with no Origin" do
expect(send(method.downcase, '/')).to be_ok
end
end
- %w(GET HEAD).each do |method|
+ %w[GET HEAD].each do |method|
it "accepts #{method} requests with non-permitted Origin" do
expect(send(method.downcase, '/', {}, 'HTTP_ORIGIN' => 'http://malicious.com')).to be_ok
end
end
- %w(GET HEAD POST PUT DELETE).each do |method|
+ %w[GET HEAD POST PUT DELETE].each do |method|
it "accepts #{method} requests when allow_if is true" do
mock_app do
- use Rack::Protection::HttpOrigin, :allow_if => lambda{|env| env.has_key?('HTTP_ORIGIN') }
+ use Rack::Protection::HttpOrigin, allow_if: ->(env) { env.key?('HTTP_ORIGIN') }
run DummyApp
end
expect(send(method.downcase, '/', {}, 'HTTP_ORIGIN' => 'http://any.domain.com')).to be_ok
end
end
- %w(POST PUT DELETE).each do |method|
+ %w[POST PUT DELETE].each do |method|
it "denies #{method} requests with non-permitted Origin" do
expect(send(method.downcase, '/', {}, 'HTTP_ORIGIN' => 'http://malicious.com')).not_to be_ok
end
diff --git a/rack-protection/spec/lib/rack/protection/ip_spoofing_spec.rb b/rack-protection/spec/lib/rack/protection/ip_spoofing_spec.rb
index b3a9109438..2cb201f6e7 100644
--- a/rack-protection/spec/lib/rack/protection/ip_spoofing_spec.rb
+++ b/rack-protection/spec/lib/rack/protection/ip_spoofing_spec.rb
@@ -1,5 +1,7 @@
+# frozen_string_literal: true
+
RSpec.describe Rack::Protection::IPSpoofing do
- it_behaves_like "any rack application"
+ it_behaves_like 'any rack application'
it 'accepts requests without X-Forward-For header' do
get('/', {}, 'HTTP_CLIENT_IP' => '1.2.3.4', 'HTTP_X_REAL_IP' => '4.3.2.1')
@@ -8,7 +10,7 @@
it 'accepts requests with proper X-Forward-For header' do
get('/', {}, 'HTTP_CLIENT_IP' => '1.2.3.4',
- 'HTTP_X_FORWARDED_FOR' => '192.168.1.20, 1.2.3.4, 127.0.0.1')
+ 'HTTP_X_FORWARDED_FOR' => '192.168.1.20, 1.2.3.4, 127.0.0.1')
expect(last_response).to be_ok
end
@@ -19,15 +21,15 @@
it 'denies requests where the client spoofs the IP but not X-Forward-For' do
get('/', {}, 'HTTP_CLIENT_IP' => '1.2.3.5',
- 'HTTP_X_FORWARDED_FOR' => '192.168.1.20, 1.2.3.4, 127.0.0.1')
+ 'HTTP_X_FORWARDED_FOR' => '192.168.1.20, 1.2.3.4, 127.0.0.1')
expect(last_response).not_to be_ok
end
it 'denies requests where IP and X-Forward-For are spoofed but not X-Real-IP' do
get('/', {},
- 'HTTP_CLIENT_IP' => '1.2.3.5',
- 'HTTP_X_FORWARDED_FOR' => '1.2.3.5',
- 'HTTP_X_REAL_IP' => '1.2.3.4')
+ 'HTTP_CLIENT_IP' => '1.2.3.5',
+ 'HTTP_X_FORWARDED_FOR' => '1.2.3.5',
+ 'HTTP_X_REAL_IP' => '1.2.3.4')
expect(last_response).not_to be_ok
end
end
diff --git a/rack-protection/spec/lib/rack/protection/json_csrf_spec.rb b/rack-protection/spec/lib/rack/protection/json_csrf_spec.rb
index fb45b8405d..06312665a3 100644
--- a/rack-protection/spec/lib/rack/protection/json_csrf_spec.rb
+++ b/rack-protection/spec/lib/rack/protection/json_csrf_spec.rb
@@ -1,5 +1,7 @@
+# frozen_string_literal: true
+
RSpec.describe Rack::Protection::JsonCsrf do
- it_behaves_like "any rack application"
+ it_behaves_like 'any rack application'
module DummyAppWithBody
module Closeable
@@ -22,22 +24,22 @@ def self.body
def self.call(env)
Thread.current[:last_env] = env
- [200, {'Content-Type' => 'application/json'}, body]
+ [200, { 'Content-Type' => 'application/json' }, body]
end
end
describe 'json response' do
before do
- mock_app { |e| [200, {'Content-Type' => 'application/json'}, []]}
+ mock_app { |_e| [200, { 'Content-Type' => 'application/json' }, []] }
end
- it "denies get requests with json responses with a remote referrer" do
+ it 'denies get requests with json responses with a remote referrer' do
expect(get('/', {}, 'HTTP_REFERER' => 'http://evil.com')).not_to be_ok
end
- it "closes the body returned by the app if it denies the get request" do
- mock_app DummyAppWithBody do |e|
- [200, {'Content-Type' => 'application/json'}, []]
+ it 'closes the body returned by the app if it denies the get request' do
+ mock_app DummyAppWithBody do |_e|
+ [200, { 'Content-Type' => 'application/json' }, []]
end
get('/', {}, 'HTTP_REFERER' => 'http://evil.com')
@@ -45,10 +47,10 @@ def self.call(env)
expect(DummyAppWithBody.body).to be_closed
end
- it "accepts requests with json responses with a remote referrer when allow_if is true" do
+ it 'accepts requests with json responses with a remote referrer when allow_if is true' do
mock_app do
- use Rack::Protection::JsonCsrf, :allow_if => lambda{|env| env['HTTP_REFERER'] == 'http://good.com'}
- run proc { |e| [200, {'Content-Type' => 'application/json'}, []]}
+ use Rack::Protection::JsonCsrf, allow_if: ->(env) { env['HTTP_REFERER'] == 'http://good.com' }
+ run proc { |_e| [200, { 'Content-Type' => 'application/json' }, []] }
end
expect(get('/', {}, 'HTTP_REFERER' => 'http://good.com')).to be_ok
@@ -62,37 +64,34 @@ def self.call(env)
expect(get('/', {}, 'HTTP_REFERER' => 'http://good.com', 'HTTP_X_ORIGIN' => 'http://good.com')).to be_ok
end
- it "accepts get requests with json responses with a local referrer" do
+ it 'accepts get requests with json responses with a local referrer' do
expect(get('/', {}, 'HTTP_REFERER' => '/')).to be_ok
end
- it "accepts get requests with json responses with no referrer" do
+ it 'accepts get requests with json responses with no referrer' do
expect(get('/', {})).to be_ok
end
- it "accepts XHR requests" do
+ it 'accepts XHR requests' do
expect(get('/', {}, 'HTTP_REFERER' => 'http://evil.com', 'HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest')).to be_ok
end
-
end
describe 'not json response' do
-
- it "accepts get requests with 304 headers" do
- mock_app { |e| [304, {}, []]}
+ it 'accepts get requests with 304 headers' do
+ mock_app { |_e| [304, {}, []] }
expect(get('/', {}).status).to eq(304)
end
-
end
describe 'with drop_session as default reaction' do
it 'still denies' do
mock_app do
- use Rack::Protection, :reaction => :drop_session
- run proc { |e| [200, {'Content-Type' => 'application/json'}, []]}
+ use Rack::Protection, reaction: :drop_session
+ run proc { |_e| [200, { 'Content-Type' => 'application/json' }, []] }
end
- session = {:foo => :bar}
+ session = { foo: :bar }
get('/', {}, 'HTTP_REFERER' => 'http://evil.com', 'rack.session' => session)
expect(last_response).not_to be_ok
end
diff --git a/rack-protection/spec/lib/rack/protection/path_traversal_spec.rb b/rack-protection/spec/lib/rack/protection/path_traversal_spec.rb
index fba876d9ce..1eca34bdc8 100644
--- a/rack-protection/spec/lib/rack/protection/path_traversal_spec.rb
+++ b/rack-protection/spec/lib/rack/protection/path_traversal_spec.rb
@@ -1,9 +1,11 @@
+# frozen_string_literal: true
+
RSpec.describe Rack::Protection::PathTraversal do
- it_behaves_like "any rack application"
+ it_behaves_like 'any rack application'
context 'escaping' do
before do
- mock_app { |e| [200, {'Content-Type' => 'text/plain'}, [e['PATH_INFO']]] }
+ mock_app { |e| [200, { 'Content-Type' => 'text/plain' }, [e['PATH_INFO']]] }
end
%w[/foo/bar /foo/bar/ / /.f /a.x].each do |path|
@@ -26,7 +28,7 @@
context "PATH_INFO's encoding" do
before do
- @app = Rack::Protection::PathTraversal.new(proc { |e| [200, {'Content-Type' => 'text/plain'}, [e['PATH_INFO'].encoding.to_s]] })
+ @app = Rack::Protection::PathTraversal.new(proc { |e| [200, { 'Content-Type' => 'text/plain' }, [e['PATH_INFO'].encoding.to_s]] })
end
it 'should remain unchanged as ASCII-8BIT' do
diff --git a/rack-protection/spec/lib/rack/protection/protection_spec.rb b/rack-protection/spec/lib/rack/protection/protection_spec.rb
index 9bdb34e063..e8dc55df5d 100644
--- a/rack-protection/spec/lib/rack/protection/protection_spec.rb
+++ b/rack-protection/spec/lib/rack/protection/protection_spec.rb
@@ -1,13 +1,15 @@
+# frozen_string_literal: true
+
RSpec.describe Rack::Protection do
- it_behaves_like "any rack application"
+ it_behaves_like 'any rack application'
it 'passes on options' do
mock_app do
- use Rack::Protection, :track => ['HTTP_FOO']
- run proc { |e| [200, {'Content-Type' => 'text/plain'}, ['hi']] }
+ use Rack::Protection, track: ['HTTP_FOO']
+ run proc { |_e| [200, { 'Content-Type' => 'text/plain' }, ['hi']] }
end
- session = {:foo => :bar}
+ session = { foo: :bar }
get '/', {}, 'rack.session' => session, 'HTTP_ACCEPT_ENCODING' => 'a'
get '/', {}, 'rack.session' => session, 'HTTP_ACCEPT_ENCODING' => 'b'
expect(session[:foo]).to eq(:bar)
@@ -18,21 +20,21 @@
it 'passes errors through if :reaction => :report is used' do
mock_app do
- use Rack::Protection, :reaction => :report
- run proc { |e| [200, {'Content-Type' => 'text/plain'}, [e["protection.failed"].to_s]] }
+ use Rack::Protection, reaction: :report
+ run proc { |e| [200, { 'Content-Type' => 'text/plain' }, [e['protection.failed'].to_s]] }
end
- session = {:foo => :bar}
+ session = { foo: :bar }
post('/', {}, 'rack.session' => session, 'HTTP_ORIGIN' => 'http://malicious.com')
expect(last_response).to be_ok
- expect(body).to eq("true")
+ expect(body).to eq('true')
end
- describe "#react" do
+ describe '#react' do
it 'prevents attacks and warns about it' do
io = StringIO.new
mock_app do
- use Rack::Protection, :logger => Logger.new(io)
+ use Rack::Protection, logger: Logger.new(io)
run DummyApp
end
post('/', {}, 'rack.session' => {}, 'HTTP_ORIGIN' => 'http://malicious.com')
@@ -42,7 +44,7 @@
it 'reports attacks if reaction is to report' do
io = StringIO.new
mock_app do
- use Rack::Protection, :reaction => :report, :logger => Logger.new(io)
+ use Rack::Protection, reaction: :report, logger: Logger.new(io)
run DummyApp
end
post('/', {}, 'rack.session' => {}, 'HTTP_ORIGIN' => 'http://malicious.com')
@@ -54,7 +56,7 @@
io = StringIO.new
Rack::Protection::Base.send(:define_method, :special) { |*args| io << args.inspect }
mock_app do
- use Rack::Protection, :reaction => :special, :logger => Logger.new(io)
+ use Rack::Protection, reaction: :special, logger: Logger.new(io)
run DummyApp
end
post('/', {}, 'rack.session' => {}, 'HTTP_ORIGIN' => 'http://malicious.com')
@@ -63,34 +65,34 @@
end
end
- describe "#html?" do
- context "given an appropriate content-type header" do
- subject { Rack::Protection::Base.new(nil).html? 'content-type' => "text/html" }
+ describe '#html?' do
+ context 'given an appropriate content-type header' do
+ subject { Rack::Protection::Base.new(nil).html? 'content-type' => 'text/html' }
it { is_expected.to be_truthy }
end
- context "given an appropriate content-type header of text/xml" do
- subject { Rack::Protection::Base.new(nil).html? 'content-type' => "text/xml" }
+ context 'given an appropriate content-type header of text/xml' do
+ subject { Rack::Protection::Base.new(nil).html? 'content-type' => 'text/xml' }
it { is_expected.to be_truthy }
end
- context "given an appropriate content-type header of application/xml" do
- subject { Rack::Protection::Base.new(nil).html? 'content-type' => "application/xml" }
+ context 'given an appropriate content-type header of application/xml' do
+ subject { Rack::Protection::Base.new(nil).html? 'content-type' => 'application/xml' }
it { is_expected.to be_truthy }
end
- context "given an inappropriate content-type header" do
- subject { Rack::Protection::Base.new(nil).html? 'content-type' => "image/gif" }
+ context 'given an inappropriate content-type header' do
+ subject { Rack::Protection::Base.new(nil).html? 'content-type' => 'image/gif' }
it { is_expected.to be_falsey }
end
- context "given no content-type header" do
+ context 'given no content-type header' do
subject { Rack::Protection::Base.new(nil).html?({}) }
it { is_expected.to be_falsey }
end
end
- describe "#instrument" do
+ describe '#instrument' do
let(:env) { { 'rack.protection.attack' => 'base' } }
let(:instrumenter) { double('Instrumenter') }
@@ -99,7 +101,7 @@
end
context 'with an instrumenter specified' do
- let(:app) { Rack::Protection::Base.new(nil, :instrumenter => instrumenter) }
+ let(:app) { Rack::Protection::Base.new(nil, instrumenter: instrumenter) }
it { expect(instrumenter).to receive(:instrument).with('rack.protection', env) }
end
@@ -111,14 +113,14 @@
end
end
- describe "new" do
+ describe 'new' do
it 'should allow disable session protection' do
mock_app do
- use Rack::Protection, :without_session => true
+ use Rack::Protection, without_session: true
run DummyApp
end
- session = {:foo => :bar}
+ session = { foo: :bar }
get '/', {}, 'rack.session' => session, 'HTTP_USER_AGENT' => 'a'
get '/', {}, 'rack.session' => session, 'HTTP_USER_AGENT' => 'b'
expect(session[:foo]).to eq :bar
@@ -126,7 +128,7 @@
it 'should allow disable CSRF protection' do
mock_app do
- use Rack::Protection, :without_session => true
+ use Rack::Protection, without_session: true
run DummyApp
end
diff --git a/rack-protection/spec/lib/rack/protection/remote_referrer_spec.rb b/rack-protection/spec/lib/rack/protection/remote_referrer_spec.rb
index 35512b9df9..92165ecaf4 100644
--- a/rack-protection/spec/lib/rack/protection/remote_referrer_spec.rb
+++ b/rack-protection/spec/lib/rack/protection/remote_referrer_spec.rb
@@ -1,28 +1,30 @@
+# frozen_string_literal: true
+
RSpec.describe Rack::Protection::RemoteReferrer do
- it_behaves_like "any rack application"
+ it_behaves_like 'any rack application'
- it "accepts post requests with no referrer" do
+ it 'accepts post requests with no referrer' do
expect(post('/')).to be_ok
end
- it "does not accept post requests with no referrer if allow_empty_referrer is false" do
+ it 'does not accept post requests with no referrer if allow_empty_referrer is false' do
mock_app do
- use Rack::Protection::RemoteReferrer, :allow_empty_referrer => false
+ use Rack::Protection::RemoteReferrer, allow_empty_referrer: false
run DummyApp
end
expect(post('/')).not_to be_ok
end
- it "should allow post request with a relative referrer" do
+ it 'should allow post request with a relative referrer' do
expect(post('/', {}, 'HTTP_REFERER' => '/')).to be_ok
end
- it "accepts post requests with the same host in the referrer" do
+ it 'accepts post requests with the same host in the referrer' do
post('/', {}, 'HTTP_REFERER' => 'http://example.com/foo', 'HTTP_HOST' => 'example.com')
expect(last_response).to be_ok
end
- it "denies post requests with a remote referrer" do
+ it 'denies post requests with a remote referrer' do
post('/', {}, 'HTTP_REFERER' => 'http://example.com/foo', 'HTTP_HOST' => 'example.org')
expect(last_response).not_to be_ok
end
diff --git a/rack-protection/spec/lib/rack/protection/remote_token_spec.rb b/rack-protection/spec/lib/rack/protection/remote_token_spec.rb
index bc04f1ade9..8693ba1c68 100644
--- a/rack-protection/spec/lib/rack/protection/remote_token_spec.rb
+++ b/rack-protection/spec/lib/rack/protection/remote_token_spec.rb
@@ -1,57 +1,59 @@
+# frozen_string_literal: true
+
RSpec.describe Rack::Protection::RemoteToken do
let(:token) { described_class.random_token }
let(:masked_token) { described_class.token(session) }
- let(:bad_token) { Base64.strict_encode64("badtoken") }
- let(:session) { {:csrf => token} }
+ let(:bad_token) { Base64.strict_encode64('badtoken') }
+ let(:session) { { csrf: token } }
- it_behaves_like "any rack application"
+ it_behaves_like 'any rack application'
- it "accepts post requests with no referrer" do
+ it 'accepts post requests with no referrer' do
expect(post('/')).to be_ok
end
- it "accepts post requests with a local referrer" do
+ it 'accepts post requests with a local referrer' do
expect(post('/', {}, 'HTTP_REFERER' => '/')).to be_ok
end
- it "denies post requests with a remote referrer and no token" do
+ it 'denies post requests with a remote referrer and no token' do
post('/', {}, 'HTTP_REFERER' => 'http://example.com/foo', 'HTTP_HOST' => 'example.org')
expect(last_response).not_to be_ok
end
- it "accepts post requests with a remote referrer and correct X-CSRF-Token header" do
+ it 'accepts post requests with a remote referrer and correct X-CSRF-Token header' do
post('/', {}, 'HTTP_REFERER' => 'http://example.com/foo', 'HTTP_HOST' => 'example.org',
- 'rack.session' => session, 'HTTP_X_CSRF_TOKEN' => token)
+ 'rack.session' => session, 'HTTP_X_CSRF_TOKEN' => token)
expect(last_response).to be_ok
end
- it "accepts post requests with a remote referrer and masked X-CSRF-Token header" do
+ it 'accepts post requests with a remote referrer and masked X-CSRF-Token header' do
post('/', {}, 'HTTP_REFERER' => 'http://example.com/foo', 'HTTP_HOST' => 'example.org',
- 'rack.session' => session, 'HTTP_X_CSRF_TOKEN' => masked_token)
+ 'rack.session' => session, 'HTTP_X_CSRF_TOKEN' => masked_token)
expect(last_response).to be_ok
end
- it "denies post requests with a remote referrer and wrong X-CSRF-Token header" do
+ it 'denies post requests with a remote referrer and wrong X-CSRF-Token header' do
post('/', {}, 'HTTP_REFERER' => 'http://example.com/foo', 'HTTP_HOST' => 'example.org',
- 'rack.session' => session, 'HTTP_X_CSRF_TOKEN' => bad_token)
+ 'rack.session' => session, 'HTTP_X_CSRF_TOKEN' => bad_token)
expect(last_response).not_to be_ok
end
- it "accepts post form requests with a remote referrer and correct authenticity_token field" do
- post('/', {"authenticity_token" => token}, 'HTTP_REFERER' => 'http://example.com/foo',
- 'HTTP_HOST' => 'example.org', 'rack.session' => session)
+ it 'accepts post form requests with a remote referrer and correct authenticity_token field' do
+ post('/', { 'authenticity_token' => token }, 'HTTP_REFERER' => 'http://example.com/foo',
+ 'HTTP_HOST' => 'example.org', 'rack.session' => session)
expect(last_response).to be_ok
end
- it "accepts post form requests with a remote referrer and masked authenticity_token field" do
- post('/', {"authenticity_token" => masked_token}, 'HTTP_REFERER' => 'http://example.com/foo',
- 'HTTP_HOST' => 'example.org', 'rack.session' => session)
+ it 'accepts post form requests with a remote referrer and masked authenticity_token field' do
+ post('/', { 'authenticity_token' => masked_token }, 'HTTP_REFERER' => 'http://example.com/foo',
+ 'HTTP_HOST' => 'example.org', 'rack.session' => session)
expect(last_response).to be_ok
end
- it "denies post form requests with a remote referrer and wrong authenticity_token field" do
- post('/', {"authenticity_token" => bad_token}, 'HTTP_REFERER' => 'http://example.com/foo',
- 'HTTP_HOST' => 'example.org', 'rack.session' => session)
+ it 'denies post form requests with a remote referrer and wrong authenticity_token field' do
+ post('/', { 'authenticity_token' => bad_token }, 'HTTP_REFERER' => 'http://example.com/foo',
+ 'HTTP_HOST' => 'example.org', 'rack.session' => session)
expect(last_response).not_to be_ok
end
end
diff --git a/rack-protection/spec/lib/rack/protection/session_hijacking_spec.rb b/rack-protection/spec/lib/rack/protection/session_hijacking_spec.rb
index 0a96c23075..e39497b1a9 100644
--- a/rack-protection/spec/lib/rack/protection/session_hijacking_spec.rb
+++ b/rack-protection/spec/lib/rack/protection/session_hijacking_spec.rb
@@ -1,30 +1,32 @@
+# frozen_string_literal: true
+
RSpec.describe Rack::Protection::SessionHijacking do
- it_behaves_like "any rack application"
+ it_behaves_like 'any rack application'
- it "accepts a session without changes to tracked parameters" do
- session = {:foo => :bar}
+ it 'accepts a session without changes to tracked parameters' do
+ session = { foo: :bar }
get '/', {}, 'rack.session' => session
get '/', {}, 'rack.session' => session
expect(session[:foo]).to eq(:bar)
end
- it "denies requests with a changing User-Agent header" do
- session = {:foo => :bar}
+ it 'denies requests with a changing User-Agent header' do
+ session = { foo: :bar }
get '/', {}, 'rack.session' => session, 'HTTP_USER_AGENT' => 'a'
get '/', {}, 'rack.session' => session, 'HTTP_USER_AGENT' => 'b'
expect(session).to be_empty
end
- it "accepts requests with a changing Accept-Encoding header" do
+ it 'accepts requests with a changing Accept-Encoding header' do
# this is tested because previously it led to clearing the session
- session = {:foo => :bar}
+ session = { foo: :bar }
get '/', {}, 'rack.session' => session, 'HTTP_ACCEPT_ENCODING' => 'a'
get '/', {}, 'rack.session' => session, 'HTTP_ACCEPT_ENCODING' => 'b'
expect(session).not_to be_empty
end
- it "accepts requests with a changing Version header"do
- session = {:foo => :bar}
+ it 'accepts requests with a changing Version header' do
+ session = { foo: :bar }
get '/', {}, 'rack.session' => session, 'HTTP_VERSION' => '1.0'
get '/', {}, 'rack.session' => session, 'HTTP_VERSION' => '1.1'
expect(session[:foo]).to eq(:bar)
diff --git a/rack-protection/spec/lib/rack/protection/strict_transport_spec.rb b/rack-protection/spec/lib/rack/protection/strict_transport_spec.rb
index 21cba97aa5..62602fa03a 100644
--- a/rack-protection/spec/lib/rack/protection/strict_transport_spec.rb
+++ b/rack-protection/spec/lib/rack/protection/strict_transport_spec.rb
@@ -1,43 +1,45 @@
+# frozen_string_literal: true
+
RSpec.describe Rack::Protection::StrictTransport do
- it_behaves_like "any rack application"
+ it_behaves_like 'any rack application'
it 'should set the Strict-Transport-Security header' do
- expect(get('/', {}, 'wants' => 'text/html').headers["Strict-Transport-Security"]).to eq("max-age=31536000")
+ expect(get('/', {}, 'wants' => 'text/html').headers['Strict-Transport-Security']).to eq('max-age=31536000')
end
it 'should allow changing the max-age option' do
mock_app do
- use Rack::Protection::StrictTransport, :max_age => 16_070_400
+ use Rack::Protection::StrictTransport, max_age: 16_070_400
run DummyApp
end
- expect(get('/', {}, 'wants' => 'text/html').headers["Strict-Transport-Security"]).to eq("max-age=16070400")
+ expect(get('/', {}, 'wants' => 'text/html').headers['Strict-Transport-Security']).to eq('max-age=16070400')
end
it 'should allow switching on the include_subdomains option' do
mock_app do
- use Rack::Protection::StrictTransport, :include_subdomains => true
+ use Rack::Protection::StrictTransport, include_subdomains: true
run DummyApp
end
- expect(get('/', {}, 'wants' => 'text/html').headers["Strict-Transport-Security"]).to eq("max-age=31536000; includeSubDomains")
+ expect(get('/', {}, 'wants' => 'text/html').headers['Strict-Transport-Security']).to eq('max-age=31536000; includeSubDomains')
end
it 'should allow switching on the preload option' do
mock_app do
- use Rack::Protection::StrictTransport, :preload => true
+ use Rack::Protection::StrictTransport, preload: true
run DummyApp
end
- expect(get('/', {}, 'wants' => 'text/html').headers["Strict-Transport-Security"]).to eq("max-age=31536000; preload")
+ expect(get('/', {}, 'wants' => 'text/html').headers['Strict-Transport-Security']).to eq('max-age=31536000; preload')
end
it 'should allow switching on all the options' do
mock_app do
- use Rack::Protection::StrictTransport, :preload => true, :include_subdomains => true
+ use Rack::Protection::StrictTransport, preload: true, include_subdomains: true
run DummyApp
end
- expect(get('/', {}, 'wants' => 'text/html').headers["Strict-Transport-Security"]).to eq("max-age=31536000; includeSubDomains; preload")
+ expect(get('/', {}, 'wants' => 'text/html').headers['Strict-Transport-Security']).to eq('max-age=31536000; includeSubDomains; preload')
end
end
diff --git a/rack-protection/spec/lib/rack/protection/xss_header_spec.rb b/rack-protection/spec/lib/rack/protection/xss_header_spec.rb
index 1b5d3c03bf..2fef0edbf7 100644
--- a/rack-protection/spec/lib/rack/protection/xss_header_spec.rb
+++ b/rack-protection/spec/lib/rack/protection/xss_header_spec.rb
@@ -1,54 +1,54 @@
+# frozen_string_literal: true
+
RSpec.describe Rack::Protection::XSSHeader do
- it_behaves_like "any rack application"
+ it_behaves_like 'any rack application'
it 'should set the X-XSS-Protection' do
- expect(get('/', {}, 'wants' => 'text/html;charset=utf-8').headers["X-XSS-Protection"]).to eq("1; mode=block")
+ expect(get('/', {}, 'wants' => 'text/html;charset=utf-8').headers['X-XSS-Protection']).to eq('1; mode=block')
end
it 'should set the X-XSS-Protection for XHTML' do
- expect(get('/', {}, 'wants' => 'application/xhtml+xml').headers["X-XSS-Protection"]).to eq("1; mode=block")
+ expect(get('/', {}, 'wants' => 'application/xhtml+xml').headers['X-XSS-Protection']).to eq('1; mode=block')
end
it 'should not set the X-XSS-Protection for other content types' do
- expect(get('/', {}, 'wants' => 'application/foo').headers["X-XSS-Protection"]).to be_nil
+ expect(get('/', {}, 'wants' => 'application/foo').headers['X-XSS-Protection']).to be_nil
end
it 'should allow changing the protection mode' do
# I have no clue what other modes are available
mock_app do
- use Rack::Protection::XSSHeader, :xss_mode => :foo
+ use Rack::Protection::XSSHeader, xss_mode: :foo
run DummyApp
end
- expect(get('/', {}, 'wants' => 'application/xhtml').headers["X-XSS-Protection"]).to eq("1; mode=foo")
+ expect(get('/', {}, 'wants' => 'application/xhtml').headers['X-XSS-Protection']).to eq('1; mode=foo')
end
it 'should not override the header if already set' do
- mock_app with_headers("X-XSS-Protection" => "0")
- expect(get('/', {}, 'wants' => 'text/html').headers["X-XSS-Protection"]).to eq("0")
+ mock_app with_headers('X-XSS-Protection' => '0')
+ expect(get('/', {}, 'wants' => 'text/html').headers['X-XSS-Protection']).to eq('0')
end
it 'should set the X-Content-Type-Options' do
- expect(get('/', {}, 'wants' => 'text/html').header["X-Content-Type-Options"]).to eq("nosniff")
+ expect(get('/', {}, 'wants' => 'text/html').header['X-Content-Type-Options']).to eq('nosniff')
end
-
it 'should set the X-Content-Type-Options for other content types' do
- expect(get('/', {}, 'wants' => 'application/foo').header["X-Content-Type-Options"]).to eq("nosniff")
+ expect(get('/', {}, 'wants' => 'application/foo').header['X-Content-Type-Options']).to eq('nosniff')
end
-
it 'should allow changing the nosniff-mode off' do
mock_app do
- use Rack::Protection::XSSHeader, :nosniff => false
+ use Rack::Protection::XSSHeader, nosniff: false
run DummyApp
end
- expect(get('/').headers["X-Content-Type-Options"]).to be_nil
+ expect(get('/').headers['X-Content-Type-Options']).to be_nil
end
it 'should not override the header if already set X-Content-Type-Options' do
- mock_app with_headers("X-Content-Type-Options" => "sniff")
- expect(get('/', {}, 'wants' => 'text/html').headers["X-Content-Type-Options"]).to eq("sniff")
+ mock_app with_headers('X-Content-Type-Options' => 'sniff')
+ expect(get('/', {}, 'wants' => 'text/html').headers['X-Content-Type-Options']).to eq('sniff')
end
end
diff --git a/rack-protection/spec/spec_helper.rb b/rack-protection/spec/spec_helper.rb
index 745c2f9f87..f35ec34855 100644
--- a/rack-protection/spec/spec_helper.rb
+++ b/rack-protection/spec/spec_helper.rb
@@ -1,8 +1,10 @@
+# frozen_string_literal: true
+
require 'rack/protection'
require 'rack/test'
require 'rack'
-Dir[File.expand_path('support/**/*.rb', __dir__)].each { |f| require f }
+Dir[File.expand_path('support/**/*.rb', __dir__)].sort.each { |f| require f }
# This file was generated by the `rspec --init` command. Conventionally, all
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
diff --git a/rack-protection/spec/support/dummy_app.rb b/rack-protection/spec/support/dummy_app.rb
index 980e183b54..fb83d36bb3 100644
--- a/rack-protection/spec/support/dummy_app.rb
+++ b/rack-protection/spec/support/dummy_app.rb
@@ -1,7 +1,9 @@
+# frozen_string_literal: true
+
module DummyApp
def self.call(env)
Thread.current[:last_env] = env
body = (env['REQUEST_METHOD'] == 'HEAD' ? '' : 'ok')
- [200, {'Content-Type' => env['wants'] || 'text/plain'}, [body]]
+ [200, { 'Content-Type' => env['wants'] || 'text/plain' }, [body]]
end
-end
\ No newline at end of file
+end
diff --git a/rack-protection/spec/support/not_implemented_as_pending.rb b/rack-protection/spec/support/not_implemented_as_pending.rb
index 2598ca41ea..2ef371b377 100644
--- a/rack-protection/spec/support/not_implemented_as_pending.rb
+++ b/rack-protection/spec/support/not_implemented_as_pending.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# see http://blog.101ideas.cz/posts/pending-examples-via-not-implemented-error-in-rspec.html
module NotImplementedAsPending
def self.included(base)
@@ -20,4 +22,4 @@ def finish(reporter)
end
RSpec::Core::Example.send :include, self
-end
\ No newline at end of file
+end
diff --git a/rack-protection/spec/support/rack_monkey_patches.rb b/rack-protection/spec/support/rack_monkey_patches.rb
index aa4a4565c9..574d367e08 100644
--- a/rack-protection/spec/support/rack_monkey_patches.rb
+++ b/rack-protection/spec/support/rack_monkey_patches.rb
@@ -1,12 +1,15 @@
-if defined? Gem.loaded_specs and Gem.loaded_specs.include? 'rack'
- version = Gem.loaded_specs['rack'].version.to_s
-else
- version = Rack.release + '.0'
-end
+# frozen_string_literal: true
+
+version = if defined? Gem.loaded_specs&.include?('rack')
+ Gem.loaded_specs['rack'].version.to_s
+ else
+ "#{Rack.release}.0"
+ end
-if version == "1.3"
+if version == '1.3'
Rack::Session::Abstract::ID.class_eval do
private
+
def prepare_session(env)
session_was = env[ENV_SESSION_KEY]
env[ENV_SESSION_KEY] = SessionHash.new(self, env)
diff --git a/rack-protection/spec/support/shared_examples.rb b/rack-protection/spec/support/shared_examples.rb
index 6e56cdb8ba..c8eeaad69b 100644
--- a/rack-protection/spec/support/shared_examples.rb
+++ b/rack-protection/spec/support/shared_examples.rb
@@ -1,10 +1,12 @@
+# frozen_string_literal: true
+
RSpec.shared_examples_for 'any rack application' do
- it "should not interfere with normal get requests" do
+ it 'should not interfere with normal get requests' do
expect(get('/')).to be_ok
expect(body).to eq('ok')
end
- it "should not interfere with normal head requests" do
+ it 'should not interfere with normal head requests' do
expect(head('/')).to be_ok
end
@@ -14,9 +16,10 @@
def call(env)
was = env.dup
res = app.call(env)
- was.each do |k,v|
+ was.each do |k, v|
next if env[k] == v
- fail "env[#{k.inspect}] changed from #{v.inspect} to #{env[k].inspect}"
+
+ raise "env[#{k.inspect}] changed from #{v.inspect} to #{env[k].inspect}"
end
res
end
@@ -24,13 +27,13 @@ def call(env)
mock_app do
use Rack::Head
- use(Rack::Config) { |e| e['rack.session'] ||= {}}
+ use(Rack::Config) { |e| e['rack.session'] ||= {} }
use detector
use klass
run DummyApp
end
- expect(get('/..', :foo => '')).to be_ok
+ expect(get('/..', foo: '')).to be_ok
end
it 'allows passing on values in env' do
@@ -53,7 +56,7 @@ def call(env)
mock_app do
use Rack::Head
- use(Rack::Config) { |e| e['rack.session'] ||= {}}
+ use(Rack::Config) { |e| e['rack.session'] ||= {} }
use changer
use klass
use detector
diff --git a/rack-protection/spec/support/spec_helpers.rb b/rack-protection/spec/support/spec_helpers.rb
index e862f172b9..d9f40a2777 100644
--- a/rack-protection/spec/support/spec_helpers.rb
+++ b/rack-protection/spec/support/spec_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'forwardable'
module SpecHelpers
@@ -12,12 +14,12 @@ def app
end
def mock_app(app = nil, &block)
- app = block if app.nil? and block.arity == 1
+ app = block if app.nil? && (block.arity == 1)
if app
klass = described_class
mock_app do
use Rack::Head
- use(Rack::Config) { |e| e['rack.session'] ||= {}}
+ use(Rack::Config) { |e| e['rack.session'] ||= {} }
use klass
run app
end
@@ -27,10 +29,10 @@ def mock_app(app = nil, &block)
end
def with_headers(headers)
- proc { [200, {'Content-Type' => 'text/plain'}.merge(headers), ['ok']] }
+ proc { [200, { 'Content-Type' => 'text/plain' }.merge(headers), ['ok']] }
end
def env
Thread.current[:last_env]
end
-end
\ No newline at end of file
+end
diff --git a/sinatra-contrib/Gemfile b/sinatra-contrib/Gemfile
index 0a42b5a234..af41c9e5c9 100644
--- a/sinatra-contrib/Gemfile
+++ b/sinatra-contrib/Gemfile
@@ -1,8 +1,10 @@
-source "https://rubygems.org"
+# frozen_string_literal: true
+
+source 'https://rubygems.org'
gemspec
-gem 'sinatra', path: '..'
gem 'rack-protection', path: '../rack-protection'
+gem 'sinatra', path: '..'
gem 'rack-test', github: 'rack/rack-test'
@@ -36,6 +38,6 @@ repos = { 'tilt' => 'rtomayko/tilt', 'rack' => 'rack/rack' }
%w[tilt rack].each do |lib|
dep = (ENV[lib] || 'stable').sub "#{lib}-", ''
dep = nil if dep == 'stable'
- dep = {:github => repos[lib], :branch => dep} if dep and dep !~ /(\d+\.)+\d+/
+ dep = { github: repos[lib], branch: dep } if dep && dep !~ (/(\d+\.)+\d+/)
gem lib, dep if dep
end
diff --git a/sinatra-contrib/Rakefile b/sinatra-contrib/Rakefile
index 94d2afdc71..c78189421b 100644
--- a/sinatra-contrib/Rakefile
+++ b/sinatra-contrib/Rakefile
@@ -1,12 +1,14 @@
+# frozen_string_literal: true
+
$LOAD_PATH.unshift File.expand_path('lib', __dir__)
require 'open-uri'
require 'yaml'
require 'sinatra/contrib/version'
-desc "run specs"
+desc 'run specs'
task(:spec) { ruby '-S rspec' }
-task(:test => :spec)
-task(:default => :spec)
+task(test: :spec)
+task(default: :spec)
namespace :doc do
task :readmes do
@@ -14,36 +16,37 @@ namespace :doc do
puts "Trying file... #{file}"
excluded_files = %w[lib/sinatra/contrib.rb lib/sinatra/decompile.rb]
next if excluded_files.include?(file)
+
doc = File.read(file)[/^module Sinatra(\n)+( #[^\n]*\n)*/m].scan(/^ *#(?!#) ?(.*)\n/).join("\n")
- file = "doc/#{file[4..-4].tr("/_", "-")}.rdoc"
- Dir.mkdir "doc" unless File.directory? "doc"
+ file = "doc/#{file[4..-4].tr('/_', '-')}.rdoc"
+ Dir.mkdir 'doc' unless File.directory? 'doc'
puts "writing #{file}"
- File.open(file, "w") { |f| f << doc }
+ File.open(file, 'w') { |f| f << doc }
end
end
task :index do
- doc = File.read("README.md")
- file = "doc/sinatra-contrib-readme.md"
- Dir.mkdir "doc" unless File.directory? "doc"
+ doc = File.read('README.md')
+ file = 'doc/sinatra-contrib-readme.md'
+ Dir.mkdir 'doc' unless File.directory? 'doc'
puts "writing #{file}"
- File.open(file, "w") { |f| f << doc }
+ File.open(file, 'w') { |f| f << doc }
end
- task :all => [:readmes, :index]
+ task all: %i[readmes index]
end
-desc "generate documentation"
-task :doc => 'doc:all'
+desc 'generate documentation'
+task doc: 'doc:all'
-desc "generate gemspec"
+desc 'generate gemspec'
task 'sinatra-contrib.gemspec' do
content = File.read 'sinatra-contrib.gemspec'
fields = {
- :authors => `git shortlog -sn`.scan(/[^\d\s].*/),
- :email => `git shortlog -sne`.scan(/[^<]+@[^>]+/),
- :files => `git ls-files`.split("\n").reject { |f| f =~ /^(\.|Gemfile)/ }
+ authors: `git shortlog -sn`.scan(/[^\d\s].*/),
+ email: `git shortlog -sne`.scan(/[^<]+@[^>]+/),
+ files: `git ls-files`.split("\n").grep_v(/^(\.|Gemfile)/)
}
fields.each do |field, values|
@@ -56,9 +59,9 @@ task 'sinatra-contrib.gemspec' do
File.open('sinatra-contrib.gemspec', 'w') { |f| f << content }
end
-task :gemspec => 'sinatra-contrib.gemspec'
+task gemspec: 'sinatra-contrib.gemspec'
-task :release => :gemspec do
+task release: :gemspec do
sh <<-SH
rm -Rf sinatra-contrib*.gem &&
gem build sinatra-contrib.gemspec &&
@@ -70,4 +73,3 @@ task :release => :gemspec do
git push --tags && (git push origin --tags || true)
SH
end
-
diff --git a/sinatra-contrib/lib/sinatra/capture.rb b/sinatra-contrib/lib/sinatra/capture.rb
index 3a74688a61..f6d18e2d28 100644
--- a/sinatra-contrib/lib/sinatra/capture.rb
+++ b/sinatra-contrib/lib/sinatra/capture.rb
@@ -86,17 +86,19 @@ module Capture
def capture(*args, &block)
return block[*args] if ruby?
+
if haml? && Tilt[:haml] == Tilt::HamlTemplate
buffer = Haml::Buffer.new(nil, Haml::Options.new.for_buffer)
with_haml_buffer(buffer) { capture_haml(*args, &block) }
else
- @_out_buf, _buf_was = '', @_out_buf
+ buf_was = @_out_buf
+ @_out_buf = ''
begin
raw = block[*args]
captured = block.binding.eval('@_out_buf')
captured.empty? ? raw : captured
ensure
- @_out_buf = _buf_was
+ @_out_buf = buf_was
end
end
end
diff --git a/sinatra-contrib/lib/sinatra/config_file.rb b/sinatra-contrib/lib/sinatra/config_file.rb
index cbaa06deee..c3c0a1aa47 100644
--- a/sinatra-contrib/lib/sinatra/config_file.rb
+++ b/sinatra-contrib/lib/sinatra/config_file.rb
@@ -3,7 +3,6 @@
require 'erb'
module Sinatra
-
# = Sinatra::ConfigFile
#
# Sinatra::ConfigFile is an extension that allows you to load the
@@ -107,7 +106,6 @@ module Sinatra
# bar: 'baz' # override the default value
#
module ConfigFile
-
# When the extension is registered sets the +environments+ setting to the
# traditional environments: development, test and production.
def self.registered(base)
@@ -122,8 +120,9 @@ def config_file(*paths)
paths.each do |pattern|
Dir.glob(pattern) do |file|
raise UnsupportedConfigType unless ['.yml', '.yaml', '.erb'].include?(File.extname(file))
+
logger.info "loading config file '#{file}'" if logging? && respond_to?(:logger)
- document = ERB.new(IO.read(file)).result
+ document = ERB.new(File.read(file)).result
yaml = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(document) : YAML.load(document)
config = config_for_env(yaml)
config.each_pair { |key, value| set(key, value) }
@@ -132,7 +131,7 @@ def config_file(*paths)
end
end
- class UnsupportedConfigType < Exception
+ class UnsupportedConfigType < StandardError
def message
'Invalid config file type, use .yml, .yaml or .erb'
end
diff --git a/sinatra-contrib/lib/sinatra/content_for.rb b/sinatra-contrib/lib/sinatra/content_for.rb
index 6102f2c8fd..75d1ee562a 100644
--- a/sinatra-contrib/lib/sinatra/content_for.rb
+++ b/sinatra-contrib/lib/sinatra/content_for.rb
@@ -1,8 +1,9 @@
+# frozen_string_literal: true
+
require 'sinatra/base'
require 'sinatra/capture'
module Sinatra
-
# = Sinatra::ContentFor
#
# Sinatra::ContentFor is a set of helpers that allows you to capture
@@ -174,7 +175,7 @@ def clear_content_for(key)
# for :head.
def yield_content(key, *args, &block)
if block_given? && !content_for?(key)
- (haml? && Tilt[:haml] == Tilt::HamlTemplate) ? capture_haml(*args, &block) : yield(*args)
+ haml? && Tilt[:haml] == Tilt::HamlTemplate ? capture_haml(*args, &block) : yield(*args)
else
content = content_blocks[key.to_sym].map { |b| capture(*args, &b) }
content.join.tap do |c|
diff --git a/sinatra-contrib/lib/sinatra/contrib.rb b/sinatra-contrib/lib/sinatra/contrib.rb
index e27888b368..9bee090215 100644
--- a/sinatra-contrib/lib/sinatra/contrib.rb
+++ b/sinatra-contrib/lib/sinatra/contrib.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'sinatra/contrib/setup'
module Sinatra
diff --git a/sinatra-contrib/lib/sinatra/contrib/all.rb b/sinatra-contrib/lib/sinatra/contrib/all.rb
index 427be2686d..c217a112a0 100644
--- a/sinatra-contrib/lib/sinatra/contrib/all.rb
+++ b/sinatra-contrib/lib/sinatra/contrib/all.rb
@@ -1,2 +1,4 @@
+# frozen_string_literal: true
+
require 'sinatra/contrib'
Sinatra.register Sinatra::Contrib::All
diff --git a/sinatra-contrib/lib/sinatra/contrib/setup.rb b/sinatra-contrib/lib/sinatra/contrib/setup.rb
index 61e4f87279..b2ae15e944 100644
--- a/sinatra-contrib/lib/sinatra/contrib/setup.rb
+++ b/sinatra-contrib/lib/sinatra/contrib/setup.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'sinatra/base'
require 'sinatra/contrib/version'
@@ -5,7 +7,7 @@ module Sinatra
module Contrib
module Loader
def extensions
- @extensions ||= {:helpers => [], :register => []}
+ @extensions ||= { helpers: [], register: [] }
end
def register(name, path)
diff --git a/sinatra-contrib/lib/sinatra/contrib/version.rb b/sinatra-contrib/lib/sinatra/contrib/version.rb
index 2c9d689f93..d81b671fdb 100644
--- a/sinatra-contrib/lib/sinatra/contrib/version.rb
+++ b/sinatra-contrib/lib/sinatra/contrib/version.rb
@@ -1,6 +1,7 @@
+# frozen_string_literal: true
+
module Sinatra
module Contrib
VERSION = '3.0.0'
end
end
-
diff --git a/sinatra-contrib/lib/sinatra/cookies.rb b/sinatra-contrib/lib/sinatra/cookies.rb
index 14aee33a71..742f45d7d0 100644
--- a/sinatra-contrib/lib/sinatra/cookies.rb
+++ b/sinatra-contrib/lib/sinatra/cookies.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'sinatra/base'
module Sinatra
@@ -65,15 +67,15 @@ def initialize(app)
@deleted = []
@options = {
- :path => @request.script_name.to_s.empty? ? '/' : @request.script_name,
- :domain => @request.host == 'localhost' ? nil : @request.host,
- :secure => @request.secure?,
- :httponly => true
+ path: @request.script_name.to_s.empty? ? '/' : @request.script_name,
+ domain: @request.host == 'localhost' ? nil : @request.host,
+ secure: @request.secure?,
+ httponly: true
}
- if app.settings.respond_to? :cookie_options
- @options.merge! app.settings.cookie_options
- end
+ return unless app.settings.respond_to? :cookie_options
+
+ @options.merge! app.settings.cookie_options
end
def ==(other)
@@ -88,9 +90,11 @@ def []=(key, value)
set(key, value: value)
end
- def assoc(key)
- to_hash.assoc(key.to_s)
- end if Hash.method_defined? :assoc
+ if Hash.method_defined? :assoc
+ def assoc(key)
+ to_hash.assoc(key.to_s)
+ end
+ end
def clear
each_key { |k| delete(k) }
@@ -114,17 +118,20 @@ def delete(key)
def delete_if
return enum_for(__method__) unless block_given?
+
each { |k, v| delete(k) if yield(k, v) }
self
end
def each(&block)
return enum_for(__method__) unless block_given?
+
to_hash.each(&block)
end
def each_key(&block)
return enum_for(__method__) unless block_given?
+
to_hash.each_key(&block)
end
@@ -132,6 +139,7 @@ def each_key(&block)
def each_value(&block)
return enum_for(__method__) unless block_given?
+
to_hash.each_value(&block)
end
@@ -145,16 +153,18 @@ def fetch(key, &block)
end
end
- def flatten
- to_hash.flatten
- end if Hash.method_defined? :flatten
+ if Hash.method_defined? :flatten
+ def flatten
+ to_hash.flatten
+ end
+ end
def has_key?(key)
- response_cookies.has_key? key.to_s or request_cookies.has_key? key.to_s
+ response_cookies.key? key.to_s or request_cookies.key? key.to_s
end
def has_value?(value)
- response_cookies.has_value? value or request_cookies.has_value? value
+ response_cookies.value? value or request_cookies.value? value
end
def hash
@@ -168,13 +178,16 @@ def inspect
"<##{self.class}: #{to_hash.inspect[1..-2]}>"
end
- def invert
- to_hash.invert
- end if Hash.method_defined? :invert
+ if Hash.method_defined? :invert
+ def invert
+ to_hash.invert
+ end
+ end
def keep_if
return enum_for(__method__) unless block_given?
- delete_if { |*a| not yield(*a) }
+
+ delete_if { |*a| !yield(*a) }
end
def key(value)
@@ -197,11 +210,11 @@ def merge(other, &block)
def merge!(other)
other.each_pair do |key, value|
- if block_given? and include? key
- self[key] = yield(key.to_s, self[key], value)
- else
- self[key] = value
- end
+ self[key] = if block_given? && include?(key)
+ yield(key.to_s, self[key], value)
+ else
+ value
+ end
end
end
@@ -217,18 +230,20 @@ def rehash
def reject(&block)
return enum_for(__method__) unless block_given?
+
to_hash.reject(&block)
end
alias reject! delete_if
def replace(other)
- select! { |k, v| other.include?(k) or other.include?(k.to_s) }
+ select! { |k, _v| other.include?(k) or other.include?(k.to_s) }
merge! other
end
def select(&block)
return enum_for(__method__) unless block_given?
+
to_hash.select(&block)
end
@@ -246,9 +261,11 @@ def shift
alias size length
- def sort(&block)
- to_hash.sort(&block)
- end if Hash.method_defined? :sort
+ if Hash.method_defined? :sort
+ def sort(&block)
+ to_hash.sort(&block)
+ end
+ end
alias store []=
@@ -300,6 +317,7 @@ def parse_response
string.each_line do |line|
key, value = line.split(';', 2).first.to_s.split('=', 2)
next if key.nil?
+
key = Rack::Utils.unescape(key)
if line =~ /expires=Thu, 01[-\s]Jan[-\s]1970/
@deleted << key
@@ -314,7 +332,7 @@ def parse_response
end
def request_cookies
- @request.cookies.reject { |key, value| deleted.include? key }
+ @request.cookies.reject { |key, _value| deleted.include? key }
end
end
diff --git a/sinatra-contrib/lib/sinatra/custom_logger.rb b/sinatra-contrib/lib/sinatra/custom_logger.rb
index 42c0d639cd..eb3295601a 100644
--- a/sinatra-contrib/lib/sinatra/custom_logger.rb
+++ b/sinatra-contrib/lib/sinatra/custom_logger.rb
@@ -1,5 +1,6 @@
-module Sinatra
+# frozen_string_literal: true
+module Sinatra
# = Sinatra::CustomLogger
#
# CustomLogger extension allows you to define your own logger instance
diff --git a/sinatra-contrib/lib/sinatra/engine_tracking.rb b/sinatra-contrib/lib/sinatra/engine_tracking.rb
index 6709d880ad..52ebf858fb 100644
--- a/sinatra-contrib/lib/sinatra/engine_tracking.rb
+++ b/sinatra-contrib/lib/sinatra/engine_tracking.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'sinatra/base'
module Sinatra
@@ -17,7 +19,7 @@ def erb?
# @return [Boolean] Returns true if current engine is `:erubi`.
def erubi?
@current_engine == :erubi or
- erb? && Tilt[:erb] == Tilt::ErubiTemplate
+ (erb? && Tilt[:erb] == Tilt::ErubiTemplate)
end
# @return [Boolean] Returns true if current engine is `:haml`.
@@ -72,7 +74,8 @@ def initialize(*)
# @param engine [Symbol, String] Name of Engine to shift to.
def with_engine(engine)
- @current_engine, engine_was = engine.to_sym, @current_engine
+ engine_was = @current_engine
+ @current_engine = engine.to_sym
yield
ensure
@current_engine = engine_was
diff --git a/sinatra-contrib/lib/sinatra/extension.rb b/sinatra-contrib/lib/sinatra/extension.rb
index 2a5cda78d4..7e6b609180 100644
--- a/sinatra-contrib/lib/sinatra/extension.rb
+++ b/sinatra-contrib/lib/sinatra/extension.rb
@@ -1,7 +1,8 @@
+# frozen_string_literal: true
+
require 'sinatra/base'
module Sinatra
-
# = Sinatra::Extension
#
# Sinatra::Extension is a mixin that provides some syntactic sugar
@@ -81,13 +82,14 @@ def recorded_methods
def method_missing(method, *args, &block)
return super unless Sinatra::Base.respond_to? method
+
record(method, *args, &block)
DontCall.new(method)
end
class DontCall < BasicObject
def initialize(method) @method = method end
- def method_missing(*) fail "not supposed to use result of #@method!" end
+ def method_missing(*) raise "not supposed to use result of #{@method}!" end
def inspect; "#<#{self.class}: #{@method}>" end
end
end
diff --git a/sinatra-contrib/lib/sinatra/json.rb b/sinatra-contrib/lib/sinatra/json.rb
index 712419bbd1..31c4199ad9 100644
--- a/sinatra-contrib/lib/sinatra/json.rb
+++ b/sinatra-contrib/lib/sinatra/json.rb
@@ -1,7 +1,8 @@
+# frozen_string_literal: true
+
require 'sinatra/base'
require 'multi_json'
module Sinatra
-
# = Sinatra::JSON
#
# Sinatra::JSON adds a helper method, called +json+, for (obviously)
@@ -95,7 +96,7 @@ def encode(object)
def json(object, options = {})
content_type resolve_content_type(options)
- resolve_encoder_action object, resolve_encoder(options)
+ resolve_encoder_action object, resolve_encoder(options)
end
private
@@ -109,16 +110,14 @@ def resolve_encoder(options = {})
end
def resolve_encoder_action(object, encoder)
- [:encode, :generate].each do |method|
+ %i[encode generate].each do |method|
return encoder.send(method, object) if encoder.respond_to? method
end
- if encoder.is_a? Symbol
- object.__send__(encoder)
- else
- fail "#{encoder} does not respond to #generate nor #encode"
- end #if
- end #resolve_encoder_action
- end #JSON
+ raise "#{encoder} does not respond to #generate nor #encode" unless encoder.is_a? Symbol
+
+ object.__send__(encoder)
+ end
+ end
Base.set :json_encoder do
::MultiJson
diff --git a/sinatra-contrib/lib/sinatra/link_header.rb b/sinatra-contrib/lib/sinatra/link_header.rb
index 6d9bac4d3c..fcf1dcb41e 100644
--- a/sinatra-contrib/lib/sinatra/link_header.rb
+++ b/sinatra-contrib/lib/sinatra/link_header.rb
@@ -1,7 +1,6 @@
require 'sinatra/base'
module Sinatra
-
# = Sinatra::LinkHeader
#
# Sinatra::LinkHeader adds a set of helper methods to generate link
@@ -86,8 +85,8 @@ def link(*urls)
opts[:rel] = urls.shift unless urls.first.respond_to? :to_str
options = opts.map { |k, v| " #{k}=#{v.to_s.inspect}" }
html_pattern = ""
- http_pattern = ["<%s>", *options].join ";"
- link = (response["Link"] ||= "")
+ http_pattern = ['<%s>', *options].join ';'
+ link = (response['Link'] ||= '')
urls.map do |url|
link << ",\n" unless link.empty?
@@ -116,14 +115,15 @@ def link(*urls)
# %body= yield
def link_headers
yield if block_given?
- return "" unless response.include? "Link"
- response["Link"].split(",\n").map do |line|
+ return '' unless response.include? 'Link'
+
+ response['Link'].split(",\n").map do |line|
url, *opts = line.split(';').map(&:strip)
- ""
+ ""
end.join "\n"
end
- def self.registered(base)
+ def self.registered(_base)
puts "WARNING: #{self} is a helpers module, not an extension."
end
end
diff --git a/sinatra-contrib/lib/sinatra/multi_route.rb b/sinatra-contrib/lib/sinatra/multi_route.rb
index af48ac008b..6de5acb2f0 100644
--- a/sinatra-contrib/lib/sinatra/multi_route.rb
+++ b/sinatra-contrib/lib/sinatra/multi_route.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'sinatra/base'
module Sinatra
diff --git a/sinatra-contrib/lib/sinatra/namespace.rb b/sinatra-contrib/lib/sinatra/namespace.rb
index 157a965b13..6a00d3753f 100644
--- a/sinatra-contrib/lib/sinatra/namespace.rb
+++ b/sinatra-contrib/lib/sinatra/namespace.rb
@@ -1,8 +1,9 @@
+# frozen_string_literal: true
+
require 'sinatra/base'
require 'mustermann'
module Sinatra
-
# = Sinatra::Namespace
#
# Sinatra::Namespace is an extension that adds namespaces to an
@@ -187,13 +188,16 @@ module Sinatra
module Namespace
def self.new(base, pattern, conditions = {}, &block)
Module.new do
- #quelch uninitialized variable warnings, since these get used by compile method.
- @pattern, @conditions = nil, nil
+ # quelch uninitialized variable warnings, since these get used by compile method.
+ @pattern = nil
+ @conditions = nil
extend NamespacedMethods
include InstanceMethods
- @base, @extensions, @errors = base, [], {}
+ @base = base
+ @extensions = []
+ @errors = {}
@pattern, @conditions = compile(pattern, conditions)
- @templates = Hash.new { |h,k| @base.templates[k] }
+ @templates = Hash.new { |_h, k| @base.templates[k] }
namespace = self
before { extend(@namespace = namespace) }
class_eval(&block)
@@ -224,14 +228,14 @@ module NamespacedMethods
include SharedMethods
attr_reader :base, :templates
- ALLOWED_ENGINES = [
- :erb, :erubi, :haml, :hamlit, :builder, :nokogiri,
- :liquid, :markdown, :rdoc, :asciidoc, :markaby,
- :rabl, :slim, :yajl
+ ALLOWED_ENGINES = %i[
+ erb erubi haml hamlit builder nokogiri
+ liquid markdown rdoc asciidoc markaby
+ rabl slim yajl
]
def self.prefixed(*names)
- names.each { |n| define_method(n) { |*a, &b| prefixed(n, *a, &b) }}
+ names.each { |n| define_method(n) { |*a, &b| prefixed(n, *a, &b) } }
end
prefixed :before, :after, :delete, :get, :head, :options, :patch, :post, :put
@@ -267,7 +271,7 @@ def namespace_errors
end
def error(*codes, &block)
- args = Sinatra::Base.send(:compile!, "ERROR", /.*/, block)
+ args = Sinatra::Base.send(:compile!, 'ERROR', /.*/, block)
codes = codes.map { |c| Array(c) }.flatten
codes << Exception if codes.empty?
codes << Sinatra::NotFound if codes.include?(404)
@@ -280,12 +284,14 @@ def error(*codes, &block)
def respond_to(*args)
return @conditions[:provides] || base.respond_to if args.empty?
+
@conditions[:provides] = args
end
def set(key, value = self, &block)
- return key.each { |k,v| set(k, v) } if key.respond_to?(:each) and block.nil? and value == self
+ return key.each { |k, v| set(k, v) } if key.respond_to?(:each) && block.nil? && (value == self)
raise ArgumentError, "may not set #{key}" unless ([:views] + ALLOWED_ENGINES).include?(key)
+
block ||= proc { value }
singleton_class.send(:define_method, key, &block)
end
@@ -300,11 +306,12 @@ def disable(*opts)
def template(name, &block)
first_location = caller_locations.first
- filename, line = first_location.path, first_location.lineno
+ filename = first_location.path
+ line = first_location.lineno
templates[name] = [block, filename, line]
end
- def layout(name=:layout, &block)
+ def layout(name = :layout, &block)
template name, &block
end
@@ -323,21 +330,22 @@ def compile(pattern, conditions, default_pattern = nil)
conditions = conditions.merge pattern.to_hash
pattern = nil
end
- base_pattern, base_conditions = @pattern, @conditions
+ base_pattern = @pattern
+ base_conditions = @conditions
pattern ||= default_pattern
- [ prefixed_path(base_pattern, pattern),
- (base_conditions || {}).merge(conditions) ]
+ [prefixed_path(base_pattern, pattern),
+ (base_conditions || {}).merge(conditions)]
end
def prefixed_path(a, b)
- return a || b || /.*/ unless a and b
+ return a || b || /.*/ unless a && b
return Mustermann.new(b) if a == /.*/
Mustermann.new(a) + Mustermann.new(b)
end
def prefixed(method, pattern = nil, conditions = {}, &block)
- default = %r{(?:/.*)?} if method == :before or method == :after
+ default = %r{(?:/.*)?} if (method == :before) || (method == :after)
pattern, conditions = compile pattern, conditions, default
result = base.send(method, pattern, **conditions, &block)
invoke_hook :route_added, method.to_s.upcase, pattern, block
diff --git a/sinatra-contrib/lib/sinatra/quiet_logger.rb b/sinatra-contrib/lib/sinatra/quiet_logger.rb
index 7c00b403f6..ee0ff8dafd 100644
--- a/sinatra-contrib/lib/sinatra/quiet_logger.rb
+++ b/sinatra-contrib/lib/sinatra/quiet_logger.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Sinatra
# = Sinatra::QuietLogger
#
@@ -32,10 +34,14 @@ module Sinatra
# end
#
module QuietLogger
-
def self.registered(app)
- quiet_logger_prefixes = app.settings.quiet_logger_prefixes.join('|') rescue ''
+ quiet_logger_prefixes = begin
+ app.settings.quiet_logger_prefixes.join('|')
+ rescue StandardError
+ ''
+ end
return warn('You need to specify the paths you wish to exclude from logging via `set :quiet_logger_prefixes, %w(images css fonts)`') if quiet_logger_prefixes.empty?
+
const_set('QUIET_LOGGER_REGEX', %r(\A/{0,2}(?:#{quiet_logger_prefixes})))
::Rack::CommonLogger.prepend(
::Module.new do
@@ -45,6 +51,5 @@ def log(env, *)
end
)
end
-
end
end
diff --git a/sinatra-contrib/lib/sinatra/reloader.rb b/sinatra-contrib/lib/sinatra/reloader.rb
index 7cb1612373..6c309770a7 100644
--- a/sinatra-contrib/lib/sinatra/reloader.rb
+++ b/sinatra-contrib/lib/sinatra/reloader.rb
@@ -1,7 +1,8 @@
+# frozen_string_literal: true
+
require 'sinatra/base'
module Sinatra
-
# = Sinatra::Reloader
#
# Extension to reload modified files. Useful during development,
@@ -94,11 +95,9 @@ module Sinatra
# end
#
module Reloader
-
# Watches a file so it can tell when it has been updated, and what
# elements does it contain.
class Watcher
-
# Represents an element of a Sinatra application that may need to
# be reloaded. An element could be:
# * a route
@@ -172,7 +171,8 @@ def updated
# Creates a new +Watcher+ instance for the file located at +path+.
def initialize(path)
@ignore = nil
- @path, @elements = path, []
+ @path = path
+ @elements = []
update
end
@@ -254,12 +254,13 @@ def self.perform(klass)
reloaded_paths << watcher.path
end
return if reloaded_paths.empty?
+
@@after_reload.each do |block|
- block.arity != 0 ? block.call(reloaded_paths) : block.call
+ block.arity.zero? ? block.call : block.call(reloaded_paths)
end
# Prevents after_reload from increasing each time it's reloaded.
@@after_reload.delete_if do |blk|
- path, _ = blk.source_location
+ path, = blk.source_location
path && reloaded_paths.include?(path)
end
end
@@ -286,7 +287,7 @@ def compile!(verb, path, block, **options)
block.source_location.first : caller_files[1]
signature = super
watch_element(
- source_location, :route, { :verb => verb, :signature => signature }
+ source_location, :route, { verb: verb, signature: signature }
)
signature
end
@@ -295,9 +296,8 @@ def compile!(verb, path, block, **options)
# tells the +Watcher::List+ for the Sinatra application to watch the
# inline templates in +file+ or the file who made the call to this
# method.
- def inline_templates=(file=nil)
- file = (file.nil? || file == true) ?
- (caller_files[1] || File.expand_path($0)) : file
+ def inline_templates=(file = nil)
+ file = (caller_files[1] || File.expand_path($0)) if file.nil? || file == true
watch_element(file, :inline_templates)
super
end
@@ -329,7 +329,7 @@ def error(*codes, &block)
path = caller_files[1] || File.expand_path($0)
result = super
codes.each do |c|
- watch_element(path, :error, :code => c, :handler => @errors[c])
+ watch_element(path, :error, code: c, handler: @errors[c])
end
result
end
@@ -358,17 +358,17 @@ module ExtensionMethods
# Removes the +element+ from the Sinatra application.
def deactivate(element)
case element.type
- when :route then
+ when :route
verb = element.representation[:verb]
signature = element.representation[:signature]
(routes[verb] ||= []).delete(signature)
- when :middleware then
+ when :middleware
@middleware.delete(element.representation)
- when :before_filter then
+ when :before_filter
filters[:before].delete(element.representation)
- when :after_filter then
+ when :after_filter
filters[:after].delete(element.representation)
- when :error then
+ when :error
code = element.representation[:code]
handler = element.representation[:handler]
@errors.delete(code) if @errors[code] == handler
@@ -387,7 +387,7 @@ def dont_reload(*glob)
Dir[*glob].each { |path| Watcher::List.for(self).ignore(path) }
end
- private
+ private
# attr_reader :register_path warn on -w (private attribute)
def register_path; @register_path ||= nil; end
@@ -415,7 +415,7 @@ def registering_extension?
# watch it in the file where the extension has been registered.
# This prevents the duplication of the elements added by the
# extension in its +registered+ method with every reload.
- def watch_element(path, type, representation=nil)
+ def watch_element(path, type, representation = nil)
list = Watcher::List.for(self)
element = Watcher::Element.new(type, representation)
list.watch(path, element)
diff --git a/sinatra-contrib/lib/sinatra/required_params.rb b/sinatra-contrib/lib/sinatra/required_params.rb
index c14525e447..dc286f2862 100644
--- a/sinatra-contrib/lib/sinatra/required_params.rb
+++ b/sinatra-contrib/lib/sinatra/required_params.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'sinatra/base'
module Sinatra
@@ -60,7 +62,7 @@ def _required_params(p, *keys)
elsif key.is_a?(Array)
_required_params(p, *key)
else
- halt 400 unless p && p.respond_to?(:has_key?) && p.has_key?(key.to_s)
+ halt 400 unless p.respond_to?(:key?) && p&.key?(key.to_s)
end
end
true
diff --git a/sinatra-contrib/lib/sinatra/respond_with.rb b/sinatra-contrib/lib/sinatra/respond_with.rb
index 91c3391cb2..498decfa57 100644
--- a/sinatra-contrib/lib/sinatra/respond_with.rb
+++ b/sinatra-contrib/lib/sinatra/respond_with.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'sinatra/json'
require 'sinatra/base'
@@ -88,14 +90,17 @@ module Sinatra
module RespondWith
class Format
def initialize(app)
- @app, @map, @generic, @default = app, {}, {}, nil
+ @app = app
+ @map = {}
+ @generic = {}
+ @default = nil
end
def on(type, &block)
@app.settings.mime_types(type).each do |mime|
case mime
when '*/*' then @default = block
- when /^([^\/]+)\/\*$/ then @generic[$1] = block
+ when %r{^([^/]+)/\*$} then @generic[$1] = block
else @map[mime] = block
end
end
@@ -103,23 +108,24 @@ def on(type, &block)
def finish
yield self if block_given?
- mime_type = @app.content_type ||
- @app.request.preferred_type(@map.keys) ||
- @app.request.preferred_type ||
- 'text/html'
+ mime_type = @app.content_type ||
+ @app.request.preferred_type(@map.keys) ||
+ @app.request.preferred_type ||
+ 'text/html'
type = mime_type.split(/\s*;\s*/, 2).first
- handlers = [@map[type], @generic[type[/^[^\/]+/]], @default].compact
+ handlers = [@map[type], @generic[type[%r{^[^/]+}]], @default].compact
handlers.each do |block|
- if result = block.call(type)
+ if (result = block.call(type))
@app.content_type mime_type
@app.halt result
end
end
- @app.halt 500, "Unknown template engine"
+ @app.halt 500, 'Unknown template engine'
end
def method_missing(method, *args, &block)
- return super if args.any? or block.nil? or not @app.mime_type(method)
+ return super if args.any? || block.nil? || !@app.mime_type(method)
+
on(method, &block)
end
end
@@ -128,19 +134,22 @@ module Helpers
include Sinatra::JSON
def respond_with(template, object = nil, &block)
- object, template = template, nil unless Symbol === template
+ unless Symbol === template
+ object = template
+ template = nil
+ end
format = Format.new(self)
- format.on "*/*" do |type|
+ format.on '*/*' do |type|
exts = settings.ext_map[type]
exts << :xml if type.end_with? '+xml'
if template
args = template_cache.fetch(type, template) { template_for(template, exts) }
if args.any?
- locals = { :object => object }
+ locals = { object: object }
locals.merge! object.to_hash if object.respond_to? :to_hash
renderer = args.first
- options = args[1..-1] + [{:locals => locals}]
+ options = args[1..] + [{ locals: locals }]
halt send(renderer, *options)
end
@@ -149,6 +158,7 @@ def respond_with(template, object = nil, &block)
exts.each do |ext|
halt json(object) if ext == :json
next unless object.respond_to? method = "to_#{ext}"
+
halt(*object.send(method))
end
end
@@ -176,10 +186,11 @@ def template_for(name, exts)
possible.each do |engine, template|
klass = Tilt.default_mapping.template_map[engine.to_s] ||
- Tilt.lazy_map[engine.to_s].fetch(0, [])[0]
+ Tilt.lazy_map[engine.to_s].fetch(0, [])[0]
find_template(settings.views, template, klass) do |file|
next unless File.exist? file
+
return settings.rendering_method(engine) << template.to_sym
end
end
@@ -189,7 +200,7 @@ def template_for(name, exts)
def remap_extensions
ext_map.clear
- Rack::Mime::MIME_TYPES.each { |e,t| ext_map[t] << e[1..-1].to_sym }
+ Rack::Mime::MIME_TYPES.each { |e, t| ext_map[t] << e[1..].to_sym }
ext_map['text/javascript'] << 'js'
ext_map['text/xml'] << 'xml'
end
@@ -206,7 +217,7 @@ def respond_to(*formats)
if formats.any?
@respond_to ||= []
@respond_to.concat formats
- elsif @respond_to.nil? and superclass.respond_to? :respond_to
+ elsif @respond_to.nil? && superclass.respond_to?(:respond_to)
superclass.respond_to
else
@respond_to
@@ -216,7 +227,8 @@ def respond_to(*formats)
def rendering_method(engine)
return [engine] if Sinatra::Templates.method_defined? engine
return [:mab] if engine.to_sym == :markaby
- [:render, :engine]
+
+ %i[render engine]
end
private
@@ -228,8 +240,8 @@ def compile!(verb, path, block, **options)
def self.jrubyify(engs)
not_supported = [:markdown]
- engs.keys.each do |key|
- engs[key].collect! { |eng| (eng == :yajl) ? :json_pure : eng }
+ engs.each_key do |key|
+ engs[key].collect! { |eng| eng == :yajl ? :json_pure : eng }
engs[key].delete_if { |eng| not_supported.include?(eng) }
end
engs
@@ -237,19 +249,19 @@ def self.jrubyify(engs)
def self.engines
engines = {
- :xml => [:builder, :nokogiri],
- :html => [:erb, :erubi, :haml, :hamlit, :slim, :liquid,
- :mab, :markdown, :rdoc],
- :all => (Sinatra::Templates.instance_methods.map(&:to_sym) +
- [:mab] - [:find_template, :markaby]),
- :json => [:yajl],
+ xml: %i[builder nokogiri],
+ html: %i[erb erubi haml hamlit slim liquid
+ mab markdown rdoc],
+ all: (Sinatra::Templates.instance_methods.map(&:to_sym) +
+ [:mab] - %i[find_template markaby]),
+ json: [:yajl]
}
engines.default = []
- (defined? JRUBY_VERSION) ? jrubyify(engines) : engines
+ defined?(JRUBY_VERSION) ? jrubyify(engines) : engines
end
def self.registered(base)
- base.set :ext_map, Hash.new { |h,k| h[k] = [] }
+ base.set :ext_map, Hash.new { |h, k| h[k] = [] }
base.set :template_engines, engines
base.remap_extensions
base.helpers Helpers
diff --git a/sinatra-contrib/lib/sinatra/runner.rb b/sinatra-contrib/lib/sinatra/runner.rb
index 4e967aaefe..b2124618c9 100644
--- a/sinatra-contrib/lib/sinatra/runner.rb
+++ b/sinatra-contrib/lib/sinatra/runner.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'open-uri'
require 'net/http'
require 'timeout'
@@ -49,7 +51,7 @@ module Sinatra
# For an example, check https://github.com/apotonick/roar/blob/master/test/integration/runner.rb
class Runner
def app_file
- File.expand_path("server.rb", __dir__)
+ File.expand_path('server.rb', __dir__)
end
def run
@@ -60,7 +62,8 @@ def run
def kill
return unless pipe
- Process.kill("KILL", pipe.pid)
+
+ Process.kill('KILL', pipe.pid)
rescue NotImplementedError
system "kill -9 #{pipe.pid}"
rescue Errno::ESRCH
@@ -70,7 +73,7 @@ def get(url)
Timeout.timeout(1) { get_url("#{protocol}://127.0.0.1:#{port}#{url}") }
end
- def get_stream(url = "/stream", &block)
+ def get_stream(url = '/stream', &block)
Net::HTTP.start '127.0.0.1', port do |http|
request = Net::HTTP::Get.new url
http.request request do |response|
@@ -89,29 +92,32 @@ def get_response(url)
end
def log
- @log ||= ""
- loop { @log << pipe.read_nonblock(1) }
+ @log ||= ''
+ loop { @log << pipe.read_nonblock(1) }
rescue Exception
@log
end
- private
+ private
+
attr_accessor :pipe
def start
IO.popen(command)
end
- def command # to be overwritten
+ # to be overwritten
+ def command
"bundle exec ruby #{app_file} -p #{port} -e production"
end
- def ping(timeout=30)
+ def ping(timeout = 30)
loop do
return if alive?
+
if Time.now - @started > timeout
- $stderr.puts command, log
- fail "timeout"
+ warn command, log
+ raise 'timeout'
else
sleep 0.1
end
@@ -121,26 +127,29 @@ def ping(timeout=30)
def alive?
3.times { get(ping_path) }
true
- rescue Errno::ECONNREFUSED, Errno::ECONNRESET, EOFError, SystemCallError, OpenURI::HTTPError, Timeout::Error
+ rescue EOFError, SystemCallError, OpenURI::HTTPError, Timeout::Error
false
end
- def ping_path # to be overwritten
+ # to be overwritten
+ def ping_path
'/ping'
end
- def port # to be overwritten
+ # to be overwritten
+ def port
4567
end
def protocol
- "http"
+ 'http'
end
def get_url(url)
uri = URI.parse(url)
- return uri.read unless protocol == "https"
+ return uri.read unless protocol == 'https'
+
get_https_url(uri)
end
diff --git a/sinatra-contrib/lib/sinatra/streaming.rb b/sinatra-contrib/lib/sinatra/streaming.rb
index aa9e717f82..e54eb27d7a 100644
--- a/sinatra-contrib/lib/sinatra/streaming.rb
+++ b/sinatra-contrib/lib/sinatra/streaming.rb
@@ -1,7 +1,8 @@
+# frozen_string_literal: true
+
require 'sinatra/base'
module Sinatra
-
# = Sinatra::Streaming
#
# Sinatra 1.3 introduced the +stream+ helper. This addon improves the
@@ -84,13 +85,14 @@ def stream(*)
end
module Stream
-
attr_accessor :app, :lineno, :pos, :transformer, :closed
alias tell pos
alias closed? closed
def self.extended(obj)
- obj.closed, obj.lineno, obj.pos = false, 0, 0
+ obj.closed = false
+ obj.lineno = 0
+ obj.pos = 0
obj.callback { obj.closed = true }
obj.errback { obj.closed = true }
end
@@ -108,6 +110,7 @@ def <<(data)
def each
# that way body.each.map { ... } works
return self unless block_given?
+
super
end
@@ -120,7 +123,8 @@ def map!(&block)
@transformer ||= nil
if @transformer
- inner, outer = @transformer, block
+ inner = @transformer
+ outer = block
block = proc { |value| outer[inner[value]] }
end
@transformer = block
@@ -132,7 +136,7 @@ def write(data)
data.to_s.bytesize
end
- alias syswrite write
+ alias syswrite write
alias write_nonblock write
def print(*args)
@@ -154,7 +158,7 @@ def puts(*args)
end
def close_read
- raise IOError, "closing non-duplex IO for reading"
+ raise IOError, 'closing non-duplex IO for reading'
end
def closed_read?
@@ -171,10 +175,6 @@ def external_encoding
settings.default_encoding
end
- def closed?
- @closed
- end
-
def settings
app.settings
end
@@ -184,7 +184,7 @@ def rewind
end
def not_open_for_reading(*)
- raise IOError, "not opened for reading"
+ raise IOError, 'not opened for reading'
end
alias bytes not_open_for_reading
diff --git a/sinatra-contrib/lib/sinatra/test_helpers.rb b/sinatra-contrib/lib/sinatra/test_helpers.rb
index 9c053b0337..15599bf255 100644
--- a/sinatra-contrib/lib/sinatra/test_helpers.rb
+++ b/sinatra-contrib/lib/sinatra/test_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'sinatra/base'
require 'rack'
begin
@@ -158,7 +160,7 @@ def app
# @param params [Hash]
# @param env [Hash]
def options(uri, params = {}, env = {}, &block)
- env = env_for(uri, env.merge(:method => "OPTIONS", :params => params))
+ env = env_for(uri, env.merge(method: 'OPTIONS', params: params))
current_session.send(:process_request, uri, env, &block)
end
end
@@ -170,7 +172,7 @@ def options(uri, params = {}, env = {}, &block)
# @param params [Hash]
# @param env [Hash]
def patch(uri, params = {}, env = {}, &block)
- env = env_for(uri, env.merge(:method => "PATCH", :params => params))
+ env = env_for(uri, env.merge(method: 'PATCH', params: params))
current_session.send(:process_request, uri, env, &block)
end
end
@@ -187,7 +189,8 @@ def last_request?
# @return [Hash] Session of last request, or the empty Hash
def session
return {} unless last_request?
- raise Rack::Test::Error, "session not enabled for app" unless last_env["rack.session"] or app.session?
+ raise Rack::Test::Error, 'session not enabled for app' unless last_env['rack.session'] || app.session?
+
last_request.session
end
diff --git a/sinatra-contrib/lib/sinatra/webdav.rb b/sinatra-contrib/lib/sinatra/webdav.rb
index 6529c8ba14..70ef7f632c 100644
--- a/sinatra-contrib/lib/sinatra/webdav.rb
+++ b/sinatra-contrib/lib/sinatra/webdav.rb
@@ -1,7 +1,8 @@
+# frozen_string_literal: true
+
require 'sinatra/base'
module Sinatra
-
# = Sinatra::WebDAV
#
# This extensions provides WebDAV verbs, as defined by RFC 4918
@@ -37,8 +38,8 @@ def self.registered(_)
module Request
def self.included(base)
base.class_eval do
- alias _safe? safe?
- alias _idempotent? idempotent?
+ alias_method :_safe?, :safe?
+ alias_method :_idempotent?, :idempotent?
def safe?
_safe? or propfind?
@@ -70,9 +71,9 @@ def move?
request_method == 'MOVE'
end
- #def lock?
+ # def lock?
# request_method == 'LOCK'
- #end
+ # end
def unlock?
request_method == 'UNLOCK'
@@ -84,7 +85,7 @@ def proppatch(path, opts = {}, &bk) route 'PROPPATCH', path, opts, &bk end
def mkcol(path, opts = {}, &bk) route 'MKCOL', path, opts, &bk end
def copy(path, opts = {}, &bk) route 'COPY', path, opts, &bk end
def move(path, opts = {}, &bk) route 'MOVE', path, opts, &bk end
- #def lock(path, opts = {}, &bk) route 'LOCK', path, opts, &bk end
+ # def lock(path, opts = {}, &bk) route 'LOCK', path, opts, &bk end
def unlock(path, opts = {}, &bk) route 'UNLOCK', path, opts, &bk end
end
diff --git a/sinatra-contrib/sinatra-contrib.gemspec b/sinatra-contrib/sinatra-contrib.gemspec
index e9043676ae..f5248b69b2 100644
--- a/sinatra-contrib/sinatra-contrib.gemspec
+++ b/sinatra-contrib/sinatra-contrib.gemspec
@@ -1,57 +1,58 @@
-# -*- encoding: utf-8 -*-
+# frozen_string_literal: true
-version = File.read(File.expand_path("../VERSION", __dir__)).strip
+version = File.read(File.expand_path('../VERSION', __dir__)).strip
Gem::Specification.new do |s|
- s.name = "sinatra-contrib"
+ s.name = 'sinatra-contrib'
s.version = version
- s.description = "Collection of useful Sinatra extensions"
- s.homepage = "http://sinatrarb.com/contrib/"
- s.license = "MIT"
+ s.description = 'Collection of useful Sinatra extensions'
+ s.homepage = 'http://sinatrarb.com/contrib/'
+ s.license = 'MIT'
s.summary = s.description
- s.authors = ["https://github.com/sinatra/sinatra/graphs/contributors"]
- s.email = "sinatrarb@googlegroups.com"
- s.files = Dir["lib/**/*.rb"] + [
- "LICENSE",
- "README.md",
- "Rakefile",
- "ideas.md",
- "sinatra-contrib.gemspec"
+ s.authors = ['https://github.com/sinatra/sinatra/graphs/contributors']
+ s.email = 'sinatrarb@googlegroups.com'
+ s.files = Dir['lib/**/*.rb'] + [
+ 'LICENSE',
+ 'README.md',
+ 'Rakefile',
+ 'ideas.md',
+ 'sinatra-contrib.gemspec'
]
- if s.respond_to?(:metadata)
- s.metadata = {
- 'source_code_uri' => 'https://github.com/sinatra/sinatra/tree/master/sinatra-contrib',
- 'homepage_uri' => 'http://sinatrarb.com/contrib/',
- 'documentation_uri' => 'https://www.rubydoc.info/gems/sinatra-contrib'
- }
- else
- raise <<-EOF
+ unless s.respond_to?(:metadata)
+ raise <<-WARN
RubyGems 2.0 or newer is required to protect against public gem pushes. You can update your rubygems version by running:
gem install rubygems-update
update_rubygems:
gem update --system
-EOF
+ WARN
end
+ s.metadata = {
+ 'source_code_uri' => 'https://github.com/sinatra/sinatra/tree/master/sinatra-contrib',
+ 'homepage_uri' => 'http://sinatrarb.com/contrib/',
+ 'documentation_uri' => 'https://www.rubydoc.info/gems/sinatra-contrib',
+ 'rubygems_mfa_required' => 'true'
+ }
+
s.required_ruby_version = '>= 2.6.0'
- s.add_dependency "sinatra", version
- s.add_dependency "mustermann", "~> 3.0"
- s.add_dependency "tilt", "~> 2.0"
- s.add_dependency "rack-protection", version
- s.add_dependency "multi_json"
+ s.add_dependency 'multi_json'
+ s.add_dependency 'mustermann', '~> 3.0'
+ s.add_dependency 'rack-protection', version
+ s.add_dependency 'sinatra', version
+ s.add_dependency 'tilt', '~> 2.0'
- s.add_development_dependency "rspec", "~> 3"
- s.add_development_dependency "haml"
- s.add_development_dependency "erubi"
- s.add_development_dependency "slim"
- s.add_development_dependency "builder"
- s.add_development_dependency "liquid"
- s.add_development_dependency "redcarpet"
- s.add_development_dependency "asciidoctor"
- s.add_development_dependency "nokogiri"
- s.add_development_dependency "markaby"
- s.add_development_dependency "rake", "< 11"
- s.add_development_dependency "rack-test", "~> 2"
+ s.add_development_dependency 'asciidoctor'
+ s.add_development_dependency 'builder'
+ s.add_development_dependency 'erubi'
+ s.add_development_dependency 'haml'
+ s.add_development_dependency 'liquid'
+ s.add_development_dependency 'markaby'
+ s.add_development_dependency 'nokogiri'
+ s.add_development_dependency 'rack-test', '~> 2'
+ s.add_development_dependency 'rake', '< 11'
+ s.add_development_dependency 'redcarpet'
+ s.add_development_dependency 'rspec', '~> 3'
+ s.add_development_dependency 'slim'
end
diff --git a/sinatra-contrib/spec/cookies_spec.rb b/sinatra-contrib/spec/cookies_spec.rb
index 4de61f16fe..852f642574 100644
--- a/sinatra-contrib/spec/cookies_spec.rb
+++ b/sinatra-contrib/spec/cookies_spec.rb
@@ -1,14 +1,14 @@
require 'spec_helper'
RSpec.describe Sinatra::Cookies do
- def cookie_route(*cookies, &block)
+ def cookie_route(*cookies, headers: {}, &block)
result = nil
set_cookie(cookies)
@cookie_app.get('/') do
result = instance_eval(&block)
"ok"
end
- get '/', {}, @headers || {}
+ get '/', {}, headers || {}
expect(last_response).to be_ok
expect(body).to eq("ok")
result
@@ -97,8 +97,8 @@ def cookies(*set_cookies)
end
it 'sets domain to nil if localhost' do
- @headers = {'HTTP_HOST' => 'localhost'}
- expect(cookie_route do
+ headers = {'HTTP_HOST' => 'localhost'}
+ expect(cookie_route(headers: headers) do
cookies['foo'] = 'bar'
response['Set-Cookie']
end).not_to include("domain")
diff --git a/sinatra-contrib/spec/namespace_spec.rb b/sinatra-contrib/spec/namespace_spec.rb
index c747001745..0382dda53c 100644
--- a/sinatra-contrib/spec/namespace_spec.rb
+++ b/sinatra-contrib/spec/namespace_spec.rb
@@ -263,6 +263,7 @@ def foo
specify 'are accepted in the before-filter' do
namespace '/foo' do
+ before { @yes = nil }
before(:host_name => 'example.com') { @yes = 'yes' }
send(verb) { @yes || 'no' }
end
diff --git a/sinatra.gemspec b/sinatra.gemspec
index a0f01b9731..8f9cf599b3 100644
--- a/sinatra.gemspec
+++ b/sinatra.gemspec
@@ -1,51 +1,54 @@
-version = File.read(File.expand_path("VERSION", __dir__)).strip
+# frozen_string_literal: true
+
+version = File.read(File.expand_path('VERSION', __dir__)).strip
Gem::Specification.new 'sinatra', version do |s|
- s.description = "Sinatra is a DSL for quickly creating web applications in Ruby with minimal effort."
- s.summary = "Classy web-development dressed in a DSL"
- s.authors = ["Blake Mizerany", "Ryan Tomayko", "Simon Rozet", "Konstantin Haase"]
- s.email = "sinatrarb@googlegroups.com"
- s.homepage = "http://sinatrarb.com/"
+ s.description = 'Sinatra is a DSL for quickly creating web applications in Ruby with minimal effort.'
+ s.summary = 'Classy web-development dressed in a DSL'
+ s.authors = ['Blake Mizerany', 'Ryan Tomayko', 'Simon Rozet', 'Konstantin Haase']
+ s.email = 'sinatrarb@googlegroups.com'
+ s.homepage = 'http://sinatrarb.com/'
s.license = 'MIT'
s.files = Dir['README*.md', 'lib/**/*', 'examples/*'] + [
- ".yardopts",
- "AUTHORS.md",
- "CHANGELOG.md",
- "CONTRIBUTING.md",
- "Gemfile",
- "LICENSE",
- "MAINTENANCE.md",
- "Rakefile",
- "SECURITY.md",
- "sinatra.gemspec",
- "VERSION"]
+ '.yardopts',
+ 'AUTHORS.md',
+ 'CHANGELOG.md',
+ 'CONTRIBUTING.md',
+ 'Gemfile',
+ 'LICENSE',
+ 'MAINTENANCE.md',
+ 'Rakefile',
+ 'SECURITY.md',
+ 'sinatra.gemspec',
+ 'VERSION'
+ ]
s.extra_rdoc_files = %w[README.md LICENSE]
s.rdoc_options = %w[--line-numbers --title Sinatra --main README.rdoc --encoding=UTF-8]
- if s.respond_to?(:metadata)
- s.metadata = {
- 'source_code_uri' => 'https://github.com/sinatra/sinatra',
- 'changelog_uri' => 'https://github.com/sinatra/sinatra/blob/master/CHANGELOG.md',
- 'homepage_uri' => 'http://sinatrarb.com/',
- 'bug_tracker_uri' => 'https://github.com/sinatra/sinatra/issues',
- 'mailing_list_uri' => 'http://groups.google.com/group/sinatrarb',
- 'documentation_uri' => 'https://www.rubydoc.info/gems/sinatra'
- }
- else
- raise <<-EOF
+ unless s.respond_to?(:metadata)
+ raise <<-WARN
RubyGems 2.0 or newer is required to protect against public gem pushes. You can update your rubygems version by running:
gem install rubygems-update
update_rubygems:
gem update --system
-EOF
+ WARN
end
+ s.metadata = {
+ 'source_code_uri' => 'https://github.com/sinatra/sinatra',
+ 'changelog_uri' => 'https://github.com/sinatra/sinatra/blob/master/CHANGELOG.md',
+ 'homepage_uri' => 'http://sinatrarb.com/',
+ 'bug_tracker_uri' => 'https://github.com/sinatra/sinatra/issues',
+ 'mailing_list_uri' => 'http://groups.google.com/group/sinatrarb',
+ 'documentation_uri' => 'https://www.rubydoc.info/gems/sinatra'
+ }
+
s.required_ruby_version = '>= 2.6.0'
+ s.add_dependency 'mustermann', '~> 3.0'
s.add_dependency 'rack', '~> 2.2', '>= 2.2.4'
- s.add_dependency 'tilt', '~> 2.0'
s.add_dependency 'rack-protection', version
- s.add_dependency 'mustermann', '~> 3.0'
+ s.add_dependency 'tilt', '~> 2.0'
s.add_development_dependency 'rack-test', '~> 2'
end