From 38cd3d64514655c508eba980b7abc216ef2a1c0b Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sat, 2 May 2015 22:41:37 -0400 Subject: Add Commit#== Prior, comparison would use the Ruby object's ID, which got out of sync after a Spring fork and would result in erroneous test failures. Now we just check that the compared object is a Commit and then compare their underlying raw commit objects. --- app/models/commit.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/models/commit.rb b/app/models/commit.rb index be5a118bfec..5dea7dda513 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -56,6 +56,10 @@ class Commit @raw.id end + def ==(other) + (self.class === other) && (raw == other.raw) + end + def diff_line_count @diff_line_count ||= Commit::diff_line_count(self.diffs) @diff_line_count -- cgit v1.2.1 From b06dc74d611192744d34acda944d7ed9e554342a Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sat, 9 May 2015 19:02:59 -0400 Subject: Add Referable concern --- app/models/concerns/referable.rb | 52 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 app/models/concerns/referable.rb diff --git a/app/models/concerns/referable.rb b/app/models/concerns/referable.rb new file mode 100644 index 00000000000..b41df301c3f --- /dev/null +++ b/app/models/concerns/referable.rb @@ -0,0 +1,52 @@ +# == Referable concern +# +# Contains functionality related to making a model referable in Markdown, such +# as "#1", "!2", "~3", etc. +module Referable + extend ActiveSupport::Concern + + # Returns the String necessary to reference this object in Markdown + # + # from_project - Refering Project object + # + # This should be overridden by the including class. + # + # Examples: + # + # Issue.first.to_reference # => "#1" + # Issue.last.to_reference(other_project) # => "cross-project#1" + # + # Returns a String + def to_reference(_from_project = nil) + '' + end + + module ClassMethods + # The character that prefixes the actual reference identifier + # + # This should be overridden by the including class. + # + # Examples: + # + # Issue.reference_prefix # => '#' + # MergeRequest.reference_prefix # => '!' + # + # Returns a String + def reference_prefix + '' + end + end + + private + + # Check if a reference is being done cross-project + # + # from_project - Refering Project object + def cross_project_reference?(from_project) + if Project === self + self != from_project + else + from_project && project && project != from_project + end + end +end -- cgit v1.2.1 From c0faf91ff23815404a95cf4510b43dcf5e331c4f Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sat, 2 May 2015 23:11:21 -0400 Subject: Add `to_reference` for models that support references Now there is a single source of information for which attribute a model uses to be referenced, and its special character. --- app/models/commit.rb | 16 +++++++++-- app/models/commit_range.rb | 9 ++++++ app/models/external_issue.rb | 6 ++++ app/models/group.rb | 10 +++++++ app/models/issue.rb | 21 ++++++++++++-- app/models/label.rb | 27 ++++++++++++++++++ app/models/merge_request.rb | 21 ++++++++++++-- app/models/project.rb | 9 ++++-- app/models/snippet.rb | 19 +++++++++++-- app/models/user.rb | 16 +++++++++-- spec/models/commit_range_spec.rb | 23 +++++++++++++++ spec/models/commit_spec.rb | 24 ++++++++++++++-- spec/models/external_issue_spec.rb | 24 ++++++++++++++++ spec/models/group_spec.rb | 26 +++++++++++++---- spec/models/issue_spec.rb | 21 ++++++++++++-- spec/models/label_spec.rb | 58 +++++++++++++++++++++++++++----------- spec/models/merge_request_spec.rb | 32 +++++++++++++++++++-- spec/models/project_spec.rb | 21 ++++++++++++-- spec/models/snippet_spec.rb | 26 ++++++++++++++++- spec/models/user_spec.rb | 20 ++++++++++++- 20 files changed, 377 insertions(+), 52 deletions(-) create mode 100644 spec/models/external_issue_spec.rb diff --git a/app/models/commit.rb b/app/models/commit.rb index 5dea7dda513..3cc8d11a4aa 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -1,9 +1,11 @@ class Commit - include ActiveModel::Conversion - include StaticModel extend ActiveModel::Naming + + include ActiveModel::Conversion include Mentionable include Participable + include Referable + include StaticModel attr_mentionable :safe_message participant :author, :committer, :notes, :mentioned_users @@ -60,6 +62,14 @@ class Commit (self.class === other) && (raw == other.raw) end + def to_reference(from_project = nil) + if cross_project_reference?(from_project) + "#{project.to_reference}@#{id}" + else + id + end + end + def diff_line_count @diff_line_count ||= Commit::diff_line_count(self.diffs) @diff_line_count @@ -132,7 +142,7 @@ class Commit # Mentionable override. def gfm_reference - "commit #{id}" + "commit #{to_reference}" end def author diff --git a/app/models/commit_range.rb b/app/models/commit_range.rb index e6456198264..b98f939a115 100644 --- a/app/models/commit_range.rb +++ b/app/models/commit_range.rb @@ -19,6 +19,7 @@ # class CommitRange include ActiveModel::Conversion + include Referable attr_reader :sha_from, :notation, :sha_to @@ -59,6 +60,14 @@ class CommitRange "#{sha_from[0..7]}#{notation}#{sha_to[0..7]}" end + def to_reference(from_project = nil) + if cross_project_reference?(from_project) + "#{project.to_reference}@#{to_s}" + else + to_s + end + end + # Returns a String for use in a link's title attribute def reference_title "Commits #{suffixed_sha_from} through #{sha_to}" diff --git a/app/models/external_issue.rb b/app/models/external_issue.rb index 85fdb12bfdc..6fda4a2ab77 100644 --- a/app/models/external_issue.rb +++ b/app/models/external_issue.rb @@ -1,4 +1,6 @@ class ExternalIssue + include Referable + def initialize(issue_identifier, project) @issue_identifier, @project = issue_identifier, project end @@ -7,6 +9,10 @@ class ExternalIssue @issue_identifier.to_s end + def to_reference(_from_project = nil) + id + end + def id @issue_identifier.to_s end diff --git a/app/models/group.rb b/app/models/group.rb index 687458adac4..33d72e0d9ee 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -17,6 +17,8 @@ require 'carrierwave/orm/activerecord' require 'file_size_validator' class Group < Namespace + include Referable + has_many :group_members, dependent: :destroy, as: :source, class_name: 'GroupMember' has_many :users, through: :group_members @@ -36,6 +38,14 @@ class Group < Namespace def sort(method) order_by(method) end + + def reference_prefix + '@' + end + end + + def to_reference(_from_project = nil) + "#{self.class.reference_prefix}#{name}" end def human_name diff --git a/app/models/issue.rb b/app/models/issue.rb index 6e102051387..ff13cbca845 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -21,10 +21,11 @@ require 'carrierwave/orm/activerecord' require 'file_size_validator' class Issue < ActiveRecord::Base - include Issuable include InternalId - include Taskable + include Issuable + include Referable include Sortable + include Taskable ActsAsTaggableOn.strict_case_match = true @@ -49,14 +50,28 @@ class Issue < ActiveRecord::Base state :closed end + def self.reference_prefix + '#' + end + def hook_attrs attributes end + def to_reference(from_project = nil) + reference = "#{self.class.reference_prefix}#{iid}" + + if cross_project_reference?(from_project) + reference = project.to_reference + reference + end + + reference + end + # Mentionable overrides. def gfm_reference - "issue ##{iid}" + "issue #{to_reference}" end # Reset issue events cache diff --git a/app/models/label.rb b/app/models/label.rb index eee28acefc1..013e6bf5978 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -11,6 +11,8 @@ # class Label < ActiveRecord::Base + include Referable + DEFAULT_COLOR = '#428BCA' default_value_for :color, DEFAULT_COLOR @@ -34,6 +36,31 @@ class Label < ActiveRecord::Base alias_attribute :name, :title + def self.reference_prefix + '~' + end + + # Returns the String necessary to reference this Label in Markdown + # + # format - Symbol format to use (default: :id, optional: :name) + # + # Note that its argument differs from other objects implementing Referable. If + # a non-Symbol argument is given (such as a Project), it will default to :id. + # + # Examples: + # + # Label.first.to_reference # => "~1" + # Label.first.to_reference(:name) # => "~\"bug\"" + # + # Returns a String + def to_reference(format = :id) + if format == :name + %(#{self.class.reference_prefix}"#{name}") + else + "#{self.class.reference_prefix}#{id}" + end + end + def open_issues_count issues.opened.count end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 64f3c39f131..bfbf498591a 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -25,10 +25,11 @@ require Rails.root.join("app/models/commit") require Rails.root.join("lib/static_model") class MergeRequest < ActiveRecord::Base - include Issuable - include Taskable include InternalId + include Issuable + include Referable include Sortable + include Taskable belongs_to :target_project, foreign_key: :target_project_id, class_name: "Project" belongs_to :source_project, foreign_key: :source_project_id, class_name: "Project" @@ -135,6 +136,20 @@ class MergeRequest < ActiveRecord::Base scope :closed, -> { with_states(:closed, :merged) } scope :declined, -> { with_states(:closed) } + def self.reference_prefix + '!' + end + + def to_reference(from_project = nil) + reference = "#{self.class.reference_prefix}#{iid}" + + if cross_project_reference?(from_project) + reference = project.to_reference + reference + end + + reference + end + def validate_branches if target_project == source_project && target_branch == source_branch errors.add :branch_conflict, "You can not use same project/branch for source and target" @@ -291,7 +306,7 @@ class MergeRequest < ActiveRecord::Base # Mentionable override. def gfm_reference - "merge request !#{iid}" + "merge request #{to_reference}" end def target_project_path diff --git a/app/models/project.rb b/app/models/project.rb index 09d3ffd22fe..c943114449a 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -33,11 +33,12 @@ require 'carrierwave/orm/activerecord' require 'file_size_validator' class Project < ActiveRecord::Base - include Sortable + include Gitlab::ConfigHelper include Gitlab::ShellAdapter include Gitlab::VisibilityLevel - include Gitlab::ConfigHelper include Rails.application.routes.url_helpers + include Referable + include Sortable extend Gitlab::ConfigHelper extend Enumerize @@ -305,6 +306,10 @@ class Project < ActiveRecord::Base path end + def to_reference(_from_project = nil) + path_with_namespace + end + def web_url [gitlab_config.url, path_with_namespace].join('/') end diff --git a/app/models/snippet.rb b/app/models/snippet.rb index d2af26539b6..90fada3c114 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -16,10 +16,11 @@ # class Snippet < ActiveRecord::Base - include Sortable - include Linguist::BlobHelper include Gitlab::VisibilityLevel + include Linguist::BlobHelper include Participable + include Referable + include Sortable default_value_for :visibility_level, Snippet::PRIVATE @@ -50,6 +51,20 @@ class Snippet < ActiveRecord::Base participant :author, :notes + def self.reference_prefix + '$' + end + + def to_reference(from_project = nil) + reference = "#{self.class.reference_prefix}#{id}" + + if cross_project_reference?(from_project) + reference = project.to_reference + reference + end + + reference + end + def self.content_types [ ".rb", ".py", ".pl", ".scala", ".c", ".cpp", ".java", diff --git a/app/models/user.rb b/app/models/user.rb index 4dd37e73564..f546dc015c2 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -62,11 +62,13 @@ require 'carrierwave/orm/activerecord' require 'file_size_validator' class User < ActiveRecord::Base - include Sortable - include Gitlab::ConfigHelper - include TokenAuthenticatable extend Gitlab::ConfigHelper + + include Gitlab::ConfigHelper include Gitlab::CurrentSettings + include Referable + include Sortable + include TokenAuthenticatable default_value_for :admin, false default_value_for :can_create_group, gitlab_config.default_can_create_group @@ -247,6 +249,10 @@ class User < ActiveRecord::Base def build_user(attrs = {}) User.new(attrs) end + + def reference_prefix + '@' + end end # @@ -257,6 +263,10 @@ class User < ActiveRecord::Base username end + def to_reference(_from_project = nil) + "#{self.class.reference_prefix}#{username}" + end + def notification @notification ||= Notification.new(self) end diff --git a/spec/models/commit_range_spec.rb b/spec/models/commit_range_spec.rb index 31ee3e99cad..2d347a335a1 100644 --- a/spec/models/commit_range_spec.rb +++ b/spec/models/commit_range_spec.rb @@ -11,6 +11,29 @@ describe CommitRange do expect { described_class.new("Foo") }.to raise_error end + describe 'modules' do + subject { described_class } + + it { is_expected.to include_module(Referable) } + end + + describe '#to_reference' do + let(:project) { double('project', to_reference: 'namespace1/project') } + + before do + range.project = project + end + + it 'returns a String reference to the object' do + expect(range.to_reference).to eq range.to_s + end + + it 'supports a cross-project reference' do + cross = double('project') + expect(range.to_reference(cross)).to eq "#{project.to_reference}@#{range.to_s}" + end + end + describe '#to_s' do it 'is correct for three-dot syntax' do expect(range.to_s).to eq "#{sha_from[0..7]}...#{sha_to[0..7]}" diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb index ad2ac143d97..27eb02a870b 100644 --- a/spec/models/commit_spec.rb +++ b/spec/models/commit_spec.rb @@ -1,8 +1,28 @@ require 'spec_helper' describe Commit do - let(:project) { create :project } - let(:commit) { project.commit } + let(:project) { create(:project) } + let(:commit) { project.commit } + + describe 'modules' do + subject { described_class } + + it { is_expected.to include_module(Mentionable) } + it { is_expected.to include_module(Participable) } + it { is_expected.to include_module(Referable) } + it { is_expected.to include_module(StaticModel) } + end + + describe '#to_reference' do + it 'returns a String reference to the object' do + expect(commit.to_reference).to eq commit.id + end + + it 'supports a cross-project reference' do + cross = double('project') + expect(commit.to_reference(cross)).to eq "#{project.to_reference}@#{commit.id}" + end + end describe '#title' do it "returns no_commit_message when safe_message is blank" do diff --git a/spec/models/external_issue_spec.rb b/spec/models/external_issue_spec.rb new file mode 100644 index 00000000000..7744610db78 --- /dev/null +++ b/spec/models/external_issue_spec.rb @@ -0,0 +1,24 @@ +require 'spec_helper' + +describe ExternalIssue do + let(:project) { double('project', to_reference: 'namespace1/project1') } + let(:issue) { described_class.new('EXT-1234', project) } + + describe 'modules' do + subject { described_class } + + it { is_expected.to include_module(Referable) } + end + + describe '#to_reference' do + it 'returns a String reference to the object' do + expect(issue.to_reference).to eq issue.id + end + end + + describe '#title' do + it 'returns a title' do + expect(issue.title).to eq "External Issue #{issue}" + end + end +end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 9428224a64f..80638fc8db2 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -18,16 +18,30 @@ require 'spec_helper' describe Group do let!(:group) { create(:group) } - describe "Associations" do + describe 'associations' do it { is_expected.to have_many :projects } it { is_expected.to have_many :group_members } end - it { is_expected.to validate_presence_of :name } - it { is_expected.to validate_uniqueness_of(:name) } - it { is_expected.to validate_presence_of :path } - it { is_expected.to validate_uniqueness_of(:path) } - it { is_expected.not_to validate_presence_of :owner } + describe 'modules' do + subject { described_class } + + it { is_expected.to include_module(Referable) } + end + + describe 'validations' do + it { is_expected.to validate_presence_of :name } + it { is_expected.to validate_uniqueness_of(:name) } + it { is_expected.to validate_presence_of :path } + it { is_expected.to validate_uniqueness_of(:path) } + it { is_expected.not_to validate_presence_of :owner } + end + + describe '#to_reference' do + it 'returns a String reference to the object' do + expect(group.to_reference).to eq "@#{group.name}" + end + end describe :users do it { expect(group.users).to eq(group.owners) } diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index 20d823b40e5..4e4f816a26b 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -24,15 +24,30 @@ describe Issue do it { is_expected.to belong_to(:milestone) } end - describe "Mass assignment" do - end - describe 'modules' do + subject { described_class } + + it { is_expected.to include_module(InternalId) } it { is_expected.to include_module(Issuable) } + it { is_expected.to include_module(Referable) } + it { is_expected.to include_module(Sortable) } + it { is_expected.to include_module(Taskable) } end subject { create(:issue) } + describe '#to_reference' do + it 'returns a String reference to the object' do + expect(subject.to_reference).to eq "##{subject.iid}" + end + + it 'supports a cross-project reference' do + cross = double('project') + expect(subject.to_reference(cross)). + to eq "#{subject.project.to_reference}##{subject.iid}" + end + end + describe '#is_being_reassigned?' do it 'returns true if the issue assignee has changed' do subject.assignee = create(:user) diff --git a/spec/models/label_spec.rb b/spec/models/label_spec.rb index 8644ac46605..a13f9ac926c 100644 --- a/spec/models/label_spec.rb +++ b/spec/models/label_spec.rb @@ -14,30 +14,54 @@ require 'spec_helper' describe Label do let(:label) { create(:label) } - it { expect(label).to be_valid } - it { is_expected.to belong_to(:project) } + describe 'associations' do + it { is_expected.to belong_to(:project) } + it { is_expected.to have_many(:label_links).dependent(:destroy) } + it { is_expected.to have_many(:issues).through(:label_links).source(:target) } + end + + describe 'modules' do + subject { described_class } + + it { is_expected.to include_module(Referable) } + end + + describe 'validation' do + it { is_expected.to validate_presence_of(:project) } - describe 'Validation' do it 'should validate color code' do - expect(build(:label, color: 'G-ITLAB')).not_to be_valid - expect(build(:label, color: 'AABBCC')).not_to be_valid - expect(build(:label, color: '#AABBCCEE')).not_to be_valid - expect(build(:label, color: '#GGHHII')).not_to be_valid - expect(build(:label, color: '#')).not_to be_valid - expect(build(:label, color: '')).not_to be_valid - - expect(build(:label, color: '#AABBCC')).to be_valid + expect(label).not_to allow_value('G-ITLAB').for(:color) + expect(label).not_to allow_value('AABBCC').for(:color) + expect(label).not_to allow_value('#AABBCCEE').for(:color) + expect(label).not_to allow_value('GGHHII').for(:color) + expect(label).not_to allow_value('#').for(:color) + expect(label).not_to allow_value('').for(:color) + + expect(label).to allow_value('#AABBCC').for(:color) + expect(label).to allow_value('#abcdef').for(:color) end it 'should validate title' do - expect(build(:label, title: 'G,ITLAB')).not_to be_valid - expect(build(:label, title: 'G?ITLAB')).not_to be_valid - expect(build(:label, title: 'G&ITLAB')).not_to be_valid - expect(build(:label, title: '')).not_to be_valid + expect(label).not_to allow_value('G,ITLAB').for(:title) + expect(label).not_to allow_value('G?ITLAB').for(:title) + expect(label).not_to allow_value('G&ITLAB').for(:title) + expect(label).not_to allow_value('').for(:title) + + expect(label).to allow_value('GITLAB').for(:title) + expect(label).to allow_value('gitlab').for(:title) + expect(label).to allow_value("customer's request").for(:title) + end + end + + describe '#to_reference' do + it 'returns a String reference to the object' do + expect(label.to_reference).to eq "~#{label.id}" + expect(label.to_reference(double)).to eq "~#{label.id}" + end - expect(build(:label, title: 'GITLAB')).to be_valid - expect(build(:label, title: 'gitlab')).to be_valid + it 'returns a String reference to the object using its name' do + expect(label.to_reference(:name)).to eq %(~"#{label.name}") end end end diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 97b8abc49dd..757d8bdfae2 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -24,7 +24,26 @@ require 'spec_helper' describe MergeRequest do - describe "Validation" do + subject { create(:merge_request) } + + describe 'modules' do + subject { described_class } + + it { is_expected.to include_module(InternalId) } + it { is_expected.to include_module(Issuable) } + it { is_expected.to include_module(Referable) } + it { is_expected.to include_module(Sortable) } + it { is_expected.to include_module(Taskable) } + end + + describe 'associations' do + it { is_expected.to belong_to(:target_project).with_foreign_key(:target_project_id).class_name('Project') } + it { is_expected.to belong_to(:source_project).with_foreign_key(:source_project_id).class_name('Project') } + + it { is_expected.to have_one(:merge_request_diff).dependent(:destroy) } + end + + describe 'validation' do it { is_expected.to validate_presence_of(:target_branch) } it { is_expected.to validate_presence_of(:source_branch) } end @@ -38,8 +57,15 @@ describe MergeRequest do it { is_expected.to respond_to(:cannot_be_merged?) } end - describe 'modules' do - it { is_expected.to include_module(Issuable) } + describe '#to_reference' do + it 'returns a String reference to the object' do + expect(subject.to_reference).to eq "!#{subject.iid}" + end + + it 'supports a cross-project reference' do + cross = double('project') + expect(subject.to_reference(cross)).to eq "#{subject.source_project.to_reference}!#{subject.iid}" + end end describe "#mr_and_commit_notes" do diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 37e21a90818..48568e2a3ff 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -32,7 +32,7 @@ require 'spec_helper' describe Project do - describe 'Associations' do + describe 'associations' do it { is_expected.to belong_to(:group) } it { is_expected.to belong_to(:namespace) } it { is_expected.to belong_to(:creator).class_name('User') } @@ -54,10 +54,17 @@ describe Project do it { is_expected.to have_one(:asana_service).dependent(:destroy) } end - describe 'Mass assignment' do + describe 'modules' do + subject { described_class } + + it { is_expected.to include_module(Gitlab::ConfigHelper) } + it { is_expected.to include_module(Gitlab::ShellAdapter) } + it { is_expected.to include_module(Gitlab::VisibilityLevel) } + it { is_expected.to include_module(Referable) } + it { is_expected.to include_module(Sortable) } end - describe 'Validation' do + describe 'validation' do let!(:project) { create(:project) } it { is_expected.to validate_presence_of(:name) } @@ -91,6 +98,14 @@ describe Project do it { is_expected.to respond_to(:path_with_namespace) } end + describe '#to_reference' do + let(:project) { create(:empty_project) } + + it 'returns a String reference to the object' do + expect(project.to_reference).to eq project.path_with_namespace + end + end + it 'should return valid url to repo' do project = Project.new(path: 'somewhere') expect(project.url_to_repo).to eq(Gitlab.config.gitlab_shell.ssh_path_prefix + 'somewhere.git') diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb index e37dcc75230..252320b798e 100644 --- a/spec/models/snippet_spec.rb +++ b/spec/models/snippet_spec.rb @@ -18,7 +18,17 @@ require 'spec_helper' describe Snippet do - describe "Associations" do + describe 'modules' do + subject { described_class } + + it { is_expected.to include_module(Gitlab::VisibilityLevel) } + it { is_expected.to include_module(Linguist::BlobHelper) } + it { is_expected.to include_module(Participable) } + it { is_expected.to include_module(Referable) } + it { is_expected.to include_module(Sortable) } + end + + describe 'associations' do it { is_expected.to belong_to(:author).class_name('User') } it { is_expected.to have_many(:notes).dependent(:destroy) } end @@ -37,4 +47,18 @@ describe Snippet do it { is_expected.to validate_presence_of(:content) } end + + describe '#to_reference' do + let(:project) { create(:empty_project) } + let(:snippet) { create(:snippet, project: project) } + + it 'returns a String reference to the object' do + expect(snippet.to_reference).to eq "$#{snippet.id}" + end + + it 'supports a cross-project reference' do + cross = double('project') + expect(snippet.to_reference(cross)).to eq "#{project.to_reference}$#{snippet.id}" + end + end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 0dddcd5bda2..87f95f9af87 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -63,7 +63,17 @@ require 'spec_helper' describe User do include Gitlab::CurrentSettings - describe "Associations" do + describe 'modules' do + subject { described_class } + + it { is_expected.to include_module(Gitlab::ConfigHelper) } + it { is_expected.to include_module(Gitlab::CurrentSettings) } + it { is_expected.to include_module(Referable) } + it { is_expected.to include_module(Sortable) } + it { is_expected.to include_module(TokenAuthenticatable) } + end + + describe 'associations' do it { is_expected.to have_one(:namespace) } it { is_expected.to have_many(:snippets).class_name('Snippet').dependent(:destroy) } it { is_expected.to have_many(:project_members).dependent(:destroy) } @@ -175,6 +185,14 @@ describe User do it { is_expected.to respond_to(:private_token) } end + describe '#to_reference' do + let(:user) { create(:user) } + + it 'returns a String reference to the object' do + expect(user.to_reference).to eq "@#{user.username}" + end + end + describe '#generate_password' do it "should execute callback when force_random_password specified" do user = build(:user, force_random_password: true) -- cgit v1.2.1 From 8773f339a33cf31f979013cf306e5fca5fe66a89 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sat, 2 May 2015 23:14:31 -0400 Subject: Minor model spec cleanups Snippet model was missing project association --- app/models/snippet.rb | 3 ++- spec/models/issue_spec.rb | 7 ++----- spec/models/merge_request_spec.rb | 21 ++++++++------------- spec/models/snippet_spec.rb | 10 +++++----- spec/models/user_spec.rb | 3 --- 5 files changed, 17 insertions(+), 27 deletions(-) diff --git a/app/models/snippet.rb b/app/models/snippet.rb index 90fada3c114..8c3167833aa 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -24,7 +24,8 @@ class Snippet < ActiveRecord::Base default_value_for :visibility_level, Snippet::PRIVATE - belongs_to :author, class_name: "User" + belongs_to :author, class_name: 'User' + belongs_to :project has_many :notes, as: :noteable, dependent: :destroy diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index 4e4f816a26b..614b648bb52 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -60,11 +60,8 @@ describe Issue do describe '#is_being_reassigned?' do it 'returns issues assigned to user' do - user = create :user - - 2.times do - issue = create :issue, assignee: user - end + user = create(:user) + create_list(:issue, 2, assignee: user) expect(Issue.open_for(user).count).to eq 2 end diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 757d8bdfae2..57b1b9dfcf0 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -26,6 +26,13 @@ require 'spec_helper' describe MergeRequest do subject { create(:merge_request) } + describe 'associations' do + it { is_expected.to belong_to(:target_project).with_foreign_key(:target_project_id).class_name('Project') } + it { is_expected.to belong_to(:source_project).with_foreign_key(:source_project_id).class_name('Project') } + + it { is_expected.to have_one(:merge_request_diff).dependent(:destroy) } + end + describe 'modules' do subject { described_class } @@ -36,22 +43,12 @@ describe MergeRequest do it { is_expected.to include_module(Taskable) } end - describe 'associations' do - it { is_expected.to belong_to(:target_project).with_foreign_key(:target_project_id).class_name('Project') } - it { is_expected.to belong_to(:source_project).with_foreign_key(:source_project_id).class_name('Project') } - - it { is_expected.to have_one(:merge_request_diff).dependent(:destroy) } - end - describe 'validation' do it { is_expected.to validate_presence_of(:target_branch) } it { is_expected.to validate_presence_of(:source_branch) } end - describe "Mass assignment" do - end - - describe "Respond to" do + describe 'respond to' do it { is_expected.to respond_to(:unchecked?) } it { is_expected.to respond_to(:can_be_merged?) } it { is_expected.to respond_to(:cannot_be_merged?) } @@ -83,8 +80,6 @@ describe MergeRequest do end end - subject { create(:merge_request) } - describe '#is_being_reassigned?' do it 'returns true if the merge_request assignee has changed' do subject.assignee = create(:user) diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb index 252320b798e..c81dd36ef4b 100644 --- a/spec/models/snippet_spec.rb +++ b/spec/models/snippet_spec.rb @@ -30,22 +30,22 @@ describe Snippet do describe 'associations' do it { is_expected.to belong_to(:author).class_name('User') } + it { is_expected.to belong_to(:project) } it { is_expected.to have_many(:notes).dependent(:destroy) } end - describe "Mass assignment" do - end - - describe "Validation" do + describe 'validation' do it { is_expected.to validate_presence_of(:author) } it { is_expected.to validate_presence_of(:title) } it { is_expected.to ensure_length_of(:title).is_within(0..255) } it { is_expected.to validate_presence_of(:file_name) } - it { is_expected.to ensure_length_of(:title).is_within(0..255) } + it { is_expected.to ensure_length_of(:file_name).is_within(0..255) } it { is_expected.to validate_presence_of(:content) } + + it { is_expected.to validate_inclusion_of(:visibility_level).in_array(Gitlab::VisibilityLevel.values) } end describe '#to_reference' do diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 87f95f9af87..e1205c18a85 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -89,9 +89,6 @@ describe User do it { is_expected.to have_many(:identities).dependent(:destroy) } end - describe "Mass assignment" do - end - describe 'validations' do it { is_expected.to validate_presence_of(:username) } it { is_expected.to validate_presence_of(:projects_limit) } -- cgit v1.2.1 From 3b80cf524c0969495b602b600c2c1e3b52e1c78c Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sat, 2 May 2015 23:44:46 -0400 Subject: Use to_reference in Mentionable shared examples --- spec/support/mentionable_shared_examples.rb | 32 +++++++++++++---------------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/spec/support/mentionable_shared_examples.rb b/spec/support/mentionable_shared_examples.rb index 53fb6545553..ede62e8f37a 100644 --- a/spec/support/mentionable_shared_examples.rb +++ b/spec/support/mentionable_shared_examples.rb @@ -10,12 +10,12 @@ def common_mentionable_setup let(:mentioned_issue) { create(:issue, project: project) } let(:mentioned_mr) { create(:merge_request, :simple, source_project: project) } - let(:mentioned_commit) { project.repository.commit } + let(:mentioned_commit) { project.commit } let(:ext_proj) { create(:project, :public) } let(:ext_issue) { create(:issue, project: ext_proj) } let(:ext_mr) { create(:merge_request, :simple, source_project: ext_proj) } - let(:ext_commit) { ext_proj.repository.commit } + let(:ext_commit) { ext_proj.commit } # Override to add known commits to the repository stub. let(:extra_commits) { [] } @@ -23,21 +23,19 @@ def common_mentionable_setup # A string that mentions each of the +mentioned_.*+ objects above. Mentionables should add a self-reference # to this string and place it in their +mentionable_text+. let(:ref_string) do - cross = ext_proj.path_with_namespace - <<-MSG.strip_heredoc These references are new: - Issue: ##{mentioned_issue.iid} - Merge: !#{mentioned_mr.iid} - Commit: #{mentioned_commit.id} + Issue: #{mentioned_issue.to_reference} + Merge: #{mentioned_mr.to_reference} + Commit: #{mentioned_commit.to_reference} This reference is a repeat and should only be mentioned once: - Repeat: ##{mentioned_issue.iid} + Repeat: #{mentioned_issue.to_reference} These references are cross-referenced: - Issue: #{cross}##{ext_issue.iid} - Merge: #{cross}!#{ext_mr.iid} - Commit: #{cross}@#{ext_commit.short_id} + Issue: #{ext_issue.to_reference(project)} + Merge: #{ext_mr.to_reference(project)} + Commit: #{ext_commit.to_reference(project)} This is a self-reference and should not be mentioned at all: Self: #{backref_text} @@ -109,19 +107,17 @@ shared_examples 'an editable mentionable' do it 'creates new cross-reference notes when the mentionable text is edited' do subject.save - cross = ext_proj.path_with_namespace - new_text = <<-MSG These references already existed: - Issue: ##{mentioned_issue.iid} - Commit: #{mentioned_commit.id} + Issue: #{mentioned_issue.to_reference} + Commit: #{mentioned_commit.to_reference} This cross-project reference already existed: - Issue: #{cross}##{ext_issue.iid} + Issue: #{ext_issue.to_reference(project)} These two references are introduced in an edit: - Issue: ##{new_issues[0].iid} - Cross: #{cross}##{new_issues[1].iid} + Issue: #{new_issues[0].to_reference} + Cross: #{new_issues[1].to_reference(project)} MSG # These three objects were already referenced, and should not receive new -- cgit v1.2.1 From ca268b85f62448584eb8455048069669efdcc990 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sat, 2 May 2015 23:59:55 -0400 Subject: Use to_reference in Markdown feature spec --- spec/features/markdown_spec.rb | 17 ++++------- spec/fixtures/markdown.md.erb | 64 +++++++++++++++++++++--------------------- 2 files changed, 38 insertions(+), 43 deletions(-) diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb index 8f3dfc8d5a9..0fc144462f2 100644 --- a/spec/features/markdown_spec.rb +++ b/spec/features/markdown_spec.rb @@ -344,13 +344,13 @@ class MarkdownFeature end def commit - @commit ||= project.repository.commit + @commit ||= project.commit end def commit_range unless @commit_range - commit2 = project.repository.commit('HEAD~3') - @commit_range = CommitRange.new("#{commit.id}...#{commit2.id}") + commit2 = project.commit('HEAD~3') + @commit_range = CommitRange.new("#{commit.id}...#{commit2.id}", project) end @commit_range @@ -376,11 +376,6 @@ class MarkdownFeature @xproject end - # Shortcut to "cross-reference/project" - def xref - xproject.path_with_namespace - end - def xissue @xissue ||= create(:issue, project: xproject) end @@ -394,13 +389,13 @@ class MarkdownFeature end def xcommit - @xcommit ||= xproject.repository.commit + @xcommit ||= xproject.commit end def xcommit_range unless @xcommit_range - xcommit2 = xproject.repository.commit('HEAD~2') - @xcommit_range = CommitRange.new("#{xcommit.id}...#{xcommit2.id}") + xcommit2 = xproject.commit('HEAD~2') + @xcommit_range = CommitRange.new("#{xcommit.id}...#{xcommit2.id}", xproject) end @xcommit_range diff --git a/spec/fixtures/markdown.md.erb b/spec/fixtures/markdown.md.erb index 64817ec6700..26fc4e38e5a 100644 --- a/spec/fixtures/markdown.md.erb +++ b/spec/fixtures/markdown.md.erb @@ -127,61 +127,61 @@ But it shouldn't autolink text inside certain tags: - http://about.gitlab.com/ - http://about.gitlab.com/ -### Reference Filters (e.g., #<%= issue.iid %>) +### Reference Filters (e.g., <%= issue.to_reference %>) -References should be parseable even inside _!<%= merge_request.iid %>_ emphasis. +References should be parseable even inside _<%= merge_request.to_reference %>_ emphasis. #### UserReferenceFilter - All: @all -- User: @<%= user.username %> -- Group: @<%= group.name %> -- Ignores invalid: @fake_user -- Ignored in code: `@<%= user.username %>` -- Ignored in links: [Link to @<%= user.username %>](#user-link) +- User: <%= user.to_reference %> +- Group: <%= group.to_reference %> +- Ignores invalid: <%= User.reference_prefix %>fake_user +- Ignored in code: `<%= user.to_reference %>` +- Ignored in links: [Link to <%= user.to_reference %>](#user-link) #### IssueReferenceFilter -- Issue: #<%= issue.iid %> -- Issue in another project: <%= xref %>#<%= xissue.iid %> -- Ignored in code: `#<%= issue.iid %>` -- Ignored in links: [Link to #<%= issue.iid %>](#issue-link) +- Issue: <%= issue.to_reference %> +- Issue in another project: <%= xissue.to_reference(project) %> +- Ignored in code: `<%= issue.to_reference %>` +- Ignored in links: [Link to <%= issue.to_reference %>](#issue-link) #### MergeRequestReferenceFilter -- Merge request: !<%= merge_request.iid %> -- Merge request in another project: <%= xref %>!<%= xmerge_request.iid %> -- Ignored in code: `!<%= merge_request.iid %>` -- Ignored in links: [Link to !<%= merge_request.iid %>](#merge-request-link) +- Merge request: <%= merge_request.to_reference %> +- Merge request in another project: <%= xmerge_request.to_reference(project) %> +- Ignored in code: `<%= merge_request.to_reference %>` +- Ignored in links: [Link to <%= merge_request.to_reference %>](#merge-request-link) #### SnippetReferenceFilter -- Snippet: $<%= snippet.id %> -- Snippet in another project: <%= xref %>$<%= xsnippet.id %> -- Ignored in code: `$<%= snippet.id %>` -- Ignored in links: [Link to $<%= snippet.id %>](#snippet-link) +- Snippet: <%= snippet.to_reference %> +- Snippet in another project: <%= xsnippet.to_reference(project) %> +- Ignored in code: `<%= snippet.to_reference %>` +- Ignored in links: [Link to <%= snippet.to_reference %>](#snippet-link) #### CommitRangeReferenceFilter -- Range: <%= commit_range %> -- Range in another project: <%= xref %>@<%= xcommit_range %> -- Ignored in code: `<%= commit_range %>` -- Ignored in links: [Link to <%= commit_range %>](#commit-range-link) +- Range: <%= commit_range.to_reference %> +- Range in another project: <%= xcommit_range.to_reference(project) %> +- Ignored in code: `<%= commit_range.to_reference %>` +- Ignored in links: [Link to <%= commit_range.to_reference %>](#commit-range-link) #### CommitReferenceFilter -- Commit: <%= commit.id %> -- Commit in another project: <%= xref %>@<%= xcommit.id %> -- Ignored in code: `<%= commit.id %>` -- Ignored in links: [Link to <%= commit.id %>](#commit-link) +- Commit: <%= commit.to_reference %> +- Commit in another project: <%= xcommit.to_reference(project) %> +- Ignored in code: `<%= commit.to_reference %>` +- Ignored in links: [Link to <%= commit.to_reference %>](#commit-link) #### LabelReferenceFilter -- Label by ID: ~<%= simple_label.id %> -- Label by name: ~<%= simple_label.name %> -- Label by name in quotes: ~"<%= label.name %>" -- Ignored in code: `~<%= simple_label.name %>` -- Ignored in links: [Link to ~<%= simple_label.id %>](#label-link) +- Label by ID: <%= simple_label.to_reference %> +- Label by name: <%= Label.reference_prefix %><%= simple_label.name %> +- Label by name in quotes: <%= label.to_reference(:name) %> +- Ignored in code: `<%= simple_label.to_reference %>` +- Ignored in links: [Link to <%= simple_label.to_reference %>](#label-link) ### Task Lists -- cgit v1.2.1 From 38fb6279f9709e43f80c70c1fd4cccef77963b21 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 20 Apr 2015 18:47:22 -0400 Subject: Simplify `cross_project_reference` with `to_reference` --- app/helpers/gitlab_markdown_helper.rb | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb index 7bcc011fd5f..d89f7b4a28d 100644 --- a/app/helpers/gitlab_markdown_helper.rb +++ b/app/helpers/gitlab_markdown_helper.rb @@ -135,15 +135,25 @@ module GitlabMarkdownHelper end end + # Returns the text necessary to reference `entity` across projects + # + # project - Project to reference + # entity - Object that responds to `to_reference` + # + # Examples: + # + # cross_project_reference(project, project.issues.first) + # # => 'namespace1/project1#123' + # + # cross_project_reference(project, project.merge_requests.first) + # # => 'namespace1/project1!345' + # + # Returns a String def cross_project_reference(project, entity) - path = project.path_with_namespace - - if entity.kind_of?(Issue) - [path, entity.iid].join('#') - elsif entity.kind_of?(MergeRequest) - [path, entity.iid].join('!') + if entity.respond_to?(:to_reference) + "#{project.to_reference}#{entity.to_reference}" else - raise 'Not supported type' + '' end end end -- cgit v1.2.1 From 0359d41b064e9f7680d0017013e011103747b614 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 11 May 2015 15:56:00 -0400 Subject: Implement gfm_reference directly in Mentionable Except for Note, which still overrides it. --- app/models/commit.rb | 5 ----- app/models/concerns/mentionable.rb | 11 ++++++++--- app/models/issue.rb | 6 ------ app/models/merge_request.rb | 5 ----- 4 files changed, 8 insertions(+), 19 deletions(-) diff --git a/app/models/commit.rb b/app/models/commit.rb index 3cc8d11a4aa..085f4e6398f 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -140,11 +140,6 @@ class Commit Gitlab::ClosingIssueExtractor.new(project, current_user).closed_by_message(safe_message) end - # Mentionable override. - def gfm_reference - "commit #{to_reference}" - end - def author User.find_for_commit(author_email, author_name) end diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb index b7c39df885d..f28b20afd8e 100644 --- a/app/models/concerns/mentionable.rb +++ b/app/models/concerns/mentionable.rb @@ -20,10 +20,15 @@ module Mentionable end end - # Generate a GFM back-reference that will construct a link back to this Mentionable when rendered. Must - # be overridden if this model object can be referenced directly by GFM notation. + # Returns the text used as the body of a Note when this object is referenced + # + # By default this will be the class name and the result of calling + # `to_reference` on the object. def gfm_reference - raise NotImplementedError.new("#{self.class} does not implement #gfm_reference") + # Convert "MergeRequest" to "merge request" + friendly_name = self.class.to_s.underscore.humanize.downcase + + "#{friendly_name} #{to_reference}" end # Construct a String that contains possible GFM references. diff --git a/app/models/issue.rb b/app/models/issue.rb index ff13cbca845..31803b57b3f 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -68,12 +68,6 @@ class Issue < ActiveRecord::Base reference end - # Mentionable overrides. - - def gfm_reference - "issue #{to_reference}" - end - # Reset issue events cache # # Since we do cache @event we need to reset cache in special cases: diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index bfbf498591a..60b0ce6c018 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -304,11 +304,6 @@ class MergeRequest < ActiveRecord::Base end end - # Mentionable override. - def gfm_reference - "merge request #{to_reference}" - end - def target_project_path if target_project target_project.path_with_namespace -- cgit v1.2.1 From 9d032cddf5575e2233c81142a3ebc609cd43a47e Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 12 May 2015 16:26:53 -0400 Subject: Correct the ReferenceFilter html/pipeline/filter require --- lib/gitlab/markdown/reference_filter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gitlab/markdown/reference_filter.rb b/lib/gitlab/markdown/reference_filter.rb index a4303d96bef..be4d26af0fc 100644 --- a/lib/gitlab/markdown/reference_filter.rb +++ b/lib/gitlab/markdown/reference_filter.rb @@ -1,5 +1,5 @@ require 'active_support/core_ext/string/output_safety' -require 'html/pipeline' +require 'html/pipeline/filter' module Gitlab module Markdown -- cgit v1.2.1 From 136ab73803850c10588b369862b1e5524849d31c Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 12 May 2015 17:58:29 -0400 Subject: Update CommitRange#to_reference to use full SHAs We only want them shortened by the filter, which calls to_s --- app/models/commit_range.rb | 9 +++++--- .../markdown/commit_range_reference_filter_spec.rb | 3 +-- spec/models/commit_range_spec.rb | 26 +++++++++++----------- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/app/models/commit_range.rb b/app/models/commit_range.rb index b98f939a115..fb1f6d09be6 100644 --- a/app/models/commit_range.rb +++ b/app/models/commit_range.rb @@ -61,11 +61,14 @@ class CommitRange end def to_reference(from_project = nil) + # Not using to_s because we want the full SHAs + reference = sha_from + notation + sha_to + if cross_project_reference?(from_project) - "#{project.to_reference}@#{to_s}" - else - to_s + reference = project.to_reference + '@' + reference end + + reference end # Returns a String for use in a link's title attribute diff --git a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb index 7274cb309a0..1593088a094 100644 --- a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb @@ -9,8 +9,7 @@ module Gitlab::Markdown let(:commit2) { project.commit("HEAD~2") } it 'requires project context' do - expect { described_class.call('Commit Range 1c002d..d200c1', {}) }. - to raise_error(ArgumentError, /:project/) + expect { described_class.call('') }.to raise_error(ArgumentError, /:project/) end %w(pre code a style).each do |elem| diff --git a/spec/models/commit_range_spec.rb b/spec/models/commit_range_spec.rb index 2d347a335a1..e7fb43ff335 100644 --- a/spec/models/commit_range_spec.rb +++ b/spec/models/commit_range_spec.rb @@ -1,6 +1,12 @@ require 'spec_helper' describe CommitRange do + describe 'modules' do + subject { described_class } + + it { is_expected.to include_module(Referable) } + end + let(:sha_from) { 'f3f85602' } let(:sha_to) { 'e86e1013' } @@ -11,10 +17,14 @@ describe CommitRange do expect { described_class.new("Foo") }.to raise_error end - describe 'modules' do - subject { described_class } + describe '#to_s' do + it 'is correct for three-dot syntax' do + expect(range.to_s).to eq "#{sha_from[0..7]}...#{sha_to[0..7]}" + end - it { is_expected.to include_module(Referable) } + it 'is correct for two-dot syntax' do + expect(range2.to_s).to eq "#{sha_from[0..7]}..#{sha_to[0..7]}" + end end describe '#to_reference' do @@ -34,16 +44,6 @@ describe CommitRange do end end - describe '#to_s' do - it 'is correct for three-dot syntax' do - expect(range.to_s).to eq "#{sha_from[0..7]}...#{sha_to[0..7]}" - end - - it 'is correct for two-dot syntax' do - expect(range2.to_s).to eq "#{sha_from[0..7]}..#{sha_to[0..7]}" - end - end - describe '#reference_title' do it 'returns the correct String for three-dot ranges' do expect(range.reference_title).to eq "Commits #{sha_from} through #{sha_to}" -- cgit v1.2.1 From 91eb346de6abd56fae469417a224bc2b2e7c7364 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 12 May 2015 17:59:30 -0400 Subject: Add invalidate_reference to ReferenceFilterSpecHelper --- spec/support/reference_filter_spec_helper.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/spec/support/reference_filter_spec_helper.rb b/spec/support/reference_filter_spec_helper.rb index 06c39e1ada5..d2766615ccb 100644 --- a/spec/support/reference_filter_spec_helper.rb +++ b/spec/support/reference_filter_spec_helper.rb @@ -10,6 +10,25 @@ module ReferenceFilterSpecHelper Rails.application.routes.url_helpers end + # Modify a reference to make it invalid + # + # Commit SHAs get reversed, IDs get incremented by 1 + # + # reference - String reference to modify + # + # Returns a String + def invalidate_reference(reference) + if reference =~ /\A(.+)?.\d+\z/ + # Integer-based reference with optional project prefix + reference.gsub(/\d+\z/) { |i| i.to_i + 1 } + elsif reference =~ /\A(.+@)?(\h{6,40}\z)/ + # SHA-based reference with optional prefix + reference.gsub(/\h{6,40}\z/) { |v| v.reverse } + else + reference + end + end + # Perform `call` on the described class # # Automatically passes the current `project` value to the context if none is -- cgit v1.2.1 From 94af050117df20a661e03055a5002cae90282d6d Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 12 May 2015 18:35:00 -0400 Subject: Use `to_reference` in reference filter specs --- .../markdown/commit_range_reference_filter_spec.rb | 33 +++++++++------- .../markdown/commit_reference_filter_spec.rb | 16 ++++---- .../external_issue_reference_filter_spec.rb | 16 ++------ .../gitlab/markdown/issue_reference_filter_spec.rb | 19 +++++---- .../gitlab/markdown/label_reference_filter_spec.rb | 21 +++++----- .../merge_request_reference_filter_spec.rb | 13 +++---- .../markdown/snippet_reference_filter_spec.rb | 11 +++--- .../gitlab/markdown/user_reference_filter_spec.rb | 45 ++++++++++------------ spec/support/reference_filter_spec_helper.rb | 7 ++-- 9 files changed, 86 insertions(+), 95 deletions(-) diff --git a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb index 1593088a094..d3695ee46d0 100644 --- a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb @@ -8,33 +8,36 @@ module Gitlab::Markdown let(:commit1) { project.commit } let(:commit2) { project.commit("HEAD~2") } + let(:range) { CommitRange.new("#{commit1.id}...#{commit2.id}") } + let(:range2) { CommitRange.new("#{commit1.id}..#{commit2.id}") } + it 'requires project context' do expect { described_class.call('') }.to raise_error(ArgumentError, /:project/) end %w(pre code a style).each do |elem| it "ignores valid references contained inside '#{elem}' element" do - exp = act = "<#{elem}>Commit Range #{commit1.id}..#{commit2.id}" + exp = act = "<#{elem}>Commit Range #{range.to_reference}" expect(filter(act).to_html).to eq exp end end context 'internal reference' do - let(:reference) { "#{commit1.id}...#{commit2.id}" } - let(:reference2) { "#{commit1.id}..#{commit2.id}" } + let(:reference) { range.to_reference } + let(:reference2) { range2.to_reference } it 'links to a valid two-dot reference' do doc = filter("See #{reference2}") expect(doc.css('a').first.attr('href')). - to eq urls.namespace_project_compare_url(project.namespace, project, from: "#{commit1.id}^", to: commit2.id) + to eq urls.namespace_project_compare_url(project.namespace, project, range2.to_param) end it 'links to a valid three-dot reference' do doc = filter("See #{reference}") expect(doc.css('a').first.attr('href')). - to eq urls.namespace_project_compare_url(project.namespace, project, from: commit1.id, to: commit2.id) + to eq urls.namespace_project_compare_url(project.namespace, project, range.to_param) end it 'links to a valid short ID' do @@ -50,7 +53,7 @@ module Gitlab::Markdown it 'links with adjacent text' do doc = filter("See (#{reference}.)") - exp = Regexp.escape("#{commit1.short_id}...#{commit2.short_id}") + exp = Regexp.escape(range.to_s) expect(doc.to_html).to match(/\(#{exp}<\/a>\.\)/) end @@ -64,7 +67,7 @@ module Gitlab::Markdown it 'includes a title attribute' do doc = filter("See #{reference}") - expect(doc.css('a').first.attr('title')).to eq "Commits #{commit1.id} through #{commit2.id}" + expect(doc.css('a').first.attr('title')).to eq range.reference_title end it 'includes default classes' do @@ -94,9 +97,11 @@ module Gitlab::Markdown context 'cross-project reference' do let(:namespace) { create(:namespace, name: 'cross-reference') } let(:project2) { create(:project, namespace: namespace) } - let(:commit1) { project.commit } - let(:commit2) { project.commit("HEAD~2") } - let(:reference) { "#{project2.path_with_namespace}@#{commit1.id}...#{commit2.id}" } + let(:reference) { range.to_reference(project) } + + before do + range.project = project2 + end context 'when user can access reference' do before { allow_cross_reference! } @@ -105,21 +110,21 @@ module Gitlab::Markdown doc = filter("See #{reference}") expect(doc.css('a').first.attr('href')). - to eq urls.namespace_project_compare_url(project2.namespace, project2, from: commit1.id, to: commit2.id) + to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param) end it 'links with adjacent text' do doc = filter("Fixed (#{reference}.)") - exp = Regexp.escape("#{project2.path_with_namespace}@#{commit1.short_id}...#{commit2.short_id}") + exp = Regexp.escape("#{project2.to_reference}@#{range.to_s}") expect(doc.to_html).to match(/\(#{exp}<\/a>\.\)/) end it 'ignores invalid commit IDs on the referenced project' do - exp = act = "Fixed #{project2.path_with_namespace}##{commit1.id.reverse}...#{commit2.id}" + exp = act = "Fixed #{project2.to_reference}@#{commit1.id.reverse}...#{commit2.id}" expect(filter(act).to_html).to eq exp - exp = act = "Fixed #{project2.path_with_namespace}##{commit1.id}...#{commit2.id.reverse}" + exp = act = "Fixed #{project2.to_reference}@#{commit1.id}...#{commit2.id.reverse}" expect(filter(act).to_html).to eq exp end diff --git a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb index cc32a4fcf03..a0d2cd7e22b 100644 --- a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb @@ -8,8 +8,7 @@ module Gitlab::Markdown let(:commit) { project.commit } it 'requires project context' do - expect { described_class.call('Commit 1c002d', {}) }. - to raise_error(ArgumentError, /:project/) + expect { described_class.call('') }.to raise_error(ArgumentError, /:project/) end %w(pre code a style).each do |elem| @@ -47,10 +46,11 @@ module Gitlab::Markdown end it 'ignores invalid commit IDs' do - exp = act = "See #{reference.reverse}" + invalid = invalidate_reference(reference) + exp = act = "See #{invalid}" expect(project).to receive(:valid_repo?).and_return(true) - expect(project.repository).to receive(:commit).with(reference.reverse) + expect(project.repository).to receive(:commit).with(invalid) expect(filter(act).to_html).to eq exp end @@ -93,8 +93,8 @@ module Gitlab::Markdown context 'cross-project reference' do let(:namespace) { create(:namespace, name: 'cross-reference') } let(:project2) { create(:project, namespace: namespace) } - let(:commit) { project.commit } - let(:reference) { "#{project2.path_with_namespace}@#{commit.id}" } + let(:commit) { project2.commit } + let(:reference) { commit.to_reference(project) } context 'when user can access reference' do before { allow_cross_reference! } @@ -109,12 +109,12 @@ module Gitlab::Markdown it 'links with adjacent text' do doc = filter("Fixed (#{reference}.)") - exp = Regexp.escape(project2.path_with_namespace) + exp = Regexp.escape(project2.to_reference) expect(doc.to_html).to match(/\(#{exp}@#{commit.short_id}<\/a>\.\)/) end it 'ignores invalid commit IDs on the referenced project' do - exp = act = "Committed #{project2.path_with_namespace}##{commit.id.reverse}" + exp = act = "Committed #{invalidate_reference(reference)}" expect(filter(act).to_html).to eq exp end diff --git a/spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb b/spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb index b19bc125b92..bf9409589fa 100644 --- a/spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb @@ -9,19 +9,18 @@ module Gitlab::Markdown end let(:project) { create(:jira_project) } - let(:issue) { double('issue', iid: 123) } context 'JIRA issue references' do - let(:reference) { "JIRA-#{issue.iid}" } + let(:issue) { ExternalIssue.new('JIRA-123', project) } + let(:reference) { issue.to_reference } it 'requires project context' do - expect { described_class.call('Issue JIRA-123', {}) }. - to raise_error(ArgumentError, /:project/) + expect { described_class.call('') }.to raise_error(ArgumentError, /:project/) end %w(pre code a style).each do |elem| it "ignores valid references contained inside '#{elem}' element" do - exp = act = "<#{elem}>Issue JIRA-#{issue.iid}" + exp = act = "<#{elem}>Issue #{reference}" expect(filter(act).to_html).to eq exp end end @@ -33,13 +32,6 @@ module Gitlab::Markdown expect(filter(act).to_html).to eq exp end - %w(pre code a style).each do |elem| - it "ignores references contained inside '#{elem}' element" do - exp = act = "<#{elem}>Issue #{reference}" - expect(filter(act).to_html).to eq exp - end - end - it 'links to a valid reference' do doc = filter("Issue #{reference}") expect(doc.css('a').first.attr('href')) diff --git a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb index 08382b3e7e8..a838d7570c8 100644 --- a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb @@ -12,24 +12,23 @@ module Gitlab::Markdown let(:issue) { create(:issue, project: project) } it 'requires project context' do - expect { described_class.call('Issue #123', {}) }. - to raise_error(ArgumentError, /:project/) + expect { described_class.call('') }.to raise_error(ArgumentError, /:project/) end %w(pre code a style).each do |elem| it "ignores valid references contained inside '#{elem}' element" do - exp = act = "<#{elem}>Issue ##{issue.iid}" + exp = act = "<#{elem}>Issue #{issue.to_reference}" expect(filter(act).to_html).to eq exp end end context 'internal reference' do - let(:reference) { "##{issue.iid}" } + let(:reference) { issue.to_reference } it 'ignores valid references when using non-default tracker' do expect(project).to receive(:get_issue).with(issue.iid).and_return(nil) - exp = act = "Issue ##{issue.iid}" + exp = act = "Issue #{reference}" expect(filter(act).to_html).to eq exp end @@ -46,9 +45,9 @@ module Gitlab::Markdown end it 'ignores invalid issue IDs' do - exp = act = "Fixed ##{issue.iid + 1}" + invalid = invalidate_reference(reference) + exp = act = "Fixed #{invalid}" - expect(project).to receive(:get_issue).with(issue.iid + 1).and_return(nil) expect(filter(act).to_html).to eq exp end @@ -92,7 +91,7 @@ module Gitlab::Markdown let(:namespace) { create(:namespace, name: 'cross-reference') } let(:project2) { create(:empty_project, namespace: namespace) } let(:issue) { create(:issue, project: project2) } - let(:reference) { "#{project2.path_with_namespace}##{issue.iid}" } + let(:reference) { issue.to_reference(project) } context 'when user can access reference' do before { allow_cross_reference! } @@ -101,7 +100,7 @@ module Gitlab::Markdown expect_any_instance_of(Project).to receive(:get_issue). with(issue.iid).and_return(nil) - exp = act = "Issue ##{issue.iid}" + exp = act = "Issue #{reference}" expect(filter(act).to_html).to eq exp end @@ -118,7 +117,7 @@ module Gitlab::Markdown end it 'ignores invalid issue IDs on the referenced project' do - exp = act = "Fixed #{project2.path_with_namespace}##{issue.iid + 1}" + exp = act = "Fixed #{invalidate_reference(reference)}" expect(filter(act).to_html).to eq exp end diff --git a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb index c4548e7431f..250a44d575d 100644 --- a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb @@ -7,11 +7,10 @@ module Gitlab::Markdown let(:project) { create(:empty_project) } let(:label) { create(:label, project: project) } - let(:reference) { "~#{label.id}" } + let(:reference) { label.to_reference } it 'requires project context' do - expect { described_class.call('Label ~123', {}) }. - to raise_error(ArgumentError, /:project/) + expect { described_class.call('') }.to raise_error(ArgumentError, /:project/) end %w(pre code a style).each do |elem| @@ -36,7 +35,7 @@ module Gitlab::Markdown link = doc.css('a').first.attr('href') expect(link).not_to match %r(https?://) - expect(link).to eq urls.namespace_project_issues_url(project.namespace, project, label_name: label.name, only_path: true) + expect(link).to eq urls.namespace_project_issues_path(project.namespace, project, label_name: label.name) end it 'adds to the results hash' do @@ -70,7 +69,7 @@ module Gitlab::Markdown end it 'ignores invalid label IDs' do - exp = act = "Label ~#{label.id + 1}" + exp = act = "Label #{invalidate_reference(reference)}" expect(filter(act).to_html).to eq exp end @@ -78,7 +77,7 @@ module Gitlab::Markdown context 'String-based single-word references' do let(:label) { create(:label, name: 'gfm', project: project) } - let(:reference) { "~#{label.name}" } + let(:reference) { "#{Label.reference_prefix}#{label.name}" } it 'links to a valid reference' do doc = filter("See #{reference}") @@ -94,7 +93,7 @@ module Gitlab::Markdown end it 'ignores invalid label names' do - exp = act = "Label ~#{label.name.reverse}" + exp = act = "Label #{Label.reference_prefix}#{label.name.reverse}" expect(filter(act).to_html).to eq exp end @@ -104,7 +103,7 @@ module Gitlab::Markdown let(:label) { create(:label, name: 'gfm references', project: project) } context 'in single quotes' do - let(:reference) { "~'#{label.name}'" } + let(:reference) { "#{Label.reference_prefix}'#{label.name}'" } it 'links to a valid reference' do doc = filter("See #{reference}") @@ -120,14 +119,14 @@ module Gitlab::Markdown end it 'ignores invalid label names' do - exp = act = "Label ~'#{label.name.reverse}'" + exp = act = "Label #{Label.reference_prefix}'#{label.name.reverse}'" expect(filter(act).to_html).to eq exp end end context 'in double quotes' do - let(:reference) { %(~"#{label.name}") } + let(:reference) { %(#{Label.reference_prefix}"#{label.name}") } it 'links to a valid reference' do doc = filter("See #{reference}") @@ -143,7 +142,7 @@ module Gitlab::Markdown end it 'ignores invalid label names' do - exp = act = %(Label ~"#{label.name.reverse}") + exp = act = %(Label #{Label.reference_prefix}"#{label.name.reverse}") expect(filter(act).to_html).to eq exp end diff --git a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb index d6e745114f2..6aeb1093602 100644 --- a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb @@ -8,19 +8,18 @@ module Gitlab::Markdown let(:merge) { create(:merge_request, source_project: project) } it 'requires project context' do - expect { described_class.call('MergeRequest !123', {}) }. - to raise_error(ArgumentError, /:project/) + expect { described_class.call('') }.to raise_error(ArgumentError, /:project/) end %w(pre code a style).each do |elem| it "ignores valid references contained inside '#{elem}' element" do - exp = act = "<#{elem}>Merge !#{merge.iid}" + exp = act = "<#{elem}>Merge #{merge.to_reference}" expect(filter(act).to_html).to eq exp end end context 'internal reference' do - let(:reference) { "!#{merge.iid}" } + let(:reference) { merge.to_reference } it 'links to a valid reference' do doc = filter("See #{reference}") @@ -35,7 +34,7 @@ module Gitlab::Markdown end it 'ignores invalid merge IDs' do - exp = act = "Merge !#{merge.iid + 1}" + exp = act = "Merge #{invalidate_reference(reference)}" expect(filter(act).to_html).to eq exp end @@ -80,7 +79,7 @@ module Gitlab::Markdown let(:namespace) { create(:namespace, name: 'cross-reference') } let(:project2) { create(:project, namespace: namespace) } let(:merge) { create(:merge_request, source_project: project2) } - let(:reference) { "#{project2.path_with_namespace}!#{merge.iid}" } + let(:reference) { merge.to_reference(project) } context 'when user can access reference' do before { allow_cross_reference! } @@ -99,7 +98,7 @@ module Gitlab::Markdown end it 'ignores invalid merge IDs on the referenced project' do - exp = act = "Merge #{project2.path_with_namespace}!#{merge.iid + 1}" + exp = act = "Merge #{invalidate_reference(reference)}" expect(filter(act).to_html).to eq exp end diff --git a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb index a4b331157af..07ece66e903 100644 --- a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb @@ -6,11 +6,10 @@ module Gitlab::Markdown let(:project) { create(:empty_project) } let(:snippet) { create(:project_snippet, project: project) } - let(:reference) { "$#{snippet.id}" } + let(:reference) { snippet.to_reference } it 'requires project context' do - expect { described_class.call('Snippet $123', {}) }. - to raise_error(ArgumentError, /:project/) + expect { described_class.call('') }.to raise_error(ArgumentError, /:project/) end %w(pre code a style).each do |elem| @@ -34,7 +33,7 @@ module Gitlab::Markdown end it 'ignores invalid snippet IDs' do - exp = act = "Snippet $#{snippet.id + 1}" + exp = act = "Snippet #{invalidate_reference(reference)}" expect(filter(act).to_html).to eq exp end @@ -79,7 +78,7 @@ module Gitlab::Markdown let(:namespace) { create(:namespace, name: 'cross-reference') } let(:project2) { create(:empty_project, namespace: namespace) } let(:snippet) { create(:project_snippet, project: project2) } - let(:reference) { "#{project2.path_with_namespace}$#{snippet.id}" } + let(:reference) { snippet.to_reference(project) } context 'when user can access reference' do before { allow_cross_reference! } @@ -97,7 +96,7 @@ module Gitlab::Markdown end it 'ignores invalid snippet IDs on the referenced project' do - exp = act = "See #{project2.path_with_namespace}$#{snippet.id + 1}" + exp = act = "See #{invalidate_reference(reference)}" expect(filter(act).to_html).to eq exp end diff --git a/spec/lib/gitlab/markdown/user_reference_filter_spec.rb b/spec/lib/gitlab/markdown/user_reference_filter_spec.rb index 922502ada33..0ecbdee9b9e 100644 --- a/spec/lib/gitlab/markdown/user_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/user_reference_filter_spec.rb @@ -4,65 +4,63 @@ module Gitlab::Markdown describe UserReferenceFilter do include ReferenceFilterSpecHelper - let(:project) { create(:empty_project) } - let(:user) { create(:user) } + let(:project) { create(:empty_project) } + let(:user) { create(:user) } + let(:reference) { user.to_reference } it 'requires project context' do - expect { described_class.call('Example @mention', {}) }. - to raise_error(ArgumentError, /:project/) + expect { described_class.call('') }.to raise_error(ArgumentError, /:project/) end it 'ignores invalid users' do - exp = act = 'Hey @somebody' + exp = act = "Hey #{invalidate_reference(reference)}" expect(filter(act).to_html).to eq(exp) end %w(pre code a style).each do |elem| it "ignores valid references contained inside '#{elem}' element" do - exp = act = "<#{elem}>Hey @#{user.username}" + exp = act = "<#{elem}>Hey #{reference}" expect(filter(act).to_html).to eq exp end end context 'mentioning @all' do + let(:reference) { User.reference_prefix + 'all' } + before do project.team << [project.creator, :developer] end it 'supports a special @all mention' do - doc = filter("Hey @all") + doc = filter("Hey #{reference}") expect(doc.css('a').length).to eq 1 expect(doc.css('a').first.attr('href')) .to eq urls.namespace_project_url(project.namespace, project) end it 'adds to the results hash' do - result = pipeline_result('Hey @all') + result = pipeline_result("Hey #{reference}") expect(result[:references][:user]).to eq [project.creator] end end context 'mentioning a user' do - let(:reference) { "@#{user.username}" } - it 'links to a User' do doc = filter("Hey #{reference}") expect(doc.css('a').first.attr('href')).to eq urls.user_url(user) end - # TODO (rspeicher): This test might be overkill it 'links to a User with a period' do user = create(:user, name: 'alphA.Beta') - doc = filter("Hey @#{user.username}") + doc = filter("Hey #{user.to_reference}") expect(doc.css('a').length).to eq 1 end - # TODO (rspeicher): This test might be overkill it 'links to a User with an underscore' do user = create(:user, name: 'ping_pong_king') - doc = filter("Hey @#{user.username}") + doc = filter("Hey #{user.to_reference}") expect(doc.css('a').length).to eq 1 end @@ -73,10 +71,9 @@ module Gitlab::Markdown end context 'mentioning a group' do - let(:group) { create(:group) } - let(:user) { create(:user) } - - let(:reference) { "@#{group.name}" } + let(:group) { create(:group) } + let(:user) { create(:user) } + let(:reference) { group.to_reference } context 'that the current user can read' do before do @@ -108,23 +105,23 @@ module Gitlab::Markdown end it 'links with adjacent text' do - skip 'TODO (rspeicher): Re-enable when usernames can\'t end in periods.' - doc = filter("Mention me (@#{user.username}.)") - expect(doc.to_html).to match(/\(@#{user.username}<\/a>\.\)/) + skip "TODO (rspeicher): Re-enable when usernames can't end in periods." + doc = filter("Mention me (#{reference}.)") + expect(doc.to_html).to match(/\(#{reference}<\/a>\.\)/) end it 'includes default classes' do - doc = filter("Hey @#{user.username}") + doc = filter("Hey #{reference}") expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-project_member' end it 'includes an optional custom class' do - doc = filter("Hey @#{user.username}", reference_class: 'custom') + doc = filter("Hey #{reference}", reference_class: 'custom') expect(doc.css('a').first.attr('class')).to include 'custom' end it 'supports an :only_path context' do - doc = filter("Hey @#{user.username}", only_path: true) + doc = filter("Hey #{reference}", only_path: true) link = doc.css('a').first.attr('href') expect(link).not_to match %r(https?://) diff --git a/spec/support/reference_filter_spec_helper.rb b/spec/support/reference_filter_spec_helper.rb index d2766615ccb..afbea55ab99 100644 --- a/spec/support/reference_filter_spec_helper.rb +++ b/spec/support/reference_filter_spec_helper.rb @@ -10,9 +10,10 @@ module ReferenceFilterSpecHelper Rails.application.routes.url_helpers end - # Modify a reference to make it invalid + # Modify a String reference to make it invalid # - # Commit SHAs get reversed, IDs get incremented by 1 + # Commit SHAs get reversed, IDs get incremented by 1, all other Strings get + # their word characters reversed. # # reference - String reference to modify # @@ -25,7 +26,7 @@ module ReferenceFilterSpecHelper # SHA-based reference with optional prefix reference.gsub(/\h{6,40}\z/) { |v| v.reverse } else - reference + reference.gsub(/\w+\z/) { |v| v.reverse } end end -- cgit v1.2.1 From b88da58cb6272a86b6df2e4efe392f10e689a6b2 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 14 May 2015 16:59:39 -0400 Subject: Add `reference_pattern` to Referable models --- app/models/commit.rb | 13 +++++++++++++ app/models/commit_range.rb | 18 ++++++++++++++++-- app/models/concerns/referable.rb | 10 ++++++++++ app/models/external_issue.rb | 13 +++++++++---- app/models/group.rb | 6 +++++- app/models/issue.rb | 14 ++++++++++++-- app/models/label.rb | 16 ++++++++++++++++ app/models/merge_request.rb | 10 ++++++++++ app/models/project.rb | 5 +++++ app/models/snippet.rb | 10 ++++++++++ app/models/user.rb | 8 ++++++++ lib/gitlab/markdown/commit_range_reference_filter.rb | 9 ++------- lib/gitlab/markdown/commit_reference_filter.rb | 11 ++--------- lib/gitlab/markdown/cross_project_reference.rb | 3 --- lib/gitlab/markdown/external_issue_reference_filter.rb | 7 ++----- lib/gitlab/markdown/issue_reference_filter.rb | 9 ++------- lib/gitlab/markdown/label_reference_filter.rb | 17 ++--------------- lib/gitlab/markdown/merge_request_reference_filter.rb | 9 ++------- lib/gitlab/markdown/snippet_reference_filter.rb | 9 ++------- lib/gitlab/markdown/user_reference_filter.rb | 7 ++----- 20 files changed, 130 insertions(+), 74 deletions(-) diff --git a/app/models/commit.rb b/app/models/commit.rb index 085f4e6398f..2c244fc0410 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -62,6 +62,19 @@ class Commit (self.class === other) && (raw == other.raw) end + def self.reference_prefix + '@' + end + + # Pattern used to extract commit references from text + # + # The SHA can be between 6 and 40 hex characters. + # + # This pattern supports cross-project references. + def self.reference_pattern + %r{(?:#{Project.reference_pattern}#{reference_prefix})?(?\h{6,40})} + end + def to_reference(from_project = nil) if cross_project_reference?(from_project) "#{project.to_reference}@#{id}" diff --git a/app/models/commit_range.rb b/app/models/commit_range.rb index fb1f6d09be6..86fc9eb01a3 100644 --- a/app/models/commit_range.rb +++ b/app/models/commit_range.rb @@ -29,10 +29,24 @@ class CommitRange # See `exclude_start?` attr_reader :exclude_start - # The beginning and ending SHA sums can be between 6 and 40 hex characters, - # and the range selection can be double- or triple-dot. + # The beginning and ending SHAs can be between 6 and 40 hex characters, and + # the range notation can be double- or triple-dot. PATTERN = /\h{6,40}\.{2,3}\h{6,40}/ + def self.reference_prefix + '@' + end + + # Pattern used to extract commit range references from text + # + # This pattern supports cross-project references. + def self.reference_pattern + %r{ + (?:#{Project.reference_pattern}#{reference_prefix})? + (?#{PATTERN}) + }x + end + # Initialize a CommitRange # # range_string - The String commit range. diff --git a/app/models/concerns/referable.rb b/app/models/concerns/referable.rb index b41df301c3f..e3c1c6d268e 100644 --- a/app/models/concerns/referable.rb +++ b/app/models/concerns/referable.rb @@ -35,6 +35,16 @@ module Referable def reference_prefix '' end + + # Regexp pattern used to match references to this object + # + # This must be overridden by the including class. + # + # Returns Regexp + def reference_pattern + raise NotImplementedError, + %Q{#{self} does not implement "reference_pattern"} + end end private diff --git a/app/models/external_issue.rb b/app/models/external_issue.rb index 6fda4a2ab77..49f6c95e045 100644 --- a/app/models/external_issue.rb +++ b/app/models/external_issue.rb @@ -9,10 +9,6 @@ class ExternalIssue @issue_identifier.to_s end - def to_reference(_from_project = nil) - id - end - def id @issue_identifier.to_s end @@ -32,4 +28,13 @@ class ExternalIssue def project @project end + + # Pattern used to extract `JIRA-123` issue references from text + def self.reference_pattern + %r{(?([A-Z\-]+-)\d+)} + end + + def to_reference(_from_project = nil) + id + end end diff --git a/app/models/group.rb b/app/models/group.rb index 33d72e0d9ee..b4e908c5602 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -40,7 +40,11 @@ class Group < Namespace end def reference_prefix - '@' + User.reference_prefix + end + + def reference_pattern + User.reference_pattern end end diff --git a/app/models/issue.rb b/app/models/issue.rb index 31803b57b3f..ea6b9329b07 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -50,12 +50,22 @@ class Issue < ActiveRecord::Base state :closed end + def hook_attrs + attributes + end + def self.reference_prefix '#' end - def hook_attrs - attributes + # Pattern used to extract `#123` issue references from text + # + # This pattern supports cross-project references. + def self.reference_pattern + %r{ + #{Project.reference_pattern}? + #{Regexp.escape(reference_prefix)}(?\d+) + }x end def to_reference(from_project = nil) diff --git a/app/models/label.rb b/app/models/label.rb index 013e6bf5978..8980049cef8 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -40,6 +40,22 @@ class Label < ActiveRecord::Base '~' end + # Pattern used to extract label references from text + # + # TODO (rspeicher): Limit to double quotes (meh) or disallow single quotes in label names (bad). + def self.reference_pattern + %r{ + #{reference_prefix} + (?: + (?\d+) | # Integer-based label ID, or + (? + [A-Za-z0-9_-]+ | # String-based single-word label title + ['"][^&\?,]+['"] # String-based multi-word label surrounded in quotes + ) + ) + }x + end + # Returns the String necessary to reference this Label in Markdown # # format - Symbol format to use (default: :id, optional: :name) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 60b0ce6c018..6c90d09b866 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -140,6 +140,16 @@ class MergeRequest < ActiveRecord::Base '!' end + # Pattern used to extract `!123` merge request references from text + # + # This pattern supports cross-project references. + def self.reference_pattern + %r{ + #{Project.reference_pattern}? + #{Regexp.escape(reference_prefix)}(?\d+) + }x + end + def to_reference(from_project = nil) reference = "#{self.class.reference_prefix}#{iid}" diff --git a/app/models/project.rb b/app/models/project.rb index c943114449a..3c9f0dad28b 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -248,6 +248,11 @@ class Project < ActiveRecord::Base order_by(method) end end + + def reference_pattern + name_pattern = Gitlab::Regex::NAMESPACE_REGEX_STR + %r{(?#{name_pattern}/#{name_pattern})} + end end def team diff --git a/app/models/snippet.rb b/app/models/snippet.rb index 8c3167833aa..d1619071f49 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -56,6 +56,16 @@ class Snippet < ActiveRecord::Base '$' end + # Pattern used to extract `$123` snippet references from text + # + # This pattern supports cross-project references. + def self.reference_pattern + %r{ + #{Project.reference_pattern}? + #{Regexp.escape(reference_prefix)}(?\d+) + }x + end + def to_reference(from_project = nil) reference = "#{self.class.reference_prefix}#{id}" diff --git a/app/models/user.rb b/app/models/user.rb index f546dc015c2..50ca4bc5acc 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -253,6 +253,14 @@ class User < ActiveRecord::Base def reference_prefix '@' end + + # Pattern used to extract `@user` user references from text + def reference_pattern + %r{ + #{Regexp.escape(reference_prefix)} + (?#{Gitlab::Regex::NAMESPACE_REGEX_STR}) + }x + end end # diff --git a/lib/gitlab/markdown/commit_range_reference_filter.rb b/lib/gitlab/markdown/commit_range_reference_filter.rb index 8764f7e474f..61591a9914b 100644 --- a/lib/gitlab/markdown/commit_range_reference_filter.rb +++ b/lib/gitlab/markdown/commit_range_reference_filter.rb @@ -19,7 +19,7 @@ module Gitlab # # Returns a String replaced with the return of the block. def self.references_in(text) - text.gsub(COMMIT_RANGE_PATTERN) do |match| + text.gsub(CommitRange.reference_pattern) do |match| yield match, $~[:commit_range], $~[:project] end end @@ -30,13 +30,8 @@ module Gitlab @commit_map = {} end - # Pattern used to extract commit range references from text - # - # This pattern supports cross-project references. - COMMIT_RANGE_PATTERN = /(#{PROJECT_PATTERN}@)?(?#{CommitRange::PATTERN})/ - def call - replace_text_nodes_matching(COMMIT_RANGE_PATTERN) do |content| + replace_text_nodes_matching(CommitRange.reference_pattern) do |content| commit_range_link_filter(content) end end diff --git a/lib/gitlab/markdown/commit_reference_filter.rb b/lib/gitlab/markdown/commit_reference_filter.rb index b20b29f5d0c..f6932e76e70 100644 --- a/lib/gitlab/markdown/commit_reference_filter.rb +++ b/lib/gitlab/markdown/commit_reference_filter.rb @@ -19,20 +19,13 @@ module Gitlab # # Returns a String replaced with the return of the block. def self.references_in(text) - text.gsub(COMMIT_PATTERN) do |match| + text.gsub(Commit.reference_pattern) do |match| yield match, $~[:commit], $~[:project] end end - # Pattern used to extract commit references from text - # - # The SHA1 sum can be between 6 and 40 hex characters. - # - # This pattern supports cross-project references. - COMMIT_PATTERN = /(#{PROJECT_PATTERN}@)?(?\h{6,40})/ - def call - replace_text_nodes_matching(COMMIT_PATTERN) do |content| + replace_text_nodes_matching(Commit.reference_pattern) do |content| commit_link_filter(content) end end diff --git a/lib/gitlab/markdown/cross_project_reference.rb b/lib/gitlab/markdown/cross_project_reference.rb index c436fabd658..66c256c5104 100644 --- a/lib/gitlab/markdown/cross_project_reference.rb +++ b/lib/gitlab/markdown/cross_project_reference.rb @@ -3,9 +3,6 @@ module Gitlab # Common methods for ReferenceFilters that support an optional cross-project # reference. module CrossProjectReference - NAMING_PATTERN = Gitlab::Regex::NAMESPACE_REGEX_STR - PROJECT_PATTERN = "(?#{NAMING_PATTERN}/#{NAMING_PATTERN})" - # Given a cross-project reference string, get the Project record # # Defaults to value of `context[:project]` if: diff --git a/lib/gitlab/markdown/external_issue_reference_filter.rb b/lib/gitlab/markdown/external_issue_reference_filter.rb index 0fc3f4cca06..2e74c6e45e2 100644 --- a/lib/gitlab/markdown/external_issue_reference_filter.rb +++ b/lib/gitlab/markdown/external_issue_reference_filter.rb @@ -16,19 +16,16 @@ module Gitlab # # Returns a String replaced with the return of the block. def self.references_in(text) - text.gsub(ISSUE_PATTERN) do |match| + text.gsub(ExternalIssue.reference_pattern) do |match| yield match, $~[:issue] end end - # Pattern used to extract `JIRA-123` issue references from text - ISSUE_PATTERN = /(?([A-Z\-]+-)\d+)/ - def call # Early return if the project isn't using an external tracker return doc if project.nil? || project.default_issues_tracker? - replace_text_nodes_matching(ISSUE_PATTERN) do |content| + replace_text_nodes_matching(ExternalIssue.reference_pattern) do |content| issue_link_filter(content) end end diff --git a/lib/gitlab/markdown/issue_reference_filter.rb b/lib/gitlab/markdown/issue_reference_filter.rb index 1e885615163..2815626e247 100644 --- a/lib/gitlab/markdown/issue_reference_filter.rb +++ b/lib/gitlab/markdown/issue_reference_filter.rb @@ -20,18 +20,13 @@ module Gitlab # # Returns a String replaced with the return of the block. def self.references_in(text) - text.gsub(ISSUE_PATTERN) do |match| + text.gsub(Issue.reference_pattern) do |match| yield match, $~[:issue].to_i, $~[:project] end end - # Pattern used to extract `#123` issue references from text - # - # This pattern supports cross-project references. - ISSUE_PATTERN = /#{PROJECT_PATTERN}?\#(?([a-zA-Z\-]+-)?\d+)/ - def call - replace_text_nodes_matching(ISSUE_PATTERN) do |content| + replace_text_nodes_matching(Issue.reference_pattern) do |content| issue_link_filter(content) end end diff --git a/lib/gitlab/markdown/label_reference_filter.rb b/lib/gitlab/markdown/label_reference_filter.rb index 1a77becee89..9f8c85b7012 100644 --- a/lib/gitlab/markdown/label_reference_filter.rb +++ b/lib/gitlab/markdown/label_reference_filter.rb @@ -15,26 +15,13 @@ module Gitlab # # Returns a String replaced with the return of the block. def self.references_in(text) - text.gsub(LABEL_PATTERN) do |match| + text.gsub(Label.reference_pattern) do |match| yield match, $~[:label_id].to_i, $~[:label_name] end end - # Pattern used to extract label references from text - # - # TODO (rspeicher): Limit to double quotes (meh) or disallow single quotes in label names (bad). - LABEL_PATTERN = %r{ - ~( - (?\d+) | # Integer-based label ID, or - (? - [A-Za-z0-9_-]+ | # String-based single-word label title - ['"][^&\?,]+['"] # String-based multi-word label surrounded in quotes - ) - ) - }x - def call - replace_text_nodes_matching(LABEL_PATTERN) do |content| + replace_text_nodes_matching(Label.reference_pattern) do |content| label_link_filter(content) end end diff --git a/lib/gitlab/markdown/merge_request_reference_filter.rb b/lib/gitlab/markdown/merge_request_reference_filter.rb index 740d72abb36..fddc050635f 100644 --- a/lib/gitlab/markdown/merge_request_reference_filter.rb +++ b/lib/gitlab/markdown/merge_request_reference_filter.rb @@ -20,18 +20,13 @@ module Gitlab # # Returns a String replaced with the return of the block. def self.references_in(text) - text.gsub(MERGE_REQUEST_PATTERN) do |match| + text.gsub(MergeRequest.reference_pattern) do |match| yield match, $~[:merge_request].to_i, $~[:project] end end - # Pattern used to extract `!123` merge request references from text - # - # This pattern supports cross-project references. - MERGE_REQUEST_PATTERN = /#{PROJECT_PATTERN}?!(?\d+)/ - def call - replace_text_nodes_matching(MERGE_REQUEST_PATTERN) do |content| + replace_text_nodes_matching(MergeRequest.reference_pattern) do |content| merge_request_link_filter(content) end end diff --git a/lib/gitlab/markdown/snippet_reference_filter.rb b/lib/gitlab/markdown/snippet_reference_filter.rb index 64a0a2696f7..f22f08de27c 100644 --- a/lib/gitlab/markdown/snippet_reference_filter.rb +++ b/lib/gitlab/markdown/snippet_reference_filter.rb @@ -20,18 +20,13 @@ module Gitlab # # Returns a String replaced with the return of the block. def self.references_in(text) - text.gsub(SNIPPET_PATTERN) do |match| + text.gsub(Snippet.reference_pattern) do |match| yield match, $~[:snippet].to_i, $~[:project] end end - # Pattern used to extract `$123` snippet references from text - # - # This pattern supports cross-project references. - SNIPPET_PATTERN = /#{PROJECT_PATTERN}?\$(?\d+)/ - def call - replace_text_nodes_matching(SNIPPET_PATTERN) do |content| + replace_text_nodes_matching(Snippet.reference_pattern) do |content| snippet_link_filter(content) end end diff --git a/lib/gitlab/markdown/user_reference_filter.rb b/lib/gitlab/markdown/user_reference_filter.rb index 28ec041b1d4..ca7fd7b0338 100644 --- a/lib/gitlab/markdown/user_reference_filter.rb +++ b/lib/gitlab/markdown/user_reference_filter.rb @@ -16,16 +16,13 @@ module Gitlab # # Returns a String replaced with the return of the block. def self.references_in(text) - text.gsub(USER_PATTERN) do |match| + text.gsub(User.reference_pattern) do |match| yield match, $~[:user] end end - # Pattern used to extract `@user` user references from text - USER_PATTERN = /@(?#{Gitlab::Regex::NAMESPACE_REGEX_STR})/ - def call - replace_text_nodes_matching(USER_PATTERN) do |content| + replace_text_nodes_matching(User.reference_pattern) do |content| user_link_filter(content) end end -- cgit v1.2.1 From 1a277c502590fbdb652f73ec3c0974ff70ea5416 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 14 May 2015 17:09:02 -0400 Subject: Minor documentation updates --- app/models/concerns/mentionable.rb | 2 +- app/models/concerns/referable.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb index f28b20afd8e..9b299889476 100644 --- a/app/models/concerns/mentionable.rb +++ b/app/models/concerns/mentionable.rb @@ -25,7 +25,7 @@ module Mentionable # By default this will be the class name and the result of calling # `to_reference` on the object. def gfm_reference - # Convert "MergeRequest" to "merge request" + # "MergeRequest" > "merge_request" > "Merge request" > "merge request" friendly_name = self.class.to_s.underscore.humanize.downcase "#{friendly_name} #{to_reference}" diff --git a/app/models/concerns/referable.rb b/app/models/concerns/referable.rb index e3c1c6d268e..5f57846b589 100644 --- a/app/models/concerns/referable.rb +++ b/app/models/concerns/referable.rb @@ -40,7 +40,7 @@ module Referable # # This must be overridden by the including class. # - # Returns Regexp + # Returns a Regexp def reference_pattern raise NotImplementedError, %Q{#{self} does not implement "reference_pattern"} -- cgit v1.2.1 From 35853033b9516aeffb6d341589406b00016947c3 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 14 May 2015 20:15:06 -0400 Subject: Fix for Snippets with a project --- app/views/shared/snippets/_form.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/snippets/_form.html.haml b/app/views/shared/snippets/_form.html.haml index 9610f9ce414..2feeeecc48b 100644 --- a/app/views/shared/snippets/_form.html.haml +++ b/app/views/shared/snippets/_form.html.haml @@ -29,7 +29,7 @@ - else = f.submit 'Save', class: "btn-save btn" - - if @snippet.respond_to?(:project) + - if @snippet.project_id = link_to "Cancel", namespace_project_snippets_path(@project.namespace, @project), class: "btn btn-cancel" - else = link_to "Cancel", snippets_path(@project), class: "btn btn-cancel" -- cgit v1.2.1 From 81a09bc74cb997d3465f98cdcb72cacd413c31cd Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 15 May 2015 16:07:25 -0400 Subject: Support only double quotes for multi-word label references --- app/models/label.rb | 10 ++-- lib/gitlab/markdown/label_reference_filter.rb | 3 +- .../gitlab/markdown/label_reference_filter_spec.rb | 54 ++++++---------------- spec/models/label_spec.rb | 19 ++++++-- 4 files changed, 33 insertions(+), 53 deletions(-) diff --git a/app/models/label.rb b/app/models/label.rb index 8980049cef8..230631b5180 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -41,16 +41,14 @@ class Label < ActiveRecord::Base end # Pattern used to extract label references from text - # - # TODO (rspeicher): Limit to double quotes (meh) or disallow single quotes in label names (bad). def self.reference_pattern %r{ #{reference_prefix} (?: - (?\d+) | # Integer-based label ID, or + (?\d+) | # Integer-based label ID, or (? - [A-Za-z0-9_-]+ | # String-based single-word label title - ['"][^&\?,]+['"] # String-based multi-word label surrounded in quotes + [A-Za-z0-9_-]+ | # String-based single-word label title, or + "[^&\?,]+" # String-based multi-word label surrounded in quotes ) ) }x @@ -70,7 +68,7 @@ class Label < ActiveRecord::Base # # Returns a String def to_reference(format = :id) - if format == :name + if format == :name && !name.include?('"') %(#{self.class.reference_prefix}"#{name}") else "#{self.class.reference_prefix}#{id}" diff --git a/lib/gitlab/markdown/label_reference_filter.rb b/lib/gitlab/markdown/label_reference_filter.rb index 9f8c85b7012..e022ca69c91 100644 --- a/lib/gitlab/markdown/label_reference_filter.rb +++ b/lib/gitlab/markdown/label_reference_filter.rb @@ -72,8 +72,7 @@ module Gitlab # Returns a Hash. def label_params(id, name) if name - # TODO (rspeicher): Don't strip single quotes if we decide to only use double quotes for surrounding. - { name: name.tr('\'"', '') } + { name: name.tr('"', '') } else { id: id } end diff --git a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb index 250a44d575d..41987f57bca 100644 --- a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb @@ -100,52 +100,26 @@ module Gitlab::Markdown end context 'String-based multi-word references in quotes' do - let(:label) { create(:label, name: 'gfm references', project: project) } + let(:label) { create(:label, name: 'gfm references', project: project) } + let(:reference) { label.to_reference(:name) } - context 'in single quotes' do - let(:reference) { "#{Label.reference_prefix}'#{label.name}'" } - - it 'links to a valid reference' do - doc = filter("See #{reference}") - - expect(doc.css('a').first.attr('href')).to eq urls. - namespace_project_issues_url(project.namespace, project, label_name: label.name) - expect(doc.text).to eq 'See gfm references' - end - - it 'links with adjacent text' do - doc = filter("Label (#{reference}.)") - expect(doc.to_html).to match(%r(\(#{label.name}\.\))) - end - - it 'ignores invalid label names' do - exp = act = "Label #{Label.reference_prefix}'#{label.name.reverse}'" + it 'links to a valid reference' do + doc = filter("See #{reference}") - expect(filter(act).to_html).to eq exp - end + expect(doc.css('a').first.attr('href')).to eq urls. + namespace_project_issues_url(project.namespace, project, label_name: label.name) + expect(doc.text).to eq 'See gfm references' end - context 'in double quotes' do - let(:reference) { %(#{Label.reference_prefix}"#{label.name}") } - - it 'links to a valid reference' do - doc = filter("See #{reference}") - - expect(doc.css('a').first.attr('href')).to eq urls. - namespace_project_issues_url(project.namespace, project, label_name: label.name) - expect(doc.text).to eq 'See gfm references' - end - - it 'links with adjacent text' do - doc = filter("Label (#{reference}.)") - expect(doc.to_html).to match(%r(\(#{label.name}\.\))) - end + it 'links with adjacent text' do + doc = filter("Label (#{reference}.)") + expect(doc.to_html).to match(%r(\(#{label.name}\.\))) + end - it 'ignores invalid label names' do - exp = act = %(Label #{Label.reference_prefix}"#{label.name.reverse}") + it 'ignores invalid label names' do + exp = act = %(Label #{Label.reference_prefix}"#{label.name.reverse}") - expect(filter(act).to_html).to eq exp - end + expect(filter(act).to_html).to eq exp end end diff --git a/spec/models/label_spec.rb b/spec/models/label_spec.rb index a13f9ac926c..6518213d71c 100644 --- a/spec/models/label_spec.rb +++ b/spec/models/label_spec.rb @@ -55,13 +55,22 @@ describe Label do end describe '#to_reference' do - it 'returns a String reference to the object' do - expect(label.to_reference).to eq "~#{label.id}" - expect(label.to_reference(double)).to eq "~#{label.id}" + context 'using id' do + it 'returns a String reference to the object' do + expect(label.to_reference).to eq "~#{label.id}" + expect(label.to_reference(double('project'))).to eq "~#{label.id}" + end end - it 'returns a String reference to the object using its name' do - expect(label.to_reference(:name)).to eq %(~"#{label.name}") + context 'using name' do + it 'returns a String reference to the object' do + expect(label.to_reference(:name)).to eq %(~"#{label.name}") + end + + it 'uses id when name contains double quote' do + label = create(:label, name: %q{"irony"}) + expect(label.to_reference(:name)).to eq "~#{label.id}" + end end end end -- cgit v1.2.1 From 5cc9b17b8a7d7a8081fa60ea75f6cf423fbddbc5 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 15 May 2015 16:09:17 -0400 Subject: Make `cross_project_reference?` less magical --- app/models/concerns/referable.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/models/concerns/referable.rb b/app/models/concerns/referable.rb index 5f57846b589..cced66cc1e4 100644 --- a/app/models/concerns/referable.rb +++ b/app/models/concerns/referable.rb @@ -42,8 +42,7 @@ module Referable # # Returns a Regexp def reference_pattern - raise NotImplementedError, - %Q{#{self} does not implement "reference_pattern"} + raise NotImplementedError, "#{self} does not implement #{__method__}" end end @@ -53,10 +52,10 @@ module Referable # # from_project - Refering Project object def cross_project_reference?(from_project) - if Project === self + if self.is_a?(Project) self != from_project else - from_project && project && project != from_project + from_project && self.project && self.project != from_project end end end -- cgit v1.2.1 From 1a9da9178cfd25190997b621e428a5c7ce467cd1 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 15 May 2015 16:10:55 -0400 Subject: Surround Project.reference_pattern in parenthesis inside other patterns --- app/models/commit.rb | 5 ++++- app/models/issue.rb | 2 +- app/models/merge_request.rb | 2 +- app/models/snippet.rb | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/app/models/commit.rb b/app/models/commit.rb index 2c244fc0410..f02fe240540 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -72,7 +72,10 @@ class Commit # # This pattern supports cross-project references. def self.reference_pattern - %r{(?:#{Project.reference_pattern}#{reference_prefix})?(?\h{6,40})} + %r{ + (?:#{Project.reference_pattern}#{reference_prefix})? + (?\h{6,40}) + }x end def to_reference(from_project = nil) diff --git a/app/models/issue.rb b/app/models/issue.rb index ea6b9329b07..2456b7d0dc1 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -63,7 +63,7 @@ class Issue < ActiveRecord::Base # This pattern supports cross-project references. def self.reference_pattern %r{ - #{Project.reference_pattern}? + (#{Project.reference_pattern})? #{Regexp.escape(reference_prefix)}(?\d+) }x end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 6c90d09b866..c57016dd6a2 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -145,7 +145,7 @@ class MergeRequest < ActiveRecord::Base # This pattern supports cross-project references. def self.reference_pattern %r{ - #{Project.reference_pattern}? + (#{Project.reference_pattern})? #{Regexp.escape(reference_prefix)}(?\d+) }x end diff --git a/app/models/snippet.rb b/app/models/snippet.rb index d1619071f49..3ab9e834c63 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -61,7 +61,7 @@ class Snippet < ActiveRecord::Base # This pattern supports cross-project references. def self.reference_pattern %r{ - #{Project.reference_pattern}? + (#{Project.reference_pattern})? #{Regexp.escape(reference_prefix)}(?\d+) }x end -- cgit v1.2.1 From 68f74aa82690ca83faf81654737f7d0caefdfbd3 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 15 May 2015 16:12:23 -0400 Subject: Add a note about the commented-out test in Markdown Feature --- spec/features/markdown_spec.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb index 0fc144462f2..d6954174660 100644 --- a/spec/features/markdown_spec.rb +++ b/spec/features/markdown_spec.rb @@ -66,6 +66,10 @@ describe 'GitLab Markdown' do @doc.at_css("##{id}").parent.next_element end + # Sometimes it can be useful to see the parsed output of the Markdown document + # for debugging. Uncomment this block to write the output to + # tmp/capybara/markdown_spec.html. + # # it 'writes to a file' do # File.open(Rails.root.join('tmp/capybara/markdown_spec.html'), 'w') do |file| # file.puts @md -- cgit v1.2.1 From 5a9c5520d9e63a38ed8b839be8430a5b5815da67 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 21 May 2015 16:35:15 -0400 Subject: Make use of to_reference in more specs --- lib/gitlab/closing_issue_extractor.rb | 2 +- .../markdown/external_issue_reference_filter.rb | 2 +- lib/gitlab/markdown/issue_reference_filter.rb | 2 +- .../markdown/merge_request_reference_filter.rb | 2 +- lib/gitlab/markdown/snippet_reference_filter.rb | 2 +- lib/gitlab/markdown/user_reference_filter.rb | 9 ++- spec/features/gitlab_flavored_markdown_spec.rb | 34 +++++------ spec/helpers/gitlab_markdown_helper_spec.rb | 10 ++-- spec/lib/gitlab/closing_issue_extractor_spec.rb | 70 +++++++++++----------- spec/lib/gitlab/reference_extractor_spec.rb | 4 +- spec/models/merge_request_spec.rb | 2 +- spec/services/system_note_service_spec.rb | 10 ++-- 12 files changed, 76 insertions(+), 73 deletions(-) diff --git a/lib/gitlab/closing_issue_extractor.rb b/lib/gitlab/closing_issue_extractor.rb index ab184d95c05..aeec595782c 100644 --- a/lib/gitlab/closing_issue_extractor.rb +++ b/lib/gitlab/closing_issue_extractor.rb @@ -8,7 +8,7 @@ module Gitlab def closed_by_message(message) return [] if message.nil? - + closing_statements = message.scan(ISSUE_CLOSING_REGEX). map { |ref| ref[0] }.join(" ") diff --git a/lib/gitlab/markdown/external_issue_reference_filter.rb b/lib/gitlab/markdown/external_issue_reference_filter.rb index 2e74c6e45e2..afd28dd8cf3 100644 --- a/lib/gitlab/markdown/external_issue_reference_filter.rb +++ b/lib/gitlab/markdown/external_issue_reference_filter.rb @@ -48,7 +48,7 @@ module Gitlab %(#{issue}) + class="#{klass}">#{match}) end end diff --git a/lib/gitlab/markdown/issue_reference_filter.rb b/lib/gitlab/markdown/issue_reference_filter.rb index 2815626e247..dea04761ead 100644 --- a/lib/gitlab/markdown/issue_reference_filter.rb +++ b/lib/gitlab/markdown/issue_reference_filter.rb @@ -52,7 +52,7 @@ module Gitlab %(#{project_ref}##{id}) + class="#{klass}">#{match}) else match end diff --git a/lib/gitlab/markdown/merge_request_reference_filter.rb b/lib/gitlab/markdown/merge_request_reference_filter.rb index fddc050635f..80779819485 100644 --- a/lib/gitlab/markdown/merge_request_reference_filter.rb +++ b/lib/gitlab/markdown/merge_request_reference_filter.rb @@ -52,7 +52,7 @@ module Gitlab %(#{project_ref}!#{id}) + class="#{klass}">#{match}) else match end diff --git a/lib/gitlab/markdown/snippet_reference_filter.rb b/lib/gitlab/markdown/snippet_reference_filter.rb index f22f08de27c..174ba58af6c 100644 --- a/lib/gitlab/markdown/snippet_reference_filter.rb +++ b/lib/gitlab/markdown/snippet_reference_filter.rb @@ -52,7 +52,7 @@ module Gitlab %(#{project_ref}$#{id}) + class="#{klass}">#{match}) else match end diff --git a/lib/gitlab/markdown/user_reference_filter.rb b/lib/gitlab/markdown/user_reference_filter.rb index ca7fd7b0338..c9972957182 100644 --- a/lib/gitlab/markdown/user_reference_filter.rb +++ b/lib/gitlab/markdown/user_reference_filter.rb @@ -65,7 +65,8 @@ module Gitlab url = urls.namespace_project_url(project.namespace, project, only_path: context[:only_path]) - %(@all) + text = User.reference_prefix + 'all' + %(#{text}) end def link_to_namespace(namespace) @@ -83,7 +84,8 @@ module Gitlab url = urls.group_url(group, only_path: context[:only_path]) - %(@#{group}) + text = Group.reference_prefix + group + %(#{text}) end def link_to_user(user, namespace) @@ -91,7 +93,8 @@ module Gitlab url = urls.user_url(user, only_path: context[:only_path]) - %(@#{user}) + text = User.reference_prefix + user + %(#{text}) end def user_can_reference_group?(group) diff --git a/spec/features/gitlab_flavored_markdown_spec.rb b/spec/features/gitlab_flavored_markdown_spec.rb index 133beba7b98..16d1ca55f8d 100644 --- a/spec/features/gitlab_flavored_markdown_spec.rb +++ b/spec/features/gitlab_flavored_markdown_spec.rb @@ -11,7 +11,7 @@ describe "GitLab Flavored Markdown", feature: true do end before do - Commit.any_instance.stub(title: "fix ##{issue.iid}\n\nask @#{fred.username} for details") + Commit.any_instance.stub(title: "fix #{issue.to_reference}\n\nask #{fred.to_reference} for details") end let(:commit) { project.commit } @@ -25,25 +25,25 @@ describe "GitLab Flavored Markdown", feature: true do it "should render title in commits#index" do visit namespace_project_commits_path(project.namespace, project, 'master', limit: 1) - expect(page).to have_link("##{issue.iid}") + expect(page).to have_link(issue.to_reference) end it "should render title in commits#show" do visit namespace_project_commit_path(project.namespace, project, commit) - expect(page).to have_link("##{issue.iid}") + expect(page).to have_link(issue.to_reference) end it "should render description in commits#show" do visit namespace_project_commit_path(project.namespace, project, commit) - expect(page).to have_link("@#{fred.username}") + expect(page).to have_link(fred.to_reference) end it "should render title in repositories#branches" do visit namespace_project_branches_path(project.namespace, project) - expect(page).to have_link("##{issue.iid}") + expect(page).to have_link(issue.to_reference) end end @@ -57,20 +57,20 @@ describe "GitLab Flavored Markdown", feature: true do author: @user, assignee: @user, project: project, - title: "fix ##{@other_issue.iid}", - description: "ask @#{fred.username} for details") + title: "fix #{@other_issue.to_reference}", + description: "ask #{fred.to_reference} for details") end it "should render subject in issues#index" do visit namespace_project_issues_path(project.namespace, project) - expect(page).to have_link("##{@other_issue.iid}") + expect(page).to have_link(@other_issue.to_reference) end it "should render subject in issues#show" do visit namespace_project_issue_path(project.namespace, project, @issue) - expect(page).to have_link("##{@other_issue.iid}") + expect(page).to have_link(@other_issue.to_reference) end it "should render details in issues#show" do @@ -83,19 +83,19 @@ describe "GitLab Flavored Markdown", feature: true do describe "for merge requests" do before do - @merge_request = create(:merge_request, source_project: project, target_project: project, title: "fix ##{issue.iid}") + @merge_request = create(:merge_request, source_project: project, target_project: project, title: "fix #{issue.to_reference}") end it "should render title in merge_requests#index" do visit namespace_project_merge_requests_path(project.namespace, project) - expect(page).to have_link("##{issue.iid}") + expect(page).to have_link(issue.to_reference) end it "should render title in merge_requests#show" do visit namespace_project_merge_request_path(project.namespace, project, @merge_request) - expect(page).to have_link("##{issue.iid}") + expect(page).to have_link(issue.to_reference) end end @@ -104,26 +104,26 @@ describe "GitLab Flavored Markdown", feature: true do before do @milestone = create(:milestone, project: project, - title: "fix ##{issue.iid}", - description: "ask @#{fred.username} for details") + title: "fix #{issue.to_reference}", + description: "ask #{fred.to_reference} for details") end it "should render title in milestones#index" do visit namespace_project_milestones_path(project.namespace, project) - expect(page).to have_link("##{issue.iid}") + expect(page).to have_link(issue.to_reference) end it "should render title in milestones#show" do visit namespace_project_milestone_path(project.namespace, project, @milestone) - expect(page).to have_link("##{issue.iid}") + expect(page).to have_link(issue.to_reference) end it "should render description in milestones#show" do visit namespace_project_milestone_path(project.namespace, project, @milestone) - expect(page).to have_link("@#{fred.username}") + expect(page).to have_link(fred.to_reference) end end end diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb index 0d0418f84a7..d0b200a9ff8 100644 --- a/spec/helpers/gitlab_markdown_helper_spec.rb +++ b/spec/helpers/gitlab_markdown_helper_spec.rb @@ -26,7 +26,7 @@ describe GitlabMarkdownHelper do end describe "referencing multiple objects" do - let(:actual) { "!#{merge_request.iid} -> #{commit.id} -> ##{issue.iid}" } + let(:actual) { "#{merge_request.to_reference} -> #{commit.to_reference} -> #{issue.to_reference}" } it "should link to the merge request" do expected = namespace_project_merge_request_path(project.namespace, project, merge_request) @@ -50,7 +50,7 @@ describe GitlabMarkdownHelper do let(:issues) { create_list(:issue, 2, project: project) } it 'should handle references nested in links with all the text' do - actual = link_to_gfm("This should finally fix ##{issues[0].iid} and ##{issues[1].iid} for real", commit_path) + actual = link_to_gfm("This should finally fix #{issues[0].to_reference} and #{issues[1].to_reference} for real", commit_path) doc = Nokogiri::HTML.parse(actual) # Make sure we didn't create invalid markup @@ -63,7 +63,7 @@ describe GitlabMarkdownHelper do # First issue link expect(doc.css('a')[1].attr('href')). to eq namespace_project_issue_path(project.namespace, project, issues[0]) - expect(doc.css('a')[1].text).to eq "##{issues[0].iid}" + expect(doc.css('a')[1].text).to eq issues[0].to_reference # Internal commit link expect(doc.css('a')[2].attr('href')).to eq commit_path @@ -72,7 +72,7 @@ describe GitlabMarkdownHelper do # Second issue link expect(doc.css('a')[3].attr('href')). to eq namespace_project_issue_path(project.namespace, project, issues[1]) - expect(doc.css('a')[3].text).to eq "##{issues[1].iid}" + expect(doc.css('a')[3].text).to eq issues[1].to_reference # Trailing commit link expect(doc.css('a')[4].attr('href')).to eq commit_path @@ -90,7 +90,7 @@ describe GitlabMarkdownHelper do end it "escapes HTML passed in as the body" do - actual = "This is a

