Commit fd8394fa authored by Grzegorz Bizon's avatar Grzegorz Bizon

Move reference unfolder for GFM to separate class

parent cd0f1945
...@@ -83,11 +83,8 @@ module Issues ...@@ -83,11 +83,8 @@ module Issues
def rewrite_references(noteable) def rewrite_references(noteable)
content = noteable_content(noteable).dup content = noteable_content(noteable).dup
context = { pipeline: :reference_unfold, unfolder = Gitlab::Gfm::ReferenceUnfolder.new(content, @project_old)
project: @project_old, new_project: @project_new } unfolder.unfold(@project_new)
new_content = Banzai.render_result(content, context)
new_content[:output].to_s
end end
def noteable_content(noteable) def noteable_content(noteable)
......
require 'html/pipeline/filter'
module Banzai
module Filter
##
# Filter than unfolds local references.
#
#
class ReferenceUnfoldFilter < HTML::Pipeline::Filter
def initialize(*)
super
unless result[:references].is_a?(Hash)
raise StandardError, 'References not processed!'
end
@text = context[:text].dup
@new_project = context[:new_project]
@referables = result[:references].values.flatten
end
def call
@referables.each do |referable|
next unless referable.respond_to?(:to_reference)
pattern = /#{Regexp.escape(referable.to_reference)}/
@text.gsub!(pattern, referable.to_reference(@new_project))
end
@text
end
private
def validate
needs :project
needs :new_project
needs :text
end
end
end
end
module Banzai
module Pipeline
class ReferenceUnfoldPipeline < BasePipeline
def self.filters
FullPipeline.filters +
[Filter::ReferenceGathererFilter,
Filter::ReferenceUnfoldFilter]
end
def self.call(text, context = {})
context = context.merge(text: text)
super
end
class << self
alias_method :to_document, :call
alias_method :to_html, :call
end
end
end
end
module Gitlab
module Gfm
##
# Class than unfolds local references in text.
#
#
class ReferenceUnfolder
def initialize(text, project)
@text = text
@project = project
end
def unfold(from_project)
referables.each_with_object(@text.dup) do |referable, text|
next unless referable.respond_to?(:to_reference)
pattern = /#{Regexp.escape(referable.to_reference)}/
text.gsub!(pattern, referable.to_reference(from_project))
end
end
private
def referables
extractor = Gitlab::ReferenceExtractor.new(@project)
extractor.analyze(@text)
extractor.all
end
end
end
end
module Gitlab module Gitlab
# Extract possible GFM references from an arbitrary String for further processing. # Extract possible GFM references from an arbitrary String for further processing.
class ReferenceExtractor < Banzai::ReferenceExtractor class ReferenceExtractor < Banzai::ReferenceExtractor
REFERABLES = %i(user issue label milestone merge_request snippet commit commit_range)
attr_accessor :project, :current_user, :author attr_accessor :project, :current_user, :author
def initialize(project, current_user = nil, author = nil) def initialize(project, current_user = nil, author = nil)
...@@ -17,7 +18,7 @@ module Gitlab ...@@ -17,7 +18,7 @@ module Gitlab
super(text, context.merge(project: project)) super(text, context.merge(project: project))
end end
%i(user label milestone merge_request snippet commit commit_range).each do |type| REFERABLES.each do |type|
define_method("#{type}s") do define_method("#{type}s") do
@references[type] ||= references(type, reference_context) @references[type] ||= references(type, reference_context)
end end
...@@ -31,6 +32,11 @@ module Gitlab ...@@ -31,6 +32,11 @@ module Gitlab
end end
end end
def all
REFERABLES.each { |referable| send(referable.to_s.pluralize) }
@references.values.flatten
end
private private
def reference_context def reference_context
......
require 'spec_helper'
describe Banzai::Pipeline::ReferenceUnfoldPipeline do
let(:text) { 'some text' }
let(:old_project) { create(:project) }
let(:new_project) { create(:project) }
let(:pipeline_context) do
{ project: old_project, new_project: new_project }
end
let(:result) do
described_class.to_document(text, pipeline_context)
end
context 'invalid initializers' do
subject { -> { result } }
context 'project context is missing' do
let(:pipeline_context) { { new_project: new_project } }
it { is_expected.to raise_error ArgumentError, /Missing context keys/ }
end
context 'new project context is missing' do
let(:pipeline_context) { { project: old_project } }
it { is_expected.to raise_error ArgumentError, /Missing context keys/ }
end
end
context 'multiple issues and merge requests referenced' do
subject { result[:output] }
let!(:issue_first) { create(:issue, project: old_project) }
let!(:issue_second) { create(:issue, project: old_project) }
let!(:merge_request) { create(:merge_request, source_project: old_project) }
context 'plain text description' do
let(:text) { 'Description that references #1, #2 and !1' }
it { is_expected.to include issue_first.to_reference(new_project) }
it { is_expected.to include issue_second.to_reference(new_project) }
it { is_expected.to include merge_request.to_reference(new_project) }
end
context 'description with ignored elements' do
let(:text) do
"Hi. This references #1, but not `#2`\n" +
'<pre>and not !1</pre>'
end
it { is_expected.to include issue_first.to_reference(new_project) }
it { is_expected.to_not include issue_second.to_reference(new_project) }
it { is_expected.to_not include merge_request.to_reference(new_project) }
end
context 'description ambigous elements' do
let(:url) { 'http://gitlab.com/#1' }
let(:text) { "This references #1, but not #{url}" }
it { is_expected.to include url }
end
end
end
require 'spec_helper'
describe Gitlab::Gfm::ReferenceUnfolder do
let(:text) { 'some text' }
let(:old_project) { create(:project) }
let(:new_project) { create(:project) }
describe '#unfold' do
subject { described_class.new(text, old_project).unfold(new_project) }
context 'multiple issues and merge requests referenced' do
let!(:issue_first) { create(:issue, project: old_project) }
let!(:issue_second) { create(:issue, project: old_project) }
let!(:merge_request) { create(:merge_request, source_project: old_project) }
context 'plain text description' do
let(:text) { 'Description that references #1, #2 and !1' }
it { is_expected.to include issue_first.to_reference(new_project) }
it { is_expected.to include issue_second.to_reference(new_project) }
it { is_expected.to include merge_request.to_reference(new_project) }
end
context 'description with ignored elements' do
let(:text) do
"Hi. This references #1, but not `#2`\n" +
'<pre>and not !1</pre>'
end
it { is_expected.to include issue_first.to_reference(new_project) }
it { is_expected.to_not include issue_second.to_reference(new_project) }
it { is_expected.to_not include merge_request.to_reference(new_project) }
end
context 'description ambigous elements' do
let(:url) { 'http://gitlab.com/#1' }
let(:text) { "This references #1, but not #{url}" }
it { is_expected.to include url }
end
end
end
end
...@@ -122,4 +122,16 @@ describe Gitlab::ReferenceExtractor, lib: true do ...@@ -122,4 +122,16 @@ describe Gitlab::ReferenceExtractor, lib: true do
expect(extracted).to match_array([issue]) expect(extracted).to match_array([issue])
end end
end end
describe '#all' do
let(:issue) { create(:issue, project: project) }
let(:label) { create(:label, project: project) }
let(:text) { "Ref. #{issue.to_reference} and #{label.to_reference}" }
before { subject.analyze(text) }
it 'returns all referables' do
expect(subject.all).to match_array([issue, label])
end
end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment