Skip to content

Convert external links to other enabled docs to internal links #714

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions assets/javascripts/app/app.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
@shortcuts = new app.Shortcuts
@document = new app.views.Document
@mobile = new app.views.Mobile if @isMobile()
@urls = new app.Urls

if document.body.hasAttribute('data-doc')
@DOC = JSON.parse(document.body.getAttribute('data-doc'))
Expand Down Expand Up @@ -112,6 +113,7 @@
initDoc: (doc) ->
doc.entries.add type.toEntry() for type in doc.types.all()
@entries.add doc.entries.all()
@urls.addDoc doc
return

migrateDocs: ->
Expand Down
31 changes: 31 additions & 0 deletions assets/javascripts/app/urls.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
class app.Urls
_map: {}
_cached: {}

get: (url) ->
return unless url
parsed = new URL(url)
@_cached?[parsed.host]?[_keyFor(parsed)]

addDoc: (doc) ->
@_map[doc.slug] = doc.entries
.all()
.filter (entry) -> entry.url
.map (entry) -> [entry.url, entry.path]
@rebuild()

removeDoc: (doc) ->
deleted = delete @_map[doc.slug]
@rebuild() if deleted
delted

rebuild: ->
@_cached = {}
for slug, entries of @_map
for [url, path] in entries
parsed = new URL(url)
@_cached[parsed.host] ?= {}
@_cached[parsed.host][_keyFor(parsed)] = "/#{slug}/#{path}"


_keyFor = (parsed) -> (parsed.pathname + parsed.search + parsed.hash).toLowerCase()
2 changes: 1 addition & 1 deletion assets/javascripts/models/entry.coffee
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#= require app/searcher

class app.models.Entry extends app.Model
# Attributes: name, type, path
# Attributes: name, type, path, url

constructor: ->
super
Expand Down
12 changes: 11 additions & 1 deletion assets/javascripts/views/content/entry_page.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ class app.views.EntryPage extends app.View

$.batchUpdate @el, =>
@subview.render(content, fromCache)
@addCopyButtons() unless fromCache
unless fromCache
@addCopyButtons()
@internalizeLinks()
return

if app.disabledDocs.findBy 'slug', @entry.doc.slug
Expand All @@ -55,6 +57,14 @@ class app.views.EntryPage extends app.View
el.appendChild @copyButton.cloneNode(true) for el in @findAllByTag('pre')
return

internalizeLinks: ->
for el in @findAllByTag('a')
continue if el.classList.contains '_attribution-link'

internalUrl = app.urls.get(el.href)
if internalUrl?
el.href = internalUrl

polyfillMathML: ->
return unless window.supportsMathML is false and !@polyfilledMathML and @findByTag('math')
@polyfilledMathML = true
Expand Down
14 changes: 10 additions & 4 deletions lib/docs/core/models/entry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,24 @@ module Docs
class Entry
class Invalid < StandardError; end

attr_accessor :name, :type, :path
attr_accessor :name, :type, :path, :url

def initialize(name = nil, path = nil, type = nil)
def initialize(name = nil, path = nil, type = nil, url = nil)
self.name = name
self.path = path
self.type = type
self.url = url

unless root?
raise Invalid, 'missing name' if !name || name.empty?
raise Invalid, 'missing path' if !path || path.empty?
raise Invalid, 'missing type' if !type || type.empty?
raise Invalid, 'missing url' if !url || url.empty?
end
end

def ==(other)
other.name == name && other.path == path && other.type == type
other.name == name && other.path == path && other.type == type && other.url == url
end

def name=(value)
Expand All @@ -30,12 +32,16 @@ def type=(value)
@type = value.try :strip
end

def url=(value)
@url = value.try :strip
end

def root?
path == 'index'
end

def as_json
{ name: name, path: path, type: type }
{ name: name, path: path, type: type, url: url }
end
end
end
12 changes: 10 additions & 2 deletions lib/docs/filters/core/entries.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,16 @@ def build_entries(entries)

def build_entry(name, frag = nil, type = nil)
type ||= self.type
path = frag ? (frag.include?('#') ? frag : "#{self.path}##{frag}") : self.path
Entry.new(name, path, type)
if frag.present? && frag.include?('#')
path = frag
# TODO: What should `url` get changed to?

This comment was marked as off-topic.

url = current_url
else
hash_frag = frag ? "##{frag}" : ''
path = "#{self.path}#{hash_frag}"
url = "#{current_url}#{hash_frag}"
end
Entry.new(name, path, type, url)
end
end
end
4 changes: 2 additions & 2 deletions test/lib/docs/core/doc_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class DocsDocTest < MiniTest::Spec
end