test

- see ##{issues[0].iid}" + actual = "This is a

test

- see #{issues[0].to_reference}" expect(link_to_gfm(actual, commit_path)). to match('<h1>test</h1>') end diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb index cb7b0fbb890..63d474c0d1e 100644 --- a/spec/lib/gitlab/closing_issue_extractor_spec.rb +++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb @@ -1,131 +1,131 @@ require 'spec_helper' describe Gitlab::ClosingIssueExtractor do - let(:project) { create(:project) } - let(:issue) { create(:issue, project: project) } - let(:iid1) { issue.iid } + let(:project) { create(:project) } + let(:issue) { create(:issue, project: project) } + let(:reference) { issue.to_reference } subject { described_class.new(project, project.creator) } describe "#closed_by_message" do context 'with a single reference' do it do - message = "Awesome commit (Closes ##{iid1})" + message = "Awesome commit (Closes #{reference})" expect(subject.closed_by_message(message)).to eq([issue]) end it do - message = "Awesome commit (closes ##{iid1})" + message = "Awesome commit (closes #{reference})" expect(subject.closed_by_message(message)).to eq([issue]) end it do - message = "Closed ##{iid1}" + message = "Closed #{reference}" expect(subject.closed_by_message(message)).to eq([issue]) end it do - message = "closed ##{iid1}" + message = "closed #{reference}" expect(subject.closed_by_message(message)).to eq([issue]) end it do - message = "Closing ##{iid1}" + message = "Closing #{reference}" expect(subject.closed_by_message(message)).to eq([issue]) end it do - message = "closing ##{iid1}" + message = "closing #{reference}" expect(subject.closed_by_message(message)).to eq([issue]) end it do - message = "Close ##{iid1}" + message = "Close #{reference}" expect(subject.closed_by_message(message)).to eq([issue]) end it do - message = "close ##{iid1}" + message = "close #{reference}" expect(subject.closed_by_message(message)).to eq([issue]) end it do - message = "Awesome commit (Fixes ##{iid1})" + message = "Awesome commit (Fixes #{reference})" expect(subject.closed_by_message(message)).to eq([issue]) end it do - message = "Awesome commit (fixes ##{iid1})" + message = "Awesome commit (fixes #{reference})" expect(subject.closed_by_message(message)).to eq([issue]) end it do - message = "Fixed ##{iid1}" + message = "Fixed #{reference}" expect(subject.closed_by_message(message)).to eq([issue]) end it do - message = "fixed ##{iid1}" + message = "fixed #{reference}" expect(subject.closed_by_message(message)).to eq([issue]) end it do - message = "Fixing ##{iid1}" + message = "Fixing #{reference}" expect(subject.closed_by_message(message)).to eq([issue]) end it do - message = "fixing ##{iid1}" + message = "fixing #{reference}" expect(subject.closed_by_message(message)).to eq([issue]) end it do - message = "Fix ##{iid1}" + message = "Fix #{reference}" expect(subject.closed_by_message(message)).to eq([issue]) end it do - message = "fix ##{iid1}" + message = "fix #{reference}" expect(subject.closed_by_message(message)).to eq([issue]) end it do - message = "Awesome commit (Resolves ##{iid1})" + message = "Awesome commit (Resolves #{reference})" expect(subject.closed_by_message(message)).to eq([issue]) end it do - message = "Awesome commit (resolves ##{iid1})" + message = "Awesome commit (resolves #{reference})" expect(subject.closed_by_message(message)).to eq([issue]) end it do - message = "Resolved ##{iid1}" + message = "Resolved #{reference}" expect(subject.closed_by_message(message)).to eq([issue]) end it do - message = "resolved ##{iid1}" + message = "resolved #{reference}" expect(subject.closed_by_message(message)).to eq([issue]) end it do - message = "Resolving ##{iid1}" + message = "Resolving #{reference}" expect(subject.closed_by_message(message)).to eq([issue]) end it do - message = "resolving ##{iid1}" + message = "resolving #{reference}" expect(subject.closed_by_message(message)).to eq([issue]) end it do - message = "Resolve ##{iid1}" + message = "Resolve #{reference}" expect(subject.closed_by_message(message)).to eq([issue]) end it do - message = "resolve ##{iid1}" + message = "resolve #{reference}" expect(subject.closed_by_message(message)).to eq([issue]) end end @@ -133,40 +133,40 @@ describe Gitlab::ClosingIssueExtractor do context 'with multiple references' do let(:other_issue) { create(:issue, project: project) } let(:third_issue) { create(:issue, project: project) } - let(:iid2) { other_issue.iid } - let(:iid3) { third_issue.iid } + let(:reference2) { other_issue.to_reference } + let(:reference3) { third_issue.to_reference } it 'fetches issues in single line message' do - message = "Closes ##{iid1} and fix ##{iid2}" + message = "Closes #{reference} and fix ##{reference2}" expect(subject.closed_by_message(message)). to eq([issue, other_issue]) end it 'fetches comma-separated issues references in single line message' do - message = "Closes ##{iid1}, closes ##{iid2}" + message = "Closes #{reference}, closes ##{reference2}" expect(subject.closed_by_message(message)). to eq([issue, other_issue]) end it 'fetches comma-separated issues numbers in single line message' do - message = "Closes ##{iid1}, ##{iid2} and ##{iid3}" + message = "Closes #{reference}, ##{reference2} and ##{reference3}" expect(subject.closed_by_message(message)). to eq([issue, other_issue, third_issue]) end it 'fetches issues in multi-line message' do - message = "Awesome commit (closes ##{iid1})\nAlso fixes ##{iid2}" + message = "Awesome commit (closes #{reference})\nAlso fixes ##{reference2}" expect(subject.closed_by_message(message)). to eq([issue, other_issue]) end it 'fetches issues in hybrid message' do - message = "Awesome commit (closes ##{iid1})\n"\ - "Also fixing issues ##{iid2}, ##{iid3} and #4" + message = "Awesome commit (closes #{reference})\n"\ + "Also fixing issues ##{reference2}, ##{reference3} and #4" expect(subject.closed_by_message(message)). to eq([issue, other_issue, third_issue]) diff --git a/spec/lib/gitlab/reference_extractor_spec.rb b/spec/lib/gitlab/reference_extractor_spec.rb index 9801dc16554..c14f4ac6bf6 100644 --- a/spec/lib/gitlab/reference_extractor_spec.rb +++ b/spec/lib/gitlab/reference_extractor_spec.rb @@ -20,7 +20,7 @@ describe Gitlab::ReferenceExtractor do @i0 = create(:issue, project: project) @i1 = create(:issue, project: project) - subject.analyze("##{@i0.iid}, ##{@i1.iid}, and #999.") + subject.analyze("#{@i0.to_reference}, #{@i1.to_reference}, and #{Issue.reference_prefix}999.") expect(subject.issues).to eq([@i0, @i1]) end @@ -82,7 +82,7 @@ describe Gitlab::ReferenceExtractor do end it 'handles project issue references' do - subject.analyze("this refers issue #{other_project.path_with_namespace}##{issue.iid}") + subject.analyze("this refers issue #{issue.to_reference(project)}") extracted = subject.issues expect(extracted.size).to eq(1) expect(extracted).to eq([issue]) diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 57b1b9dfcf0..0465aa34843 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -129,7 +129,7 @@ describe MergeRequest do it 'detects issues mentioned in the description' do issue2 = create(:issue, project: subject.project) - subject.description = "Closes ##{issue2.iid}" + subject.description = "Closes #{issue2.to_reference}" subject.project.stub(default_branch: subject.target_branch) expect(subject.closes_issues).to include(issue2) diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index 4e4cb6d19ed..ec173fa322d 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -238,13 +238,13 @@ describe SystemNoteService do let(:mentioner) { project2.repository.commit } it 'references the mentioning commit' do - expect(subject.note).to eq "mentioned in commit #{project2.path_with_namespace}@#{mentioner.id}" + expect(subject.note).to eq "mentioned in commit #{mentioner.to_reference(project)}" end end context 'from non-Commit' do it 'references the mentioning object' do - expect(subject.note).to eq "mentioned in issue #{project2.path_with_namespace}##{mentioner.iid}" + expect(subject.note).to eq "mentioned in issue #{mentioner.to_reference(project)}" end end end @@ -254,13 +254,13 @@ describe SystemNoteService do let(:mentioner) { project.repository.commit } it 'references the mentioning commit' do - expect(subject.note).to eq "mentioned in commit #{mentioner.id}" + expect(subject.note).to eq "mentioned in commit #{mentioner.to_reference}" end end context 'from non-Commit' do it 'references the mentioning object' do - expect(subject.note).to eq "mentioned in issue ##{mentioner.iid}" + expect(subject.note).to eq "mentioned in issue #{mentioner.to_reference}" end end end @@ -270,7 +270,7 @@ describe SystemNoteService do describe '.cross_reference?' do it 'is truthy when text begins with expected text' do - expect(described_class.cross_reference?('mentioned in issue #1')).to be_truthy + expect(described_class.cross_reference?('mentioned in something')).to be_truthy end it 'is falsey when text does not begin with expected text' do -- cgit v1.2.1 From 2c1bf71793963f53c0921325ced2c7ad44a5fe95 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 26 May 2015 15:55:26 -0400 Subject: Fix ClosingIssueExtractor specs --- spec/lib/gitlab/closing_issue_extractor_spec.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb index 63d474c0d1e..5d7ff4f6122 100644 --- a/spec/lib/gitlab/closing_issue_extractor_spec.rb +++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb @@ -137,28 +137,28 @@ describe Gitlab::ClosingIssueExtractor do let(:reference3) { third_issue.to_reference } it 'fetches issues in single line message' do - message = "Closes #{reference} and fix ##{reference2}" + message = "Closes #{reference} and fix #{reference2}" expect(subject.closed_by_message(message)). to eq([issue, other_issue]) end it 'fetches comma-separated issues references in single line message' do - message = "Closes #{reference}, closes ##{reference2}" + message = "Closes #{reference}, closes #{reference2}" expect(subject.closed_by_message(message)). to eq([issue, other_issue]) end it 'fetches comma-separated issues numbers in single line message' do - message = "Closes #{reference}, ##{reference2} and ##{reference3}" + message = "Closes #{reference}, #{reference2} and #{reference3}" expect(subject.closed_by_message(message)). to eq([issue, other_issue, third_issue]) end it 'fetches issues in multi-line message' do - message = "Awesome commit (closes #{reference})\nAlso fixes ##{reference2}" + message = "Awesome commit (closes #{reference})\nAlso fixes #{reference2}" expect(subject.closed_by_message(message)). to eq([issue, other_issue]) @@ -166,7 +166,7 @@ describe Gitlab::ClosingIssueExtractor do it 'fetches issues in hybrid message' do message = "Awesome commit (closes #{reference})\n"\ - "Also fixing issues ##{reference2}, ##{reference3} and #4" + "Also fixing issues #{reference2}, #{reference3} and #4" expect(subject.closed_by_message(message)). to eq([issue, other_issue, third_issue]) -- cgit v1.2.1 From 3cb6a338466ca9b8e2a831cce306fc6d650231ed Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 26 May 2015 16:30:07 -0400 Subject: More SystemNoteService cleanup --- app/models/concerns/mentionable.rb | 4 +-- app/models/note.rb | 4 +-- app/services/system_note_service.rb | 60 +++++++++++-------------------------- 3 files changed, 22 insertions(+), 46 deletions(-) diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb index 9b299889476..6f9f54d08cc 100644 --- a/app/models/concerns/mentionable.rb +++ b/app/models/concerns/mentionable.rb @@ -24,11 +24,11 @@ module Mentionable # # By default this will be the class name and the result of calling # `to_reference` on the object. - def gfm_reference + def gfm_reference(from_project = nil) # "MergeRequest" > "merge_request" > "Merge request" > "merge request" friendly_name = self.class.to_s.underscore.humanize.downcase - "#{friendly_name} #{to_reference}" + "#{friendly_name} #{to_reference(from_project)}" end # Construct a String that contains possible GFM references. diff --git a/app/models/note.rb b/app/models/note.rb index 6939a7e73a0..d5f716b3de0 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -326,8 +326,8 @@ class Note < ActiveRecord::Base end # Mentionable override. - def gfm_reference - noteable.gfm_reference + def gfm_reference(from_project = nil) + noteable.gfm_reference(from_project) end # Mentionable override. diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb index 0614f8689a4..3d57c35bc1d 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -10,7 +10,7 @@ class SystemNoteService # author - User performing the change # new_commits - Array of Commits added since last push # existing_commits - Array of Commits added in a previous push - # oldrev - TODO (rspeicher): I have no idea what this actually does + # oldrev - Optional String SHA of a previous Commit # # See new_commit_summary and existing_commit_summary. # @@ -138,11 +138,11 @@ class SystemNoteService # # Example Note text: # - # "Mentioned in #1" + # "mentioned in #1" # - # "Mentioned in !2" + # "mentioned in !2" # - # "Mentioned in 54f7727c" + # "mentioned in 54f7727c" # # See cross_reference_note_content. # @@ -150,7 +150,7 @@ class SystemNoteService def self.cross_reference(noteable, mentioner, author) return if cross_reference_disallowed?(noteable, mentioner) - gfm_reference = mentioner_gfm_ref(noteable, mentioner) + gfm_reference = mentioner.gfm_reference(noteable.project) note_options = { project: noteable.project, @@ -181,12 +181,21 @@ class SystemNoteService # # Returns Boolean def self.cross_reference_disallowed?(noteable, mentioner) - return false unless MergeRequest === mentioner - return false unless Commit === noteable + return false unless mentioner.is_a?(MergeRequest) + return false unless noteable.is_a?(Commit) mentioner.commits.include?(noteable) end + # Check if a cross reference to a noteable from a mentioner already exists + # + # This method is used to prevent multiple notes being created for a mention + # when a issue is updated, for example. + # + # noteable - Noteable object being referenced + # mentioner - Mentionable object + # + # Returns Boolean def self.cross_reference_exists?(noteable, mentioner) # Initial scope should be system notes of this noteable type notes = Note.system.where(noteable_type: noteable.class) @@ -198,7 +207,7 @@ class SystemNoteService notes = notes.where(noteable_id: noteable.id) end - gfm_reference = mentioner_gfm_ref(noteable, mentioner, true) + gfm_reference = mentioner.gfm_reference(noteable.project) notes = notes.where(note: cross_reference_note_content(gfm_reference)) notes.count > 0 @@ -210,39 +219,6 @@ class SystemNoteService Note.create(args.merge(system: true)) end - # Prepend the mentioner's namespaced project path to the GFM reference for - # cross-project references. For same-project references, return the - # unmodified GFM reference. - def self.mentioner_gfm_ref(noteable, mentioner, cross_reference = false) - # FIXME (rspeicher): This was breaking things. - # if mentioner.is_a?(Commit) && cross_reference - # return mentioner.gfm_reference.sub('commit ', 'commit %') - # end - - full_gfm_reference(mentioner.project, noteable.project, mentioner) - end - - # Return the +mentioner+ GFM reference. If the mentioner and noteable - # projects are not the same, add the mentioning project's path to the - # returned value. - def self.full_gfm_reference(mentioning_project, noteable_project, mentioner) - if mentioning_project == noteable_project - mentioner.gfm_reference - else - if mentioner.is_a?(Commit) - mentioner.gfm_reference.sub( - /(commit )/, - "\\1#{mentioning_project.path_with_namespace}@" - ) - else - mentioner.gfm_reference.sub( - /(issue |merge request )/, - "\\1#{mentioning_project.path_with_namespace}" - ) - end - end - end - def self.cross_reference_note_prefix 'mentioned in ' end @@ -267,7 +243,7 @@ class SystemNoteService # # noteable - MergeRequest object # existing_commits - Array of existing Commit objects - # oldrev - Optional String SHA of ... TODO (rspeicher): I have no idea what this actually does. + # oldrev - Optional String SHA of a previous Commit # # Examples: # -- cgit v1.2.1