DEV: Prefer \A and \z over ^ and $ in regexes (#19936)

This commit is contained in:
Daniel Waterworth 2023-01-20 12:52:49 -06:00 committed by GitHub
parent f7907a3645
commit 666536cbd1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
115 changed files with 294 additions and 291 deletions

View File

@ -238,11 +238,11 @@ class Admin::BackupsController < Admin::AdminController
end
def valid_extension?(filename)
/\.(tar\.gz|t?gz)$/i =~ filename
/\.(tar\.gz|t?gz)\z/i =~ filename
end
def valid_filename?(filename)
!!(/^[a-zA-Z0-9\._\-]+$/ =~ filename)
!!(/\A[a-zA-Z0-9\._\-]+\z/ =~ filename)
end
def render_error(message_key)

View File

@ -7,9 +7,9 @@ class Admin::ReportsController < Admin::StaffController
ApplicationRequest
.req_types
.keys
.select { |r| r =~ /^page_view_/ && r !~ /mobile/ }
.select { |r| r =~ /\Apage_view_/ && r !~ /mobile/ }
.map { |r| r + "_reqs" } +
Report.singleton_methods.grep(/^report_(?!about|storage_stats)/)
Report.singleton_methods.grep(/\Areport_(?!about|storage_stats)/)
reports =
reports_methods.map do |name|
@ -61,7 +61,7 @@ class Admin::ReportsController < Admin::StaffController
def show
report_type = params[:type]
raise Discourse::NotFound unless report_type =~ /^[a-z0-9\_]+$/
raise Discourse::NotFound unless report_type =~ /\A[a-z0-9\_]+\z/
args = parse_params(params)

View File

@ -160,7 +160,7 @@ class Admin::SiteTextsController < Admin::AdminController
{ id: key, value: value, locale: locale }
end
PLURALIZED_REGEX = /(.*)\.(zero|one|two|few|many|other)$/
PLURALIZED_REGEX = /(.*)\.(zero|one|two|few|many|other)\z/
def find_site_text(locale)
if self.class.restricted_keys.include?(params[:id])

View File

@ -108,7 +108,7 @@ class Admin::ThemesController < Admin::AdminController
render json: @theme, status: :created
rescue RemoteTheme::ImportError => e
if params[:force]
theme_name = params[:remote].gsub(/.git$/, "").split("/").last
theme_name = params[:remote].gsub(/.git\z/, "").split("/").last
remote_theme = RemoteTheme.new
remote_theme.private_key = private_key

View File

@ -679,7 +679,7 @@ class ApplicationController < ActionController::Base
DiscoursePluginRegistry.html_builders.each do |name, _|
if name.start_with?("client:")
data[name.sub(/^client:/, "")] = DiscoursePluginRegistry.build_html(name, self)
data[name.sub(/\Aclient:/, "")] = DiscoursePluginRegistry.build_html(name, self)
end
end

View File

@ -28,11 +28,11 @@ class EmbedController < ApplicationController
end
if @embed_id = params[:discourse_embed_id]
raise Discourse::InvalidParameters.new(:embed_id) unless @embed_id =~ /^de\-[a-zA-Z0-9]+$/
raise Discourse::InvalidParameters.new(:embed_id) unless @embed_id =~ /\Ade\-[a-zA-Z0-9]+\z/
end
if @embed_class = params[:embed_class]
unless @embed_class =~ /^[a-zA-Z0-9\-_]+$/
unless @embed_class =~ /\A[a-zA-Z0-9\-_]+\z/
raise Discourse::InvalidParameters.new(:embed_class)
end
end
@ -139,7 +139,7 @@ class EmbedController < ApplicationController
by_url = {}
if embed_urls.present?
urls = embed_urls.map { |u| u.sub(/#discourse-comments$/, "").sub(%r{/$}, "") }
urls = embed_urls.map { |u| u.sub(/#discourse-comments\z/, "").sub(%r{/\z}, "") }
topic_embeds = TopicEmbed.where(embed_url: urls).includes(:topic).references(:topic)
topic_embeds.each do |te|

View File

@ -71,6 +71,6 @@ class ExtraLocalesController < ApplicationController
private
def valid_bundle?(bundle)
bundle == OVERRIDES_BUNDLE || (bundle =~ /^(admin|wizard)$/ && current_user&.staff?)
bundle == OVERRIDES_BUNDLE || (bundle =~ /\A(admin|wizard)\z/ && current_user&.staff?)
end
end

View File

@ -203,7 +203,7 @@ class SessionController < ApplicationController
end
# If it's not a relative URL check the host
if return_path !~ %r{^/[^/]}
if return_path !~ %r{\A/[^/]}
begin
uri = URI(return_path)
if (uri.hostname == Discourse.current_hostname)

View File

@ -47,7 +47,7 @@ class ThemeJavascriptsController < ApplicationController
def show_tests
digest = params[:digest]
raise Discourse::NotFound if !digest.match?(/^\h{40}$/)
raise Discourse::NotFound if !digest.match?(/\A\h{40}\z/)
theme = Theme.find_by(id: params[:theme_id])
raise Discourse::NotFound if theme.blank?

View File

@ -83,7 +83,7 @@ class TopicsController < ApplicationController
# Special case: a slug with a number in front should look by slug first before looking
# up that particular number
if params[:id] && params[:id] =~ /^\d+[^\d\\]+$/
if params[:id] && params[:id] =~ /\A\d+[^\d\\]+\z/
topic = Topic.find_by_slug(params[:id])
return redirect_to_correct_topic(topic, opts[:post_number]) if topic
end

View File

@ -39,7 +39,7 @@ class UserAvatarsController < ApplicationController
def show_proxy_letter
is_asset_path
if SiteSetting.external_system_avatars_url !~ %r{^/letter_avatar_proxy}
if SiteSetting.external_system_avatars_url !~ %r{\A/letter_avatar_proxy}
raise Discourse::NotFound
end

View File

@ -473,7 +473,7 @@ class UsersController < ApplicationController
end
def my_redirect
raise Discourse::NotFound if params[:path] !~ %r{^[a-z_\-/]+$}
raise Discourse::NotFound if params[:path] !~ %r{\A[a-z_\-/]+\z}
if current_user.blank?
cookies[:destination_url] = path("/my/#{params[:path]}")

View File

@ -50,7 +50,7 @@ module ApplicationHelper
def google_universal_analytics_json(ua_domain_name = nil)
result = {}
result[:cookieDomain] = ua_domain_name.gsub(%r{^http(s)?://}, "") if ua_domain_name
result[:cookieDomain] = ua_domain_name.gsub(%r{\Ahttp(s)?://}, "") if ua_domain_name
result[:userId] = current_user.id if current_user.present?
result[:allowLinker] = true if SiteSetting.ga_universal_auto_link_domains.present?
result.to_json
@ -117,9 +117,9 @@ module ApplicationHelper
# seconds.
if !script.start_with?("discourse/tests/")
if is_brotli_req?
path = path.gsub(/\.([^.]+)$/, '.br.\1')
path = path.gsub(/\.([^.]+)\z/, '.br.\1')
elsif is_gzip_req?
path = path.gsub(/\.([^.]+)$/, '.gz.\1')
path = path.gsub(/\.([^.]+)\z/, '.gz.\1')
end
end
elsif GlobalSetting.cdn_url&.start_with?("https") && is_brotli_req? &&

View File

@ -20,8 +20,8 @@ module UserNotificationsHelper
def logo_url
logo_url = SiteSetting.site_digest_logo_url
logo_url = SiteSetting.site_logo_url if logo_url.blank? || logo_url =~ /\.svg$/i
return nil if logo_url.blank? || logo_url =~ /\.svg$/i
logo_url = SiteSetting.site_logo_url if logo_url.blank? || logo_url =~ /\.svg\z/i
return nil if logo_url.blank? || logo_url =~ /\.svg\z/i
logo_url
end

View File

@ -29,7 +29,7 @@ module Jobs
end
def self.num_email_retry_jobs
Sidekiq::RetrySet.new.count { |job| job.klass =~ /Email$/ }
Sidekiq::RetrySet.new.count { |job| job.klass =~ /Email\z/ }
end
class Base

View File

@ -4,7 +4,7 @@ class Jobs::Onceoff < ::Jobs::Base
sidekiq_options retry: false
def self.name_for(klass)
klass.name.sub(/^Jobs\:\:/, "")
klass.name.sub(/\AJobs\:\:/, "")
end
def running_key_name

View File

@ -29,9 +29,9 @@ module Jobs
@raw_quote_regex = /(\[quote\s*=\s*["'']?)#{@old_username}(\,?[^\]]*\])/i
cooked_username = PrettyText::Helpers.format_username(@old_username)
@cooked_mention_username_regex = /^@#{cooked_username}$/i
@cooked_mention_username_regex = /\A@#{cooked_username}\z/i
@cooked_mention_user_path_regex =
%r{^/u(?:sers)?/#{UrlHelper.encode_component(cooked_username)}$}i
%r{\A/u(?:sers)?/#{UrlHelper.encode_component(cooked_username)}\z}i
@cooked_quote_username_regex = /(?<=\s)#{cooked_username}(?=:)/i
update_posts

View File

@ -377,7 +377,7 @@ class AdminDashboardData
end
def subfolder_ends_in_slash_check
I18n.t("dashboard.subfolder_ends_in_slash") if Discourse.base_path =~ %r{/$}
I18n.t("dashboard.subfolder_ends_in_slash") if Discourse.base_path =~ %r{/\z}
end
def email_polling_errored_recently

View File

@ -421,7 +421,7 @@ class Category < ActiveRecord::Base
end
# only allow to use category itself id.
match_id = /^(\d+)-category/.match(self.slug)
match_id = /\A(\d+)-category/.match(self.slug)
if match_id.present?
errors.add(:slug, :invalid) if new_record? || (match_id[1] != self.id.to_s)
end
@ -897,7 +897,7 @@ class Category < ActiveRecord::Base
slug_path.inject(nil) do |parent_id, slug|
category = Category.where(slug: slug, parent_category_id: parent_id)
if match_id = /^(\d+)-category/.match(slug).presence
if match_id = /\A(\d+)-category/.match(slug).presence
category = category.or(Category.where(id: match_id[1], parent_category_id: parent_id))
end

View File

@ -20,7 +20,7 @@ module HasCustomFields
sorted_types = types.keys.select { |k| k.end_with?("*") }.sort_by(&:length).reverse
sorted_types.each { |t| return types[t] if key =~ /^#{t}/i }
sorted_types.each { |t| return types[t] if key =~ /\A#{t}/i }
types[key]
end

View File

@ -66,7 +66,7 @@ module Reports::TopUploads
builder.where("up.created_at < :end_date", end_date: report.end_date)
if extension_filter
builder.where("up.extension = :extension", extension: extension_filter.sub(/^\./, ""))
builder.where("up.extension = :extension", extension: extension_filter.sub(/\A\./, ""))
end
builder.query.each do |row|

View File

@ -6,8 +6,8 @@ class EmbeddableHost < ActiveRecord::Base
after_destroy :reset_embedding_settings
before_validation do
self.host.sub!(%r{^https?://}, "")
self.host.sub!(%r{/.*$}, "")
self.host.sub!(%r{\Ahttps?://}, "")
self.host.sub!(%r{/.*\z}, "")
end
# TODO(2021-07-23): Remove

View File

@ -173,7 +173,7 @@ class Emoji
emojis.each do |name, url|
result << Emoji.new.tap do |e|
e.name = name
url = (Discourse.base_path + url) if url[%r{^/[^/]}]
url = (Discourse.base_path + url) if url[%r{\A/[^/]}]
e.url = url
e.group = group || DEFAULT_GROUP
end

View File

@ -5,7 +5,7 @@ class GlobalSetting
define_singleton_method(key) { provider.lookup(key, default) }
end
VALID_SECRET_KEY ||= /^[0-9a-f]{128}$/
VALID_SECRET_KEY ||= /\A[0-9a-f]{128}\z/
# this is named SECRET_TOKEN as opposed to SECRET_KEY_BASE
# for legacy reasons
REDIS_SECRET_KEY ||= "SECRET_TOKEN"
@ -251,7 +251,7 @@ class GlobalSetting
class BaseProvider
def self.coerce(setting)
return setting == "true" if setting == "true" || setting == "false"
return $1.to_i if setting.to_s.strip =~ /^([0-9]+)$/
return $1.to_i if setting.to_s.strip =~ /\A([0-9]+)\z/
setting
end
@ -283,7 +283,7 @@ class GlobalSetting
.result()
.split("\n")
.each do |line|
if line =~ /^\s*([a-z_]+[a-z0-9_]*)\s*=\s*(\"([^\"]*)\"|\'([^\']*)\'|[^#]*)/
if line =~ /\A\s*([a-z_]+[a-z0-9_]*)\s*=\s*(\"([^\"]*)\"|\'([^\']*)\'|[^#]*)/
@data[$1.strip.to_sym] = ($4 || $3 || $2).strip
end
end
@ -314,7 +314,7 @@ class GlobalSetting
end
def keys
ENV.keys.select { |k| k =~ /^DISCOURSE_/ }.map { |k| k[10..-1].downcase.to_sym }
ENV.keys.select { |k| k =~ /\ADISCOURSE_/ }.map { |k| k[10..-1].downcase.to_sym }
end
end

View File

@ -1005,7 +1005,7 @@ class Group < ActiveRecord::Base
user = email_username_user
domain = email_username_domain
if user.present? && domain.present?
/^#{Regexp.escape(user)}(\+[^@]*)?@#{Regexp.escape(domain)}$/i
/\A#{Regexp.escape(user)}(\+[^@]*)?@#{Regexp.escape(domain)}\z/i
end
end
@ -1160,8 +1160,8 @@ class Group < ActiveRecord::Base
value
.split("|")
.each do |domain|
domain.sub!(%r{^https?://}, "")
domain.sub!(%r{/.*$}, "")
domain.sub!(%r{\Ahttps?://}, "")
domain.sub!(%r{/.*\z}, "")
if domain =~ Group::VALID_DOMAIN_REGEX
valid_domains << domain

View File

@ -142,7 +142,7 @@ class OptimizedImage < ActiveRecord::Base
end
def local?
!(url =~ %r{^(https?:)?//})
!(url =~ %r{\A(https?:)?//})
end
def calculate_filesize
@ -337,7 +337,7 @@ class OptimizedImage < ActiveRecord::Base
else
error = +"Failed to optimize image:"
if e.message =~ /^convert:([^`]+)/
if e.message =~ /\Aconvert:([^`]+)/
error << $1
else
error << " unknown reason"

View File

@ -7,8 +7,8 @@ class PostActionType < ActiveRecord::Base
include AnonCacheInvalidator
def expire_cache
ApplicationSerializer.expire_cache_fragment!(/^post_action_types_/)
ApplicationSerializer.expire_cache_fragment!(/^post_action_flag_types_/)
ApplicationSerializer.expire_cache_fragment!(/\Apost_action_types_/)
ApplicationSerializer.expire_cache_fragment!(/\Apost_action_flag_types_/)
end
class << self

View File

@ -8,7 +8,7 @@ class PublishedPage < ActiveRecord::Base
validate :slug_format
def slug_format
if slug !~ /^[a-zA-Z\-\_0-9]+$/
if slug !~ /\A[a-zA-Z\-\_0-9]+\z/
errors.add(:slug, I18n.t("publish_page.slug_errors.invalid"))
elsif %w[check-slug by-topic].include?(slug)
errors.add(:slug, I18n.t("publish_page.slug_errors.unavailable"))

View File

@ -15,8 +15,8 @@ class RemoteTheme < ActiveRecord::Base
ALLOWED_FIELDS = %w[scss embedded_scss head_tag header after_header body_tag footer]
GITHUB_REGEXP = %r{^https?://github\.com/}
GITHUB_SSH_REGEXP = %r{^ssh://git@github\.com:}
GITHUB_REGEXP = %r{\Ahttps?://github\.com/}
GITHUB_SSH_REGEXP = %r{\Assh://git@github\.com:}
has_one :theme, autosave: false
scope :joined_remotes,
@ -329,7 +329,7 @@ class RemoteTheme < ActiveRecord::Base
def github_diff_link
if github_repo_url.present? && local_version != remote_version
"#{github_repo_url.gsub(/\.git$/, "")}/compare/#{local_version}...#{remote_version}"
"#{github_repo_url.gsub(/\.git\z/, "")}/compare/#{local_version}...#{remote_version}"
end
end

View File

@ -268,8 +268,8 @@ class Report
wrap_slow_query do
if respond_to?(report_method)
public_send(report_method, report)
elsif type =~ /_reqs$/
req_report(report, type.split(/_reqs$/)[0].to_sym)
elsif type =~ /_reqs\z/
req_report(report, type.split(/_reqs\z/)[0].to_sym)
else
return nil
end

View File

@ -60,7 +60,7 @@ class Reviewable < ActiveRecord::Base
end
def self.valid_type?(type)
return false unless type =~ /^Reviewable[A-Za-z]+$/
return false unless type =~ /\AReviewable[A-Za-z]+\z/
type.constantize <= Reviewable
rescue NameError
false

View File

@ -17,7 +17,7 @@ class ScreenedUrl < ActiveRecord::Base
def normalize
self.url = ScreenedUrl.normalize_url(self.url) if self.url
self.domain = self.domain.downcase.sub(/^www\./, "") if self.domain
self.domain = self.domain.downcase.sub(/\Awww\./, "") if self.domain
end
def self.watch(url, domain, opts = {})
@ -30,8 +30,8 @@ class ScreenedUrl < ActiveRecord::Base
def self.normalize_url(url)
normalized = url.gsub(%r{http(s?)://}i, "")
normalized.gsub!(%r{(/)+$}, "") # trim trailing slashes
normalized.gsub!(%r{^([^/]+)(?:/)?}) { |m| m.downcase } # downcase the domain part of the url
normalized.gsub!(%r{(/)+\z}, "") # trim trailing slashes
normalized.gsub!(%r{\A([^/]+)(?:/)?}) { |m| m.downcase } # downcase the domain part of the url
normalized
end
end

View File

@ -94,7 +94,7 @@ class ThemeField < ActiveRecord::Base
.css('script[type="text/x-handlebars"]')
.each do |node|
name = node["name"] || node["data-template-name"] || "broken"
is_raw = name =~ /\.(raw|hbr)$/
is_raw = name =~ /\.(raw|hbr)\z/
hbs_template = node.inner_html
begin
@ -523,63 +523,63 @@ class ThemeField < ActiveRecord::Base
FILE_MATCHERS = [
ThemeFileMatcher.new(
regex:
%r{^(?<target>(?:mobile|desktop|common))/(?<name>(?:head_tag|header|after_header|body_tag|footer))\.html$},
%r{\A(?<target>(?:mobile|desktop|common))/(?<name>(?:head_tag|header|after_header|body_tag|footer))\.html\z},
targets: %i[mobile desktop common],
names: %w[head_tag header after_header body_tag footer],
types: :html,
canonical: ->(h) { "#{h[:target]}/#{h[:name]}.html" },
),
ThemeFileMatcher.new(
regex: %r{^(?<target>(?:mobile|desktop|common))/(?:\k<target>)\.scss$},
regex: %r{\A(?<target>(?:mobile|desktop|common))/(?:\k<target>)\.scss\z},
targets: %i[mobile desktop common],
names: "scss",
types: :scss,
canonical: ->(h) { "#{h[:target]}/#{h[:target]}.scss" },
),
ThemeFileMatcher.new(
regex: %r{^common/embedded\.scss$},
regex: %r{\Acommon/embedded\.scss\z},
targets: :common,
names: "embedded_scss",
types: :scss,
canonical: ->(h) { "common/embedded.scss" },
),
ThemeFileMatcher.new(
regex: %r{^common/color_definitions\.scss$},
regex: %r{\Acommon/color_definitions\.scss\z},
targets: :common,
names: "color_definitions",
types: :scss,
canonical: ->(h) { "common/color_definitions.scss" },
),
ThemeFileMatcher.new(
regex: %r{^(?:scss|stylesheets)/(?<name>.+)\.scss$},
regex: %r{\A(?:scss|stylesheets)/(?<name>.+)\.scss\z},
targets: :extra_scss,
names: nil,
types: :scss,
canonical: ->(h) { "stylesheets/#{h[:name]}.scss" },
),
ThemeFileMatcher.new(
regex: %r{^javascripts/(?<name>.+)$},
regex: %r{\Ajavascripts/(?<name>.+)\z},
targets: :extra_js,
names: nil,
types: :js,
canonical: ->(h) { "javascripts/#{h[:name]}" },
),
ThemeFileMatcher.new(
regex: %r{^test/(?<name>.+)$},
regex: %r{\Atest/(?<name>.+)\z},
targets: :tests_js,
names: nil,
types: :js,
canonical: ->(h) { "test/#{h[:name]}" },
),
ThemeFileMatcher.new(
regex: /^settings\.ya?ml$/,
regex: /\Asettings\.ya?ml\z/,
names: "yaml",
types: :yaml,
targets: :settings,
canonical: ->(h) { "settings.yml" },
),
ThemeFileMatcher.new(
regex: %r{^locales/(?<name>(?:#{I18n.available_locales.join("|")}))\.yml$},
regex: %r{\Alocales/(?<name>(?:#{I18n.available_locales.join("|")}))\.yml\z},
names: I18n.available_locales.map(&:to_s),
types: :yaml,
targets: :translations,

View File

@ -1200,7 +1200,7 @@ class Topic < ActiveRecord::Base
else
!!invite_to_topic(invited_by, target_user, group_ids, guardian)
end
elsif username_or_email =~ /^.+@.+$/ && guardian.can_invite_via_email?(self)
elsif username_or_email =~ /\A.+@.+\z/ && guardian.can_invite_via_email?(self)
!!Invite.generate(
invited_by,
email: username_or_email,

View File

@ -25,7 +25,7 @@ class TopicEmbed < ActiveRecord::Base
end
def self.normalize_url(url)
url.downcase.sub(%r{/$}, "").sub(/\-+/, "-").strip
url.downcase.sub(%r{/\z}, "").sub(/\-+/, "-").strip
end
def self.imported_from_html(url)
@ -36,7 +36,7 @@ class TopicEmbed < ActiveRecord::Base
# Import an article from a source (RSS/Atom/Other)
def self.import(user, url, title, contents, category_id: nil, cook_method: nil, tags: nil)
return unless url =~ %r{^https?\://}
return unless url =~ %r{\Ahttps?\://}
contents = first_paragraph_from(contents) if SiteSetting.embed_truncate && cook_method.nil?
contents ||= ""
@ -253,7 +253,7 @@ class TopicEmbed < ActiveRecord::Base
end
def self.topic_id_for_embed(embed_url)
embed_url = normalize_url(embed_url).sub(%r{^https?\://}, "")
embed_url = normalize_url(embed_url).sub(%r{\Ahttps?\://}, "")
TopicEmbed.where("embed_url ~* ?", "^https?://#{Regexp.escape(embed_url)}$").pluck_first(
:topic_id,
)

View File

@ -175,7 +175,7 @@ class TopicLink < ActiveRecord::Base
lookup = {}
results.each do |tl|
normalized = tl.url.downcase.sub(%r{^https?://}, "").sub(%r{/$}, "")
normalized = tl.url.downcase.sub(%r{\Ahttps?://}, "").sub(%r{/\z}, "")
lookup[normalized] = {
domain: tl.domain,
username: tl.user.username_lower,

View File

@ -21,9 +21,9 @@ class TopicLinkClick < ActiveRecord::Base
uri = UrlHelper.relaxed_parse(url)
urls = Set.new
urls << url
if url =~ /^http/
urls << url.sub(/^https/, "http")
urls << url.sub(/^http:/, "https:")
if url =~ /\Ahttp/
urls << url.sub(/\Ahttps/, "http")
urls << url.sub(/\Ahttp:/, "https:")
urls << UrlHelper.schemaless(url)
end
urls << UrlHelper.absolute_without_cdn(url)
@ -90,7 +90,7 @@ class TopicLinkClick < ActiveRecord::Base
# If no link is found...
unless link.present?
# ... return the url for relative links or when using the same host
return url if url =~ %r{^/[^/]} || uri.try(:host) == Discourse.current_hostname
return url if url =~ %r{\A/[^/]} || uri.try(:host) == Discourse.current_hostname
# If we have it somewhere else on the site, just allow the redirect.
# This is likely due to a onebox of another topic.

View File

@ -147,7 +147,7 @@ class TranslationOverride < ActiveRecord::Base
end
def transform_pluralized_key(key)
match = key.match(/(.*)\.(zero|two|few|many)$/)
match = key.match(/(.*)\.(zero|two|few|many)\z/)
match ? match.to_a.second + ".other" : key
end
end

View File

@ -263,7 +263,7 @@ class Upload < ActiveRecord::Base
end
def local?
!(url =~ %r{^(https?:)?//})
!(url =~ %r{\A(https?:)?//})
end
def fix_dimensions!
@ -526,7 +526,7 @@ class Upload < ActiveRecord::Base
# keep track of the url
previous_url = upload.url.dup
# where is the file currently stored?
external = previous_url =~ %r{^//}
external = previous_url =~ %r{\A//}
# download if external
if external
url = SiteSetting.scheme + ":" + previous_url

View File

@ -396,7 +396,7 @@ class User < ActiveRecord::Base
.reserved_usernames
.unicode_normalize
.split("|")
.any? { |reserved| username.match?(/^#{Regexp.escape(reserved).gsub('\*', ".*")}$/) }
.any? { |reserved| username.match?(/\A#{Regexp.escape(reserved).gsub('\*', ".*")}\z/) }
end
def self.editable_user_custom_fields(by_staff: false)
@ -1117,7 +1117,7 @@ class User < ActiveRecord::Base
# TODO it may be worth caching this in a distributed cache, should be benched
if SiteSetting.external_system_avatars_enabled
url = SiteSetting.external_system_avatars_url.dup
url = +"#{Discourse.base_path}#{url}" unless url =~ %r{^https?://}
url = +"#{Discourse.base_path}#{url}" unless url =~ %r{\Ahttps?://}
url.gsub! "{color}", letter_avatar_color(normalized_username)
url.gsub! "{username}", UrlHelper.encode_component(username)
url.gsub! "{first_letter}",

View File

@ -45,7 +45,7 @@ class UserProfile < ActiveRecord::Base
def bio_excerpt(length = 350, opts = {})
return nil if bio_cooked.blank?
excerpt = PrettyText.excerpt(bio_cooked, length, opts).sub(/<br>$/, "")
excerpt = PrettyText.excerpt(bio_cooked, length, opts).sub(/<br>\z/, "")
return excerpt if excerpt.blank? || (user.has_trust_level?(TrustLevel[1]) && !user.suspended?)
PrettyText.strip_links(excerpt)
end

View File

@ -40,13 +40,13 @@ class UsernameValidator
errors.empty?
end
CONFUSING_EXTENSIONS ||= /\.(js|json|css|htm|html|xml|jpg|jpeg|png|gif|bmp|ico|tif|tiff|woff)$/i
CONFUSING_EXTENSIONS ||= /\.(js|json|css|htm|html|xml|jpg|jpeg|png|gif|bmp|ico|tif|tiff|woff)\z/i
MAX_CHARS ||= 60
ASCII_INVALID_CHAR_PATTERN ||= /[^\w.-]/
UNICODE_INVALID_CHAR_PATTERN ||= /[^\p{Alnum}\p{M}._-]/
INVALID_LEADING_CHAR_PATTERN ||= /^[^\p{Alnum}\p{M}_]+/
INVALID_TRAILING_CHAR_PATTERN ||= /[^\p{Alnum}\p{M}]+$/
INVALID_LEADING_CHAR_PATTERN ||= /\A[^\p{Alnum}\p{M}_]+/
INVALID_TRAILING_CHAR_PATTERN ||= /[^\p{Alnum}\p{M}]+\z/
REPEATED_SPECIAL_CHAR_PATTERN ||= /[-_.]{2,}/
private

View File

@ -19,7 +19,7 @@ class WatchedWord < ActiveRecord::Base
before_validation do
self.word = self.class.normalize_word(self.word)
if self.action == WatchedWord.actions[:link] && !(self.replacement =~ %r{^https?://})
if self.action == WatchedWord.actions[:link] && !(self.replacement =~ %r{\Ahttps?://})
self.replacement =
"#{Discourse.base_url}#{self.replacement&.starts_with?("/") ? "" : "/"}#{self.replacement}"
end

View File

@ -113,7 +113,7 @@ class UserCardSerializer < BasicUserSerializer
end
return if uri.nil? || uri.host.nil?
uri.host.sub(/^www\./, "") + uri.path
uri.host.sub(/\Awww\./, "") + uri.path
end
def ignored

View File

@ -50,7 +50,7 @@ class SearchIndexer
.reduce(additional_lexemes) do |array, (lexeme, _, positions)|
count = 0
if lexeme !~ /^(\d+\.)?(\d+\.)*(\*|\d+)$/
if lexeme !~ /\A(\d+\.)?(\d+\.)*(\*|\d+)\z/
loop do
count += 1
break if count >= 10 # Safeguard here to prevent infinite loop when a term has many dots

View File

@ -347,6 +347,6 @@ class UserUpdater
def format_url(website)
return nil if website.blank?
website =~ /^http/ ? website : "http://#{website}"
website =~ /\Ahttp/ ? website : "http://#{website}"
end
end

View File

@ -50,7 +50,7 @@ class AdminUserIndexQuery
custom_order = params[:order]
if custom_order.present? &&
without_dir = SORTABLE_MAPPING[custom_order.downcase.sub(/ (asc|desc)$/, "")]
without_dir = SORTABLE_MAPPING[custom_order.downcase.sub(/ (asc|desc)\z/, "")]
order << "#{without_dir} #{custom_direction}"
end

View File

@ -153,7 +153,7 @@ class Autospec::Manager
filename, _ = failed_specs[0].split(":")
if filename && File.exist?(filename) && !File.directory?(filename)
spec = File.read(filename)
start, _ = spec.split(/\S*#focus\S*$/)
start, _ = spec.split(/\S*#focus\S*\z/)
if start.length < spec.length
line = start.scan(/\n/).length + 1
puts "Found #focus tag on line #{line}!"
@ -194,7 +194,7 @@ class Autospec::Manager
def listen_for_changes
puts "@@@@@@@@@@@@ listen_for_changes" if @debug
options = { ignore: %r{^lib/autospec} }
options = { ignore: %r{\Alib/autospec} }
if @opts[:force_polling]
options[:force_polling] = true
@ -216,7 +216,7 @@ class Autospec::Manager
# process_change can acquire a mutex and block
# the acceptor
Thread.new do
if file =~ /(es6|js)$/
if file =~ /(es6|js)\z/
process_change([[file]])
else
process_change([[file, line]])

View File

@ -10,11 +10,11 @@ class Autospec::ReloadCss
end
# css, scss, sass or handlebars
watch(/\.css$/)
watch(/\.ca?ss\.erb$/)
watch(/\.s[ac]ss$/)
watch(/\.hbs$/)
watch(/\.hbr$/)
watch(/\.css\z/)
watch(/\.ca?ss\.erb\z/)
watch(/\.s[ac]ss\z/)
watch(/\.hbs\z/)
watch(/\.hbr\z/)
def self.message_bus
MessageBus::Instance.new.tap do |bus|
@ -44,7 +44,7 @@ class Autospec::ReloadCss
p = p.sub(/\.sass\.erb/, "")
p = p.sub(/\.sass/, "")
p = p.sub(/\.scss/, "")
p = p.sub(%r{^app/assets/stylesheets}, "assets")
p = p.sub(%r{\Aapp/assets/stylesheets}, "assets")
{ name: p, hash: hash || SecureRandom.hex }
end
message_bus.publish "/file-change", paths

View File

@ -11,29 +11,31 @@ module Autospec
end
# Discourse specific
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/components/#{m[1]}_spec.rb" }
watch(%r{\Alib/(.+)\.rb\z}) { |m| "spec/components/#{m[1]}_spec.rb" }
watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
watch(%r{^app/(.+)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
watch(%r{^spec/.+_spec\.rb$})
watch(%r{^spec/support/.+\.rb$}) { "spec" }
watch(%r{\Aapp/(.+)\.rb\z}) { |m| "spec/#{m[1]}_spec.rb" }
watch(%r{\Aapp/(.+)(\.erb|\.haml)\z}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
watch(%r{\Aspec/.+_spec\.rb\z})
watch(%r{\Aspec/support/.+\.rb\z}) { "spec" }
watch("app/controllers/application_controller.rb") { "spec/requests" }
watch(%r{app/controllers/(.+).rb}) { |m| "spec/requests/#{m[1]}_spec.rb" }
watch(%r{^app/views/(.+)/.+\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
watch(%r{\Aapp/views/(.+)/.+\.(erb|haml)\z}) { |m| "spec/requests/#{m[1]}_spec.rb" }
watch(%r{^spec/fabricators/.+_fabricator\.rb$}) { "spec" }
watch(%r{\Aspec/fabricators/.+_fabricator\.rb\z}) { "spec" }
watch(%r{^app/assets/javascripts/pretty-text/.*\.js\.es6$}) do
watch(%r{\Aapp/assets/javascripts/pretty-text/.*\.js\.es6\z}) do
"spec/components/pretty_text_spec.rb"
end
watch(%r{\Aplugins/.*/discourse-markdown/.*\.js\.es6\z}) do
"spec/components/pretty_text_spec.rb"
end
watch(%r{^plugins/.*/discourse-markdown/.*\.js\.es6$}) { "spec/components/pretty_text_spec.rb" }
watch(%r{^plugins/.*/spec/.*\.rb})
watch(%r{^(plugins/.*/)plugin\.rb}) { |m| "#{m[1]}spec" }
watch(%r{^(plugins/.*)/(lib|app)}) { |m| "#{m[1]}/spec/integration" }
watch(%r{^(plugins/.*)/lib/(.*)\.rb}) { |m| "#{m[1]}/spec/lib/#{m[2]}_spec.rb" }
watch(%r{\Aplugins/.*/spec/.*\.rb})
watch(%r{\A(plugins/.*/)plugin\.rb}) { |m| "#{m[1]}spec" }
watch(%r{\A(plugins/.*)/(lib|app)}) { |m| "#{m[1]}/spec/integration" }
watch(%r{\A(plugins/.*)/lib/(.*)\.rb}) { |m| "#{m[1]}/spec/lib/#{m[2]}_spec.rb" }
RELOADERS = Set.new
def self.reload(pattern)

View File

@ -29,7 +29,7 @@ module Autospec
# launch rspec
Dir.chdir(Rails.root) do # rubocop:disable Discourse/NoChdir because this is not part of the app
env = { "RAILS_ENV" => "test" }
if specs.split(" ").any? { |s| s =~ %r{^(./)?plugins} }
if specs.split(" ").any? { |s| s =~ %r{\A(./)?plugins} }
env["LOAD_PLUGINS"] = "1"
puts "Loading plugins while running specs"
end

View File

@ -11,7 +11,7 @@ module BackupRestore
@filename = filename
@current_db = current_db
@root_tmp_directory = root_tmp_directory
@is_archive = !(@filename =~ /\.sql\.gz$/)
@is_archive = !(@filename =~ /\.sql\.gz\z/)
@store_location = location
end

View File

@ -164,7 +164,7 @@ module BackupRestore
DatabaseRestorer.core_migration_files.each do |path|
require path
class_name = File.basename(path, ".rb").sub(/^\d+_/, "").camelize
class_name = File.basename(path, ".rb").sub(/\A\d+_/, "").camelize
migration_class = class_name.constantize
if migration_class.const_defined?(:DROPPED_TABLES)

View File

@ -173,7 +173,7 @@ module BackupRestore
path = Regexp.quote(path)
end
%r{^#{path}[^/]*\.t?gz$}i
%r{\A#{path}[^/]*\.t?gz\z}i
end
end

View File

@ -8,7 +8,7 @@ class ComposerMessagesFinder
end
def self.check_methods
@check_methods ||= instance_methods.find_all { |m| m =~ /^check\_/ }
@check_methods ||= instance_methods.find_all { |m| m =~ /\Acheck\_/ }
end
def find

View File

@ -38,7 +38,7 @@ module Compression
def build_entry_path(dest_path, _, compressed_file_path)
basename = File.basename(compressed_file_path)
basename.gsub!(/#{Regexp.escape(extension)}$/, "")
basename.gsub!(/#{Regexp.escape(extension)}\z/, "")
File.join(dest_path, basename)
end

View File

@ -80,7 +80,7 @@ class ContentSecurityPolicy
uri.query = nil # CSP should not include query part of url
uri_string = uri.to_s.sub(%r{^//}, "") # Protocol-less CSP should not have // at beginning of URL
uri_string = uri.to_s.sub(%r{\A//}, "") # Protocol-less CSP should not have // at beginning of URL
auto_script_src_extension[:script_src] << uri_string
rescue URI::Error

View File

@ -242,10 +242,10 @@ class CookedPostProcessor
if !cropped && upload.width && resized_w > upload.width
cooked_url = UrlHelper.cook_url(upload.url, secure: @post.with_secure_uploads?)
srcset << ", #{cooked_url} #{ratio.to_s.sub(/\.0$/, "")}x"
srcset << ", #{cooked_url} #{ratio.to_s.sub(/\.0\z/, "")}x"
elsif t = upload.thumbnail(resized_w, resized_h)
cooked_url = UrlHelper.cook_url(t.url, secure: @post.with_secure_uploads?)
srcset << ", #{cooked_url} #{ratio.to_s.sub(/\.0$/, "")}x"
srcset << ", #{cooked_url} #{ratio.to_s.sub(/\.0\z/, "")}x"
end
img[
@ -295,7 +295,7 @@ class CookedPostProcessor
def get_filename(upload, src)
return File.basename(src) unless upload
return upload.original_filename unless upload.original_filename =~ /^blob(\.png)?$/i
return upload.original_filename unless upload.original_filename =~ /\Ablob(\.png)?\z/i
I18n.t("upload.pasted_image_filename")
end

View File

@ -174,7 +174,7 @@ module CookedProcessorMixin
return @size_cache[url] if @size_cache.has_key?(url)
absolute_url = url
absolute_url = Discourse.base_url_no_prefix + absolute_url if absolute_url =~ %r{^/[^/]}
absolute_url = Discourse.base_url_no_prefix + absolute_url if absolute_url =~ %r{\A/[^/]}
return unless absolute_url

View File

@ -99,7 +99,7 @@ class DiscourseConnectBase
end
decoded_hash.each do |k, v|
if field = k[/^custom\.(.+)$/, 1]
if field = k[/\Acustom\.(.+)\z/, 1]
sso.custom_fields[field] = v
end
end

View File

@ -160,7 +160,7 @@ class DiscourseDiff
while i < text.size
if text[i] =~ /\w/
t << text[i]
elsif text[i] =~ /[ \t]/ && t.join =~ /^\w+$/
elsif text[i] =~ /[ \t]/ && t.join =~ /\A\w+\z/
begin
t << text[i]
i += 1

View File

@ -158,8 +158,8 @@ class DiscoursePluginRegistry
end
end
JS_REGEX = /\.js$|\.js\.erb$|\.js\.es6$/
HANDLEBARS_REGEX = /\.(hb[rs]|js\.handlebars)$/
JS_REGEX = /\.js$|\.js\.erb$|\.js\.es6\z/
HANDLEBARS_REGEX = /\.(hb[rs]|js\.handlebars)\z/
def self.register_asset(asset, opts = nil, plugin_directory_name = nil)
if asset =~ JS_REGEX
@ -172,7 +172,7 @@ class DiscoursePluginRegistry
else
self.javascripts << asset
end
elsif asset =~ /\.css$|\.scss$/
elsif asset =~ /\.css$|\.scss\z/
if opts == :mobile
self.mobile_stylesheets[plugin_directory_name] ||= Set.new
self.mobile_stylesheets[plugin_directory_name] << asset

View File

@ -276,7 +276,7 @@ class DiscourseRedis
def eval(redis, *args, **kwargs)
redis.evalsha @sha1, *args, **kwargs
rescue ::Redis::CommandError => e
if e.to_s =~ /^NOSCRIPT/
if e.to_s =~ /\ANOSCRIPT/
redis.eval @script, *args, **kwargs
else
raise

View File

@ -136,7 +136,7 @@ module Email
def message_id_clean(message_id)
if message_id.present? && is_message_id_rfc?(message_id)
message_id.gsub(/^<|>$/, "")
message_id.gsub(/\A<|>\z/, "")
else
message_id
end

View File

@ -381,7 +381,7 @@ module Email
@mail[:precedence].to_s[/list|junk|bulk|auto_reply/i] ||
@mail[:from].to_s[/(mailer[\-_]?daemon|post[\-_]?master|no[\-_]?reply)@/i] ||
@mail[:subject].to_s[
/^\s*(Auto:|Automatic reply|Autosvar|Automatisk svar|Automatisch antwoord|Abwesenheitsnotiz|Risposta Non al computer|Automatisch antwoord|Auto Response|Respuesta automática|Fuori sede|Out of Office|Frånvaro|Réponse automatique)/i
/\A\s*(Auto:|Automatic reply|Autosvar|Automatisk svar|Automatisch antwoord|Abwesenheitsnotiz|Risposta Non al computer|Automatisch antwoord|Auto Response|Respuesta automática|Fuori sede|Out of Office|Frånvaro|Réponse automatique)/i
] ||
@mail.header.to_s[
/auto[\-_]?(response|submitted|replied|reply|generated|respond)|holidayreply|machinegenerated/i
@ -393,7 +393,7 @@ module Email
when "X-Spam-Flag"
@mail[:x_spam_flag].to_s[/YES/i]
when "X-Spam-Status"
@mail[:x_spam_status].to_s[/^Yes, /i]
@mail[:x_spam_status].to_s[/\AYes, /i]
when "X-SES-Spam-Verdict"
@mail[:x_ses_spam_verdict].to_s[/FAIL/i]
else
@ -639,7 +639,7 @@ module Email
.uniq
@previous_replies_regex ||=
/^--[- ]\n\*(?:#{strings.map { |x| Regexp.escape(x) }.join("|")})\*\n/im
/\A--[- ]\n\*(?:#{strings.map { |x| Regexp.escape(x) }.join("|")})\*\n/im
end
def reply_above_line_regex
@ -747,12 +747,12 @@ module Email
if value[/<[^>]+>/]
from_address = value[/<([^>]+)>/, 1]
from_display_name = value[/^([^<]+)/, 1]
from_display_name = value[/\A([^<]+)/, 1]
end
if (from_address.blank? || !from_address["@"]) && value[/\[mailto:[^\]]+\]/]
from_address = value[/\[mailto:([^\]]+)\]/, 1]
from_display_name = value[/^([^\[]+)/, 1]
from_display_name = value[/\A([^\[]+)/, 1]
end
[from_address&.downcase, from_display_name&.strip]
@ -1016,7 +1016,7 @@ module Email
end
def has_been_forwarded?
subject[/^[[:blank:]]*(fwd?|tr)[[:blank:]]?:/i] && embedded_email_raw.present?
subject[/\A[[:blank:]]*(fwd?|tr)[[:blank:]]?:/i] && embedded_email_raw.present?
end
def embedded_email_raw

View File

@ -84,9 +84,9 @@ module Email
if img["src"]
# ensure all urls are absolute
img["src"] = "#{Discourse.base_url}#{img["src"]}" if img["src"][%r{^/[^/]}]
img["src"] = "#{Discourse.base_url}#{img["src"]}" if img["src"][%r{\A/[^/]}]
# ensure no schemaless urls
img["src"] = "#{uri.scheme}:#{img["src"]}" if img["src"][%r{^//}]
img["src"] = "#{uri.scheme}:#{img["src"]}" if img["src"][%r{\A//}]
end
end
@ -110,7 +110,7 @@ module Email
.css("a.attachment")
.each do |a|
# ensure all urls are absolute
a["href"] = "#{Discourse.base_url}#{a["href"]}" if a["href"] =~ %r{^/[^/]}
a["href"] = "#{Discourse.base_url}#{a["href"]}" if a["href"] =~ %r{\A/[^/]}
# ensure no schemaless urls
a["href"] = "#{uri.scheme}:#{a["href"]}" if a["href"] && a["href"].starts_with?("//")

View File

@ -4,7 +4,7 @@
class EmailCook
def self.raw_regexp
@raw_regexp ||=
%r{^\[plaintext\]$\n(.*)\n^\[/plaintext\]$(?:\s^\[attachments\]$\n(.*)\n^\[/attachments\]$)?(?:\s^\[elided\]$\n(.*)\n^\[/elided\]$)?}m
%r{\A\[plaintext\]$\n(.*)\n^\[/plaintext\]$(?:\s^\[attachments\]$\n(.*)\n^\[/attachments\]$)?(?:\s^\[elided\]$\n(.*)\n^\[/elided\]$)?}m
end
def initialize(raw)
@ -14,7 +14,7 @@ class EmailCook
def add_quote(result, buffer)
if buffer.present?
return if buffer =~ /\A(<br>)+\z$/
return if buffer =~ /\A(<br>)+\z\z/
result << "<blockquote>#{buffer}</blockquote>"
end
end
@ -22,7 +22,7 @@ class EmailCook
def link_string!(line, unescaped_line)
unescaped_line = unescaped_line.strip
line.gsub!(/\S+/) do |str|
if str.match?(%r{^(https?://)[\S]+$}i)
if str.match?(%r{\A(https?://)[\S]+\z}i)
begin
url = URI.parse(str).to_s
if unescaped_line == url
@ -48,11 +48,11 @@ class EmailCook
text.each_line do |line|
# replace indentation with non-breaking spaces
line.sub!(/^\s{2,}/) { |s| "\u00A0" * s.length }
line.sub!(/\A\s{2,}/) { |s| "\u00A0" * s.length }
if line =~ /^\s*>/
if line =~ /\A\s*>/
in_quote = true
line.sub!(/^[\s>]*/, "")
line.sub!(/\A[\s>]*/, "")
unescaped_line = line
line = CGI.escapeHTML(line)

View File

@ -52,7 +52,7 @@ class FileHelper
retain_on_max_file_size_exceeded: false
)
url = "https:" + url if url.start_with?("//")
raise Discourse::InvalidParameters.new(:url) unless url =~ %r{^https?://}
raise Discourse::InvalidParameters.new(:url) unless url =~ %r{\Ahttps?://}
tmp = nil
@ -175,26 +175,26 @@ class FileHelper
end
def self.supported_video_regexp
@@supported_video_regexp ||= /\.(#{supported_video.to_a.join("|")})$/i
@@supported_video_regexp ||= /\.(#{supported_video.to_a.join("|")})\z/i
end
def self.supported_audio_regexp
@@supported_audio_regexp ||= /\.(#{supported_audio.to_a.join("|")})$/i
@@supported_audio_regexp ||= /\.(#{supported_audio.to_a.join("|")})\z/i
end
def self.supported_images_regexp
@@supported_images_regexp ||= /\.(#{supported_images.to_a.join("|")})$/i
@@supported_images_regexp ||= /\.(#{supported_images.to_a.join("|")})\z/i
end
def self.inline_images_regexp
@@inline_images_regexp ||= /\.(#{inline_images.to_a.join("|")})$/i
@@inline_images_regexp ||= /\.(#{inline_images.to_a.join("|")})\z/i
end
def self.supported_media_regexp
@@supported_media_regexp ||=
begin
media = supported_images | supported_audio | supported_video
/\.(#{media.to_a.join("|")})$/i
/\.(#{media.to_a.join("|")})\z/i
end
end
@ -202,7 +202,7 @@ class FileHelper
@@supported_playable_media_regexp ||=
begin
media = supported_audio | supported_video
/\.(#{media.to_a.join("|")})$/i
/\.(#{media.to_a.join("|")})\z/i
end
end
end

View File

@ -113,7 +113,7 @@ module FileStore
end
)
url = SiteSetting.scheme + ":" + url if url =~ %r{^//}
url = SiteSetting.scheme + ":" + url if url =~ %r{\A//}
file =
FileHelper.download(
url,

View File

@ -128,7 +128,7 @@ module FileStore
count = 0
model.find_each do |upload|
# could be a remote image
next unless upload.url =~ %r{^/[^/]}
next unless upload.url =~ %r{\A/[^/]}
path = "#{public_dir}#{upload.url}"
bad = true

View File

@ -216,7 +216,7 @@ module FileStore
def path_for(upload)
url = upload&.url
FileStore::LocalStore.new.path_for(upload) if url && url[%r{^/[^/]}]
FileStore::LocalStore.new.path_for(upload) if url && url[%r{\A/[^/]}]
end
def url_for(upload, force_download: false)
@ -233,7 +233,7 @@ module FileStore
def cdn_url(url)
return url if SiteSetting.Upload.s3_cdn_url.blank?
schema = url[%r{^(https?:)?//}, 1]
schema = url[%r{\A(https?:)?//}, 1]
folder = s3_bucket_folder_path.nil? ? "" : "#{s3_bucket_folder_path}/"
url.sub(
File.join("#{schema}#{absolute_base_url}", folder),

View File

@ -39,7 +39,7 @@ module I18n
if @loaded_locales.empty?
# load all rb files
I18n.backend.load_translations(I18n.load_path.grep(/\.rb$/))
I18n.backend.load_translations(I18n.load_path.grep(/\.rb\z/))
# load plural rules from plugins
DiscoursePluginRegistry.locales.each do |plugin_locale, options|
@ -50,14 +50,14 @@ module I18n
end
# load it
I18n.backend.load_translations(I18n.load_path.grep(/\.#{Regexp.escape locale}\.yml$/))
I18n.backend.load_translations(I18n.load_path.grep(/\.#{Regexp.escape locale}\.yml\z/))
if Discourse.allow_dev_populate?
I18n.backend.load_translations(
I18n.load_path.grep(%r{.*faker.*/#{Regexp.escape locale}\.yml$}),
I18n.load_path.grep(%r{.*faker.*/#{Regexp.escape locale}\.yml\z}),
)
I18n.backend.load_translations(
I18n.load_path.grep(%r{.*faker.*/#{Regexp.escape locale}/.*\.yml$}),
I18n.load_path.grep(%r{.*faker.*/#{Regexp.escape locale}/.*\.yml\z}),
)
end

View File

@ -10,7 +10,7 @@ module GitUrl
end
if url.start_with?("https://github.com/") && !url.end_with?(".git")
url = url.gsub(%r{/$}, "")
url = url.gsub(%r{/\z}, "")
url += ".git"
end

View File

@ -12,7 +12,7 @@ module GlobalPath
def upload_cdn_path(p)
p = Discourse.store.cdn_url(p) if SiteSetting.Upload.s3_cdn_url.present?
(p =~ /^http/ || p =~ %r{^//}) ? p : cdn_path(p)
(p =~ /\Ahttp/ || p =~ %r{\A//}) ? p : cdn_path(p)
end
def cdn_relative_path(path)

View File

@ -3,7 +3,7 @@
# Support for ensure_{blah}! methods.
module EnsureMagic
def method_missing(method, *args, &block)
if method.to_s =~ /^ensure_(.*)\!$/
if method.to_s =~ /\Aensure_(.*)\!\z/
can_method = :"#{Regexp.last_match[1]}?"
if respond_to?(can_method)

View File

@ -249,8 +249,8 @@ class HtmlPrettify < String
# Special case if the very first character is a quote followed by
# punctuation at a non-word-break. Close the quotes by brute
# force:
str.gsub!(/^'(?=#{punct_class}\B)/, entity(:single_right_quote))
str.gsub!(/^"(?=#{punct_class}\B)/, entity(:double_right_quote))
str.gsub!(/\A'(?=#{punct_class}\B)/, entity(:single_right_quote))
str.gsub!(/\A"(?=#{punct_class}\B)/, entity(:double_right_quote))
# Special case for double sets of quotes, e.g.:
# <p>He said, "'Quoted' words in a larger quote."</p>

View File

@ -49,7 +49,7 @@ class LocaleFileChecker
end
def reference_file(path)
path = path.gsub(/\.\w{2,}\.yml$/, ".#{REFERENCE_LOCALE}.yml")
path = path.gsub(/\.\w{2,}\.yml\z/, ".#{REFERENCE_LOCALE}.yml")
path if File.exist?(path)
end

View File

@ -25,8 +25,8 @@ module Middleware
def self.compile_key_builder
method = +"def self.__compiled_key_builder(h)\n \""
cache_key_segments.each do |k, v|
raise "Invalid key name" unless k =~ /^[a-z]+$/
raise "Invalid method name" unless v =~ /^key_[a-z_\?]+$/
raise "Invalid key name" unless k =~ /\A[a-z]+\z/
raise "Invalid method name" unless v =~ /\Akey_[a-z_\?]+\z/
method << "|#{k}=#\{h.#{v}}"
end
method << "\"\nend"

View File

@ -11,7 +11,7 @@ module Middleware
end
def call(env)
if (env["REQUEST_PATH"] =~ %r{^/uploads/default/avatars})
if (env["REQUEST_PATH"] =~ %r{\A/uploads/default/avatars})
path = "#{Rails.root}/public#{env["REQUEST_PATH"]}"
unless File.exist?(path)
default_image = "#{Rails.root}/public/images/d-logo-sketch-small.png"

View File

@ -116,7 +116,7 @@ class Migration::SafeMigrate
end
def self.protect!(sql)
if sql =~ /^\s*(?:drop\s+table|alter\s+table.*rename\s+to)\s+/i
if sql =~ /\A\s*(?:drop\s+table|alter\s+table.*rename\s+to)\s+/i
$stdout.puts("", <<~TEXT)
WARNING
-------------------------------------------------------------------------------------
@ -129,7 +129,7 @@ class Migration::SafeMigrate
in use by live applications.
TEXT
raise Discourse::InvalidMigration, "Attempt was made to drop a table"
elsif sql =~ /^\s*alter\s+table.*(?:rename|drop)\s+/i
elsif sql =~ /\A\s*alter\s+table.*(?:rename|drop)\s+/i
$stdout.puts("", <<~TEXT)
WARNING
-------------------------------------------------------------------------------------

View File

@ -7,7 +7,7 @@ module Onebox
end
def self.engines
constants.select { |constant| constant.to_s =~ /Onebox$/ }.sort.map(&method(:const_get))
constants.select { |constant| constant.to_s =~ /Onebox\z/ }.sort.map(&method(:const_get))
end
def self.all_iframe_origins

View File

@ -15,7 +15,7 @@ module Onebox
@record = Onebox::Helpers.symbolize_keys(record)
# Fix any relative paths
if @record[:image] && @record[:image] =~ %r{^/[^/]}
if @record[:image] && @record[:image] =~ %r{\A/[^/]}
@record[:image] = "#{uri.scheme}://#{uri.host}/#{@record[:image]}"
end
@ -40,7 +40,7 @@ module Onebox
link: record[:link],
title: record[:title],
favicon: record[:favicon],
domain: record[:domain] || uri.host.to_s.sub(/^www\./, ""),
domain: record[:domain] || uri.host.to_s.sub(/\Awww\./, ""),
article_published_time: record[:article_published_time],
article_published_time_title: record[:article_published_time_title],
metadata_1_label: record[:metadata_1_label],

View File

@ -119,7 +119,7 @@ module Onebox
a_lines = str.lines
a_lines.each do |l|
l = l.chomp("\n") # remove new line
m = l.match(/^[ ]*/) # find leading spaces 0 or more
m = l.match(/\A[ ]*/) # find leading spaces 0 or more
unless m.nil? || l.size == m[0].size || l.size == 0 # no match | only spaces in line | empty line
m_str_length = m[0].size
if m_str_length <= 1 # minimum space is 1 or nothing we can break we found our minimum
@ -166,7 +166,7 @@ module Onebox
@file = m[:file]
@lang = Onebox::FileTypeFinder.from_file_name(m[:file])
if @lang == "stl" && link.match?(%r{^https?://(www\.)?github\.com.*/blob/})
if @lang == "stl" && link.match?(%r{\Ahttps?://(www\.)?github\.com.*/blob/})
@model_file = @lang.dup
@raw = "https://render.githubusercontent.com/view/solid?url=" + self.raw_template(m)
else

View File

@ -32,8 +32,8 @@ module Onebox
doc
.css("meta")
.each do |m|
if (m["property"] && m["property"][/^(?:og|article|product):(.+)$/i]) ||
(m["name"] && m["name"][/^(?:og|article|product):(.+)$/i])
if (m["property"] && m["property"][/\A(?:og|article|product):(.+)\z/i]) ||
(m["name"] && m["name"][/\A(?:og|article|product):(.+)\z/i])
value = (m["content"] || m["value"]).to_s
next if Onebox::Helpers.blank?(value)
key = $1.tr("-:", "_").to_sym

View File

@ -58,7 +58,7 @@ module Onebox
next unless env[:node_name] == "a"
a_tag = env[:node]
a_tag["href"] ||= "#"
if a_tag["href"] =~ %r{^(?:[a-z]+:)?//}
if a_tag["href"] =~ %r{\A(?:[a-z]+:)?//}
a_tag["rel"] = "nofollow ugc noopener"
else
a_tag.remove_attribute("target")

View File

@ -6,8 +6,8 @@ Dir["#{Rails.root}/lib/onebox/engine/*_onebox.rb"].sort.each { |f| require f }
module Oneboxer
ONEBOX_CSS_CLASS = "onebox"
AUDIO_REGEX = /^\.(mp3|og[ga]|opus|wav|m4[abpr]|aac|flac)$/i
VIDEO_REGEX = /^\.(mov|mp4|webm|m4v|3gp|ogv|avi|mpeg|ogv)$/i
AUDIO_REGEX = /\A\.(mp3|og[ga]|opus|wav|m4[abpr]|aac|flac)\z/i
VIDEO_REGEX = /\A\.(mov|mp4|webm|m4v|3gp|ogv|avi|mpeg|ogv)\z/i
# keep reloaders happy
unless defined?(Oneboxer::Result)

View File

@ -100,7 +100,7 @@ class PlainTextToMarkdown
# @param line [Line]
def remove_quote_level_indicators!(line)
match_data = line.text.match(/^(?<indicators>>+)\s?(?<text>.*)/)
match_data = line.text.match(/\A(?<indicators>>+)\s?(?<text>.*)/)
if match_data
line.text = match_data[:text]
@ -128,7 +128,7 @@ class PlainTextToMarkdown
def classify_line_as_code!(line, previous_line)
line.code_block = previous_line.code_block unless previous_line.nil? ||
previous_line.valid_code_block?
return unless line.text =~ /^\s{0,3}```/
return unless line.text =~ /\A\s{0,3}```/
if line.code_block.present?
line.code_block.end_line = line
@ -173,7 +173,7 @@ class PlainTextToMarkdown
end
def indent_with_non_breaking_spaces(text)
text.sub(/^\s+/) do |s|
text.sub(/\A\s+/) do |s|
# replace tabs with 2 spaces
s.gsub!("\t", " ")

View File

@ -675,17 +675,17 @@ class Plugin::Instance
DiscoursePluginRegistry.register_glob(admin_path, "hbr", admin: true)
DiscourseJsProcessor.plugin_transpile_paths << root_path.sub(Rails.root.to_s, "").sub(
%r{^/*},
%r{\A/*},
"",
)
DiscourseJsProcessor.plugin_transpile_paths << admin_path.sub(Rails.root.to_s, "").sub(
%r{^/*},
%r{\A/*},
"",
)
test_path = "#{root_dir_name}/test/javascripts"
DiscourseJsProcessor.plugin_transpile_paths << test_path.sub(Rails.root.to_s, "").sub(
%r{^/*},
%r{\A/*},
"",
)
end
@ -1299,7 +1299,7 @@ class Plugin::Instance
private
def validate_directory_column_name(column_name)
match = /^[_a-z]+$/.match(column_name)
match = /\A[_a-z]+\z/.match(column_name)
unless match
raise "Invalid directory column name '#{column_name}'. Can only contain a-z and underscores"
end

View File

@ -48,7 +48,7 @@ module PrettyText
filename = find_file(root_path, part_name)
if filename
source = File.read("#{root_path}#{filename}")
source = ERB.new(source).result(binding) if filename =~ /\.erb$/
source = ERB.new(source).result(binding) if filename =~ /\.erb\z/
transpiler = DiscourseJsProcessor::Transpiler.new
transpiled = transpiler.perform(source, "#{Rails.root}/app/assets/javascripts/", part_name)
@ -64,7 +64,7 @@ module PrettyText
def self.ctx_load_directory(ctx, path)
root_path = "#{Rails.root}/app/assets/javascripts/"
Dir["#{root_path}#{path}/**/*"].sort.each do |f|
apply_es6_file(ctx, root_path, f.sub(root_path, "").sub(/\.js(.es6)?$/, ""))
apply_es6_file(ctx, root_path, f.sub(root_path, "").sub(/\.js(.es6)?\z/, ""))
end
end
@ -116,9 +116,9 @@ module PrettyText
to_load << a if File.file?(a) && a =~ /discourse-markdown/
end
to_load.uniq.each do |f|
if f =~ %r{^.+assets/javascripts/}
if f =~ %r{\A.+assets/javascripts/}
root = Regexp.last_match[0]
apply_es6_file(ctx, root, f.sub(root, "").sub(/\.js(\.es6)?$/, ""))
apply_es6_file(ctx, root, f.sub(root, "").sub(/\.js(\.es6)?\z/, ""))
end
end

View File

@ -102,7 +102,7 @@ module PrettyText
# TODO (martin) Remove this when everything is using hashtag_lookup
# after enable_experimental_hashtag_autocomplete is default.
def category_tag_hashtag_lookup(text)
is_tag = text =~ /#{TAG_HASHTAG_POSTFIX}$/
is_tag = text =~ /#{TAG_HASHTAG_POSTFIX}\z/
if !is_tag && category = Category.query_from_hashtag_slug(text)
[category.url, text]

View File

@ -14,7 +14,7 @@ module RequireDependencyBackwardCompatibility
def require_dependency(filename)
name = filename.to_s
return if name == "jobs/base"
return super(name.sub(%r{^lib/}, "")) if name.start_with?("lib/")
return super(name.sub(%r{\Alib/}, "")) if name.start_with?("lib/")
super
end

View File

@ -32,7 +32,7 @@ module RetrieveTitle
# A horrible hack - YouTube uses `document.title` to populate the title
# for some reason. For any other site than YouTube this wouldn't be worth it.
if title == "YouTube" && html =~ /document\.title *= *"(.*)";/
title = Regexp.last_match[1].sub(/ - YouTube$/, "")
title = Regexp.last_match[1].sub(/ - YouTube\z/, "")
end
if !title && node = doc.at('meta[property="og:title"]')
@ -53,11 +53,12 @@ module RetrieveTitle
def self.max_chunk_size(uri)
# Exception for sites that leave the title until very late.
if uri.host =~ /(^|\.)amazon\.(com|ca|co\.uk|es|fr|de|it|com\.au|com\.br|cn|in|co\.jp|com\.mx)$/
if uri.host =~
/(^|\.)amazon\.(com|ca|co\.uk|es|fr|de|it|com\.au|com\.br|cn|in|co\.jp|com\.mx)\z/
return 500
end
return 300 if uri.host =~ /(^|\.)youtube\.com$/ || uri.host =~ /(^|\.)youtu\.be$/
return 50 if uri.host =~ /(^|\.)github\.com$/
return 300 if uri.host =~ /(^|\.)youtube\.com\z/ || uri.host =~ /(^|\.)youtu\.be\z/
return 50 if uri.host =~ /(^|\.)github\.com\z/
# default is 20k
20

View File

@ -46,7 +46,7 @@ class RouteMatcher
return true if actions.nil? # actions are unrestricted
# message_bus is not a rails route, special handling
return true if actions.include?("message_bus") && request.fullpath =~ %r{^/message-bus/.*/poll}
return true if actions.include?("message_bus") && request.fullpath =~ %r{\A/message-bus/.*/poll}
path_params = path_params_from_request(request)
actions.include? "#{path_params[:controller]}##{path_params[:action]}"

View File

@ -334,7 +334,7 @@ class S3Inventory
objects = []
hive_path = File.join(inventory_path, bucket_name, inventory_id, "hive")
@s3_helper.list(hive_path).each { |obj| objects << obj if obj.key.match?(/symlink\.txt$/i) }
@s3_helper.list(hive_path).each { |obj| objects << obj if obj.key.match?(/symlink\.txt\z/i) }
objects
rescue Aws::Errors::ServiceError => e

View File

@ -128,7 +128,7 @@ class Search
end
data.gsub!(/\S+/) do |str|
if str =~ %r{^["]?((https?://)[\S]+)["]?$}
if str =~ %r{\A["]?((https?://)[\S]+)["]?\z}
begin
uri = URI.parse(Regexp.last_match[1])
uri.query = nil
@ -145,9 +145,9 @@ class Search
end
def self.word_to_date(str)
return Time.zone.now.beginning_of_day.days_ago(str.to_i) if str =~ /^[0-9]{1,3}$/
return Time.zone.now.beginning_of_day.days_ago(str.to_i) if str =~ /\A[0-9]{1,3}\z/
if str =~ /^([12][0-9]{3})(-([0-1]?[0-9]))?(-([0-3]?[0-9]))?$/
if str =~ /\A([12][0-9]{3})(-([0-1]?[0-9]))?(-([0-3]?[0-9]))?\z/
year = $1.to_i
month = $2 ? $3.to_i : 1
day = $4 ? $5.to_i : 1
@ -307,7 +307,7 @@ class Search
# If the term is a number or url to a topic, just include that topic
if @opts[:search_for_id] && %w[topic private_messages all_topics].include?(@results.type_filter)
if @term =~ /^\d+$/
if @term =~ /\A\d+\z/
single_topic(@term.to_i)
else
if route = Discourse.route_for(@term)
@ -355,7 +355,7 @@ class Search
Array.wrap(@custom_topic_eager_loads)
end
advanced_filter(/^in:personal-direct$/i) do |posts|
advanced_filter(/\Ain:personal-direct\z/i) do |posts|
if @guardian.user
posts.joins("LEFT JOIN topic_allowed_groups tg ON posts.topic_id = tg.topic_id").where(
<<~SQL,
@ -376,60 +376,60 @@ class Search
end
end
advanced_filter(/^in:all-pms$/i) { |posts| posts.private_posts if @guardian.is_admin? }
advanced_filter(/\Ain:all-pms\z/i) { |posts| posts.private_posts if @guardian.is_admin? }
advanced_filter(/^in:tagged$/i) do |posts|
advanced_filter(/\Ain:tagged\z/i) do |posts|
posts.where("EXISTS (SELECT 1 FROM topic_tags WHERE topic_tags.topic_id = posts.topic_id)")
end
advanced_filter(/^in:untagged$/i) do |posts|
advanced_filter(/\Ain:untagged\z/i) do |posts|
posts.joins(
"LEFT JOIN topic_tags ON
topic_tags.topic_id = posts.topic_id",
).where("topic_tags.id IS NULL")
end
advanced_filter(/^status:open$/i) do |posts|
advanced_filter(/\Astatus:open\z/i) do |posts|
posts.where("NOT topics.closed AND NOT topics.archived")
end
advanced_filter(/^status:closed$/i) { |posts| posts.where("topics.closed") }
advanced_filter(/\Astatus:closed\z/i) { |posts| posts.where("topics.closed") }
advanced_filter(/^status:public$/i) do |posts|
advanced_filter(/\Astatus:public\z/i) do |posts|
category_ids = Category.where(read_restricted: false).pluck(:id)
posts.where("topics.category_id in (?)", category_ids)
end
advanced_filter(/^status:archived$/i) { |posts| posts.where("topics.archived") }
advanced_filter(/\Astatus:archived\z/i) { |posts| posts.where("topics.archived") }
advanced_filter(/^status:noreplies$/i) { |posts| posts.where("topics.posts_count = 1") }
advanced_filter(/\Astatus:noreplies\z/i) { |posts| posts.where("topics.posts_count = 1") }
advanced_filter(/^status:single_user$/i) { |posts| posts.where("topics.participant_count = 1") }
advanced_filter(/\Astatus:single_user\z/i) { |posts| posts.where("topics.participant_count = 1") }
advanced_filter(/^posts_count:(\d+)$/i) do |posts, match|
advanced_filter(/\Aposts_count:(\d+)\z/i) do |posts, match|
posts.where("topics.posts_count = ?", match.to_i)
end
advanced_filter(/^min_post_count:(\d+)$/i) do |posts, match|
advanced_filter(/\Amin_post_count:(\d+)\z/i) do |posts, match|
posts.where("topics.posts_count >= ?", match.to_i)
end
advanced_filter(/^min_posts:(\d+)$/i) do |posts, match|
advanced_filter(/\Amin_posts:(\d+)\z/i) do |posts, match|
posts.where("topics.posts_count >= ?", match.to_i)
end
advanced_filter(/^max_posts:(\d+)$/i) do |posts, match|
advanced_filter(/\Amax_posts:(\d+)\z/i) do |posts, match|
posts.where("topics.posts_count <= ?", match.to_i)
end
advanced_filter(/^in:first|^f$/i) { |posts| posts.where("posts.post_number = 1") }
advanced_filter(/\Ain:first|^f\z/i) { |posts| posts.where("posts.post_number = 1") }
advanced_filter(/^in:pinned$/i) { |posts| posts.where("topics.pinned_at IS NOT NULL") }
advanced_filter(/\Ain:pinned\z/i) { |posts| posts.where("topics.pinned_at IS NOT NULL") }
advanced_filter(/^in:wiki$/i) { |posts, match| posts.where(wiki: true) }
advanced_filter(/\Ain:wiki\z/i) { |posts, match| posts.where(wiki: true) }
advanced_filter(/^badge:(.*)$/i) do |posts, match|
advanced_filter(/\Abadge:(.*)\z/i) do |posts, match|
badge_id = Badge.where("name ilike ? OR id = ?", match, match.to_i).pluck_first(:id)
if badge_id
posts.where(
@ -454,7 +454,7 @@ class Search
)
end
advanced_filter(/^in:(likes)$/i) do |posts, match|
advanced_filter(/\Ain:(likes)\z/i) do |posts, match|
post_action_type_filter(posts, PostActionType.types[:like]) if @guardian.user
end
@ -462,7 +462,7 @@ class Search
# this at some point, as it only acts on posts at the moment. On the other
# hand, this may not be necessary, as the user bookmark list has advanced
# search based on a RegisteredBookmarkable's #search_query method.
advanced_filter(/^in:(bookmarks)$/i) do |posts, match|
advanced_filter(/\Ain:(bookmarks)\z/i) do |posts, match|
posts.where(<<~SQL, @guardian.user.id) if @guardian.user
posts.id IN (
SELECT bookmarkable_id FROM bookmarks
@ -471,20 +471,20 @@ class Search
SQL
end
advanced_filter(/^in:posted$/i) do |posts|
advanced_filter(/\Ain:posted\z/i) do |posts|
posts.where("posts.user_id = ?", @guardian.user.id) if @guardian.user
end
advanced_filter(/^in:(created|mine)$/i) do |posts|
advanced_filter(/\Ain:(created|mine)\z/i) do |posts|
posts.where(user_id: @guardian.user.id, post_number: 1) if @guardian.user
end
advanced_filter(/^created:@(.*)$/i) do |posts, match|
advanced_filter(/\Acreated:@(.*)\z/i) do |posts, match|
user_id = User.where(username: match.downcase).pluck_first(:id)
posts.where(user_id: user_id, post_number: 1)
end
advanced_filter(/^in:(watching|tracking)$/i) do |posts, match|
advanced_filter(/\Ain:(watching|tracking)\z/i) do |posts, match|
if @guardian.user
level = TopicUser.notification_levels[match.downcase.to_sym]
posts.where(
@ -499,7 +499,7 @@ class Search
end
end
advanced_filter(/^in:seen$/i) do |posts|
advanced_filter(/\Ain:seen\z/i) do |posts|
if @guardian.user
posts.joins(
"INNER JOIN post_timings ON
@ -511,7 +511,7 @@ class Search
end
end
advanced_filter(/^in:unseen$/i) do |posts|
advanced_filter(/\Ain:unseen\z/i) do |posts|
if @guardian.user
posts.joins(
"LEFT JOIN post_timings ON
@ -523,9 +523,9 @@ class Search
end
end
advanced_filter(/^with:images$/i) { |posts| posts.where("posts.image_upload_id IS NOT NULL") }
advanced_filter(/\Awith:images\z/i) { |posts| posts.where("posts.image_upload_id IS NOT NULL") }
advanced_filter(/^category:(.+)$/i) do |posts, match|
advanced_filter(/\Acategory:(.+)\z/i) do |posts, match|
exact = false
if match[0] == "="
@ -544,7 +544,7 @@ class Search
end
end
advanced_filter(/^\#([\p{L}\p{M}0-9\-:=]+)$/i) do |posts, match|
advanced_filter(/\A\#([\p{L}\p{M}0-9\-:=]+)\z/i) do |posts, match|
category_slug, subcategory_slug = match.to_s.split(":")
next unless category_slug
@ -614,7 +614,7 @@ class Search
end
end
advanced_filter(/^group:(.+)$/i) do |posts, match|
advanced_filter(/\Agroup:(.+)\z/i) do |posts, match|
group_query =
Group
.visible_groups(@guardian.user)
@ -637,7 +637,7 @@ class Search
end
end
advanced_filter(/^group_messages:(.+)$/i) do |posts, match|
advanced_filter(/\Agroup_messages:(.+)\z/i) do |posts, match|
group_id =
Group
.visible_groups(@guardian.user)
@ -656,7 +656,7 @@ class Search
end
end
advanced_filter(/^user:(.+)$/i) do |posts, match|
advanced_filter(/\Auser:(.+)\z/i) do |posts, match|
user_id =
User
.where(staged: false)
@ -669,7 +669,7 @@ class Search
end
end
advanced_filter(/^\@(\S+)$/i) do |posts, match|
advanced_filter(/\A\@(\S+)\z/i) do |posts, match|
username = User.normalize_username(match)
user_id = User.not_staged.where(username_lower: username).pluck_first(:id)
@ -683,7 +683,7 @@ class Search
end
end
advanced_filter(/^before:(.*)$/i) do |posts, match|
advanced_filter(/\Abefore:(.*)\z/i) do |posts, match|
if date = Search.word_to_date(match)
posts.where("posts.created_at < ?", date)
else
@ -691,7 +691,7 @@ class Search
end
end
advanced_filter(/^after:(.*)$/i) do |posts, match|
advanced_filter(/\Aafter:(.*)\z/i) do |posts, match|
if date = Search.word_to_date(match)
posts.where("posts.created_at > ?", date)
else
@ -699,15 +699,15 @@ class Search
end
end
advanced_filter(/^tags?:([\p{L}\p{M}0-9,\-_+]+)$/i) do |posts, match|
advanced_filter(/\Atags?:([\p{L}\p{M}0-9,\-_+]+)\z/i) do |posts, match|
search_tags(posts, match, positive: true)
end
advanced_filter(/^\-tags?:([\p{L}\p{M}0-9,\-_+]+)$/i) do |posts, match|
advanced_filter(/\A\-tags?:([\p{L}\p{M}0-9,\-_+]+)\z/i) do |posts, match|
search_tags(posts, match, positive: false)
end
advanced_filter(/^filetypes?:([a-zA-Z0-9,\-_]+)$/i) do |posts, match|
advanced_filter(/\Afiletypes?:([a-zA-Z0-9,\-_]+)\z/i) do |posts, match|
file_extensions = match.split(",").map(&:downcase)
posts.where(
"posts.id IN (
@ -726,11 +726,11 @@ class Search
)
end
advanced_filter(/^min_views:(\d+)$/i) do |posts, match|
advanced_filter(/\Amin_views:(\d+)\z/i) do |posts, match|
posts.where("topics.views >= ?", match.to_i)
end
advanced_filter(/^max_views:(\d+)$/i) do |posts, match|
advanced_filter(/\Amax_views:(\d+)\z/i) do |posts, match|
posts.where("topics.views <= ?", match.to_i)
end
@ -789,38 +789,38 @@ class Search
if word == "l"
@order = :latest
nil
elsif word =~ /^order:\w+$/i
elsif word =~ /\Aorder:\w+\z/i
@order = word.downcase.gsub("order:", "").to_sym
nil
elsif word =~ /^in:title$/i || word == "t"
elsif word =~ /\Ain:title\z/i || word == "t"
@in_title = true
nil
elsif word =~ /^topic:(\d+)$/i
elsif word =~ /\Atopic:(\d+)\z/i
topic_id = $1.to_i
if topic_id > 1
topic = Topic.find_by(id: topic_id)
@search_context = topic if @guardian.can_see?(topic)
end
nil
elsif word =~ /^in:all$/i
elsif word =~ /\Ain:all\z/i
@search_all_topics = true
nil
elsif word =~ /^in:personal$/i
elsif word =~ /\Ain:personal\z/i
@search_pms = true
nil
elsif word =~ /^in:messages$/i
elsif word =~ /\Ain:messages\z/i
@search_pms = true
nil
elsif word =~ /^in:personal-direct$/i
elsif word =~ /\Ain:personal-direct\z/i
@search_pms = true
nil
elsif word =~ /^in:all-pms$/i
elsif word =~ /\Ain:all-pms\z/i
@search_all_pms = true
nil
elsif word =~ /^group_messages:(.+)$/i
elsif word =~ /\Agroup_messages:(.+)\z/i
@search_pms = true
nil
elsif word =~ /^personal_messages:(.+)$/i
elsif word =~ /\Apersonal_messages:(.+)\z/i
if user = User.find_by_username($1)
@search_pms = true
@search_context = user

View File

@ -98,7 +98,7 @@ class ShrinkUploadedImage
elsif !post.topic || post.topic.trashed?
log "A deleted topic"
elsif post.cooked.include?(original_upload.sha1)
if post.raw.include?("#{Discourse.base_url.sub(%r{^https?://}i, "")}/t/")
if post.raw.include?("#{Discourse.base_url.sub(%r{\Ahttps?://}i, "")}/t/")
log "Updating a topic onebox"
else
log "Updating an external onebox"

View File

@ -243,7 +243,7 @@ module SiteSettings::Validations
def validate_cors_origins(new_val)
return if new_val.blank?
return unless new_val.split("|").any?(%r{/$})
return unless new_val.split("|").any?(%r{/\z})
validate_error :cors_origins_should_not_have_trailing_slash
end

View File

@ -11,7 +11,7 @@ class Stylesheet::Manager
CACHE_PATH ||= "tmp/stylesheet-cache"
MANIFEST_DIR ||= "#{Rails.root}/tmp/cache/assets/#{Rails.env}"
THEME_REGEX ||= /_theme$/
THEME_REGEX ||= /_theme\z/
COLOR_SCHEME_STYLESHEET ||= "color_definitions"
@@lock = Mutex.new

View File

@ -35,7 +35,7 @@ class Stylesheet::Manager::Builder
end
end
rtl = @target.to_s =~ /_rtl$/
rtl = @target.to_s =~ /_rtl\z/
css, source_map =
with_load_paths do |load_paths|
Stylesheet::Compiler.compile_asset(

View File

@ -21,7 +21,7 @@ module Stylesheet
@default_paths = ["app/assets/stylesheets"]
Discourse.plugins.each do |plugin|
if plugin.path.to_s.include?(Rails.root.to_s)
@default_paths << File.dirname(plugin.path).sub(Rails.root.to_s, "").sub(%r{^/}, "")
@default_paths << File.dirname(plugin.path).sub(Rails.root.to_s, "").sub(%r{\A/}, "")
else
# if plugin doesnt seem to be in our app, consider it as outside of the app
# and ignore it
@ -41,7 +41,7 @@ module Stylesheet
end
end
listener_opts = { ignore: /xxxx/, only: /\.(css|scss)$/ }
listener_opts = { ignore: /xxxx/, only: /\.(css|scss)\z/ }
listener_opts[:force_polling] = true if ENV["FORCE_POLLING"]
Thread.new do

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