let :entry do
Docs::Entry.new 'name', 'path', 'type'
Docs::Entry.new 'name', 'path', 'type', 'url'
end

let :store do
Expand Down Expand Up @@ -283,7 +283,7 @@ class DocsDocTest < MiniTest::Spec
mock(store).write('index.json', anything) do |path, json|
json = JSON.parse(json)
assert_equal pages.length, json['entries'].length
assert_includes json['entries'], Docs::Entry.new('one', 'path', 'type').as_json.stringify_keys
assert_includes json['entries'], Docs::Entry.new('one', 'path', 'type', 'url').as_json.stringify_keys
end
doc.store_pages(store)
end
Expand Down
20 changes: 10 additions & 10 deletions test/lib/docs/core/entry_index_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

class DocsEntryIndexTest < MiniTest::Spec
let :entry do
Docs::Entry.new 'name', 'path', 'type'
Docs::Entry.new 'name', 'path', 'type', 'url'
end

let :index do
Expand All @@ -30,8 +30,8 @@ class DocsEntryIndexTest < MiniTest::Spec

it "stores an array of entries" do
entries = [
Docs::Entry.new('one', 'path', 'type'),
Docs::Entry.new('two', 'path', 'type')
Docs::Entry.new('one', 'path', 'type', 'url'),
Docs::Entry.new('two', 'path', 'type', 'url')
]

index.add(entries)
Expand All @@ -56,9 +56,9 @@ class DocsEntryIndexTest < MiniTest::Spec
end

it "creates and indexes the type" do
index.add Docs::Entry.new('one', 'path', 'a')
index.add Docs::Entry.new('two', 'path', 'b')
index.add Docs::Entry.new('three', 'path', 'b')
index.add Docs::Entry.new('one', 'path', 'a', 'url')
index.add Docs::Entry.new('two', 'path', 'b', 'url')
index.add Docs::Entry.new('three', 'path', 'b', 'url')
assert_equal ['a', 'b'], index.types.keys
assert_instance_of Docs::Type, index.types['a']
end
Expand All @@ -69,8 +69,8 @@ class DocsEntryIndexTest < MiniTest::Spec
end

it "increments the type's count" do
index.add Docs::Entry.new('one', 'path', 'type')
index.add Docs::Entry.new('two', 'path', 'type')
index.add Docs::Entry.new('one', 'path', 'type', 'url')
index.add Docs::Entry.new('two', 'path', 'type', 'url')
assert_equal 2, index.types['type'].count
end
end
Expand Down Expand Up @@ -101,8 +101,8 @@ class DocsEntryIndexTest < MiniTest::Spec
end

it "includes the json representation of the #entries" do
index.add one = Docs::Entry.new('one', 'path', 'type')
index.add two = Docs::Entry.new('two', 'path', 'type')
index.add one = Docs::Entry.new('one', 'path', 'type', 'url')
index.add two = Docs::Entry.new('two', 'path', 'type', 'url')
assert_equal [one.as_json, two.as_json], index.as_json[:entries]
end

Expand Down
59 changes: 40 additions & 19 deletions test/lib/docs/core/models/entry_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,38 @@ class DocsEntryTest < MiniTest::Spec
Entry = Docs::Entry

let :entry do
Entry.new('name', 'path', 'type')
Entry.new('name', 'path', 'type', 'url')
end

def build_entry(name = 'name', path = 'path', type = 'type')
Entry.new(name, path, type)
def build_entry(name = 'name', path = 'path', type = 'type', url = 'url')
Entry.new(name, path, type, url)
end

describe ".new" do
it "stores #name, #path and #type" do
entry = Entry.new('name', 'path', 'type')
it "stores #name, #path, #type, and #url" do
entry = Entry.new('name', 'path', 'type', 'url')
assert_equal 'name', entry.name
assert_equal 'path', entry.path
assert_equal 'type', entry.type
assert_equal 'url', entry.url
end

it "raises an error when #name, #path or #type is nil or empty" do
assert_raises(Docs::Entry::Invalid) { Entry.new(nil, 'path', 'type') }
assert_raises(Docs::Entry::Invalid) { Entry.new('', 'path', 'type') }
assert_raises(Docs::Entry::Invalid) { Entry.new('name', nil, 'type') }
assert_raises(Docs::Entry::Invalid) { Entry.new('name', '', 'type') }
assert_raises(Docs::Entry::Invalid) { Entry.new('name', 'path', nil) }
assert_raises(Docs::Entry::Invalid) { Entry.new('name', 'path', '') }
it "raises an error when #name, #path, #type, or #url is nil or empty" do
assert_raises(Docs::Entry::Invalid) { Entry.new(nil, 'path', 'type', 'url') }
assert_raises(Docs::Entry::Invalid) { Entry.new('', 'path', 'type', 'url') }
assert_raises(Docs::Entry::Invalid) { Entry.new('name', nil, 'type', 'url') }
assert_raises(Docs::Entry::Invalid) { Entry.new('name', '', 'type', 'url') }
assert_raises(Docs::Entry::Invalid) { Entry.new('name', 'path', nil, 'url') }
assert_raises(Docs::Entry::Invalid) { Entry.new('name', 'path', '', 'url') }
assert_raises(Docs::Entry::Invalid) { Entry.new('name', 'path', 'type', nil) }
assert_raises(Docs::Entry::Invalid) { Entry.new('name', 'path', 'type', '') }
end

it "don't raise an error when #path is 'index' and #name or #type is nil or empty" do
Entry.new(nil, 'index', 'type')
Entry.new('', 'index', 'type')
Entry.new('name', 'index', nil)
Entry.new('name', 'index', '')
Entry.new(nil, 'index', 'type', 'url')
Entry.new('', 'index', 'type', 'url')
Entry.new('name', 'index', nil, 'url')
Entry.new('name', 'index', '', 'url')
end
end

Expand Down Expand Up @@ -61,6 +64,19 @@ def build_entry(name = 'name', path = 'path', type = 'type')
end
end


describe "#url=" do
it "removes surrounding whitespace" do
entry.url = " \n\rurl "
assert_equal 'url', entry.url
end

it "accepts nil" do
entry.url = nil
assert_nil entry.url
end
end

describe "#==" do
it "returns true when the other has the same name, path and type" do
assert_equal build_entry, build_entry
Expand All @@ -80,6 +96,11 @@ def build_entry(name = 'name', path = 'path', type = 'type')
entry.type = 'other_type'
refute_equal build_entry, entry
end

it "returns false when the other has a different url" do
entry.url = 'other_url'
refute_equal build_entry, entry
end
end

describe "#root?" do
Expand All @@ -96,10 +117,10 @@ def build_entry(name = 'name', path = 'path', type = 'type')

describe "#as_json" do
it "returns a hash with the name, path and type" do
as_json = Entry.new('name', 'path', 'type').as_json
as_json = Entry.new('name', 'path', 'type', 'url').as_json
assert_instance_of Hash, as_json
assert_equal [:name, :path, :type], as_json.keys
assert_equal %w(name path type), as_json.values
assert_equal [:name, :path, :type, :url], as_json.keys
assert_equal %w(name path type url), as_json.values
end
end
end
15 changes: 14 additions & 1 deletion test/lib/docs/filters/core/entries_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class EntriesFilterTest < MiniTest::Spec
stub(filter).name { 'name' }
stub(filter).path { 'path' }
stub(filter).type { 'type' }
stub(filter).current_url { 'url' }
end

let :entries do
Expand Down Expand Up @@ -41,10 +42,11 @@ class EntriesFilterTest < MiniTest::Spec
end

describe "the default entry" do
it "has the #name, #path and #type" do
it "has the #name, #path, #type, and #url" do
assert_equal 'name', entries.first.name
assert_equal 'path', entries.first.path
assert_equal 'type', entries.first.type
assert_equal 'url', entries.first.url
end
end

Expand All @@ -67,6 +69,7 @@ class EntriesFilterTest < MiniTest::Spec
it "has a path with the given fragment" do
stub(filter).additional_entries { [['test', 'frag']] }
assert_equal 'path#frag', entries.last.path
assert_equal 'url#frag', entries.last.url
end

it "has a path with the given path" do
Expand All @@ -88,6 +91,16 @@ class EntriesFilterTest < MiniTest::Spec
stub(filter).additional_entries { [['test', nil, nil]] }
assert_equal 'type', entries.last.type
end

it "has a url copied from the current_url property" do
stub(filter).additional_entries { [['test', nil, 'test']] }
assert_equal 'url', entries.last.url
end

it "appends the fragment to #url" do
stub(filter).additional_entries { [['test', 'hash', 'test']] }
assert_equal 'url#hash', entries.last.url
end
end
end

Expand Down