Skip to content

Commit

Permalink
Merge pull request #61 from moggers87/46-strip-extensions
Browse files Browse the repository at this point in the history
Make strip_exts configurable
  • Loading branch information
moggers87 committed Jan 1, 2019
2 parents 20ab6d5 + 0f9f7ce commit dab1b95
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 35 deletions.
23 changes: 23 additions & 0 deletions docs/meta.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,29 @@ As glob patterns are fairly simple, ``ignore`` can also be a list of patterns:
If your site isn't deployed to the root of a domain, use this setting to tell
Exhibition about the prefix so it can be added to all URLs

``strip_exts``
^^^^^^^^^^^^^^

Specifies if certain files should have their extensions removed when being
referenced via ``Node.full_url``. When deploying, this does not change the
filename, so you will need to configure your web server to serve those files
correctly.

By default, this will be applied to files ending in ``.html``, to disable this
feature use the following:

.. code-block:: yaml
strip_exts:
You can also specify multiple file extensions:

.. code-block:: yaml
strip_exts:
- .html
- .htm
Filters
-------

Expand Down
13 changes: 11 additions & 2 deletions exhibition/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,15 @@
".json": yaml_parser.load,
}

DEFAULT_STRIP_EXTS = [".html"]


class Node:
"""
A node represents a file or directory
"""
_meta_names = ["meta.yaml", "meta.yml"]
_index_file = "index.html"
_strip_exts = [".html"]

_meta_header = "---\n"
_meta_footer = "---\n"
Expand Down Expand Up @@ -200,7 +201,7 @@ def full_url(self):
elif self.is_leaf:
if self.path_obj.name == self._index_file:
name = ""
elif self.path_obj.suffix in self._strip_exts:
elif self.path_obj.suffix in self.strip_exts:
name = self.path_obj.stem
elif self.cache_bust:
suffixes = "".join(self.path_obj.suffixes)
Expand Down Expand Up @@ -407,3 +408,11 @@ def cache_bust(self):
break

return self._cache_bust_version

@property
def strip_exts(self):
strip_exts = self.meta.get("strip_exts", DEFAULT_STRIP_EXTS)
if not isinstance(strip_exts, (list, tuple)):
strip_exts = [strip_exts]

return strip_exts
69 changes: 69 additions & 0 deletions exhibition/tests/test_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -725,3 +725,72 @@ def test_root_node_is_kept(self):
parent_node.children["pages"].children["page.html"].parent.parent.root_node,
parent_node
)

def test_strip_exts_default(self):
parent_path = pathlib.Path(self.content_path.name)
child_path = pathlib.Path(self.content_path.name, "blog.html")
child_path.touch()

parent_node = Node(parent_path, None, meta=self.default_settings)
child_node = Node(child_path, parent_node)

self.assertEqual(child_node.strip_exts, [".html"])
self.assertEqual(child_node.full_url, "/blog")

def test_strip_exts_custom(self):
parent_path = pathlib.Path(self.content_path.name)
child_path = pathlib.Path(self.content_path.name, "blog.tw2")
child_path.touch()

settings = {"strip_exts": [".tw2"]}
settings.update(self.default_settings)
parent_node = Node(parent_path, None, meta=settings)
child_node = Node(child_path, parent_node)

self.assertEqual(child_node.strip_exts, [".tw2"])
self.assertEqual(child_node.full_url, "/blog")

def test_strip_exts_multiple(self):
parent_path = pathlib.Path(self.content_path.name)
child1_path = pathlib.Path(self.content_path.name, "blog.bluh")
child1_path.touch()
child2_path = pathlib.Path(self.content_path.name, "story.tw2")
child2_path.touch()

settings = {"strip_exts": [".tw2", ".bluh"]}
settings.update(self.default_settings)
parent_node = Node(parent_path, None, meta=settings)
child1_node = Node(child1_path, parent_node)
child2_node = Node(child2_path, parent_node)

self.assertEqual(child1_node.strip_exts, [".tw2", ".bluh"])
self.assertEqual(child1_node.full_url, "/blog")

self.assertEqual(child2_node.strip_exts, [".tw2", ".bluh"])
self.assertEqual(child2_node.full_url, "/story")

def test_strip_exts_not_a_list(self):
parent_path = pathlib.Path(self.content_path.name)
child_path = pathlib.Path(self.content_path.name, "blog.html")
child_path.touch()

settings = {"strip_exts": ".html"}
settings.update(self.default_settings)
parent_node = Node(parent_path, None, meta=settings)
child_node = Node(child_path, parent_node)

self.assertEqual(child_node.strip_exts, [".html"])
self.assertEqual(child_node.full_url, "/blog")

def test_strip_exts_disabled(self):
parent_path = pathlib.Path(self.content_path.name)
child_path = pathlib.Path(self.content_path.name, "blog.html")
child_path.touch()

settings = {"strip_exts": []}
settings.update(self.default_settings)
parent_node = Node(parent_path, None, meta=settings)
child_node = Node(child_path, parent_node)

self.assertEqual(child_node.strip_exts, [])
self.assertEqual(child_node.full_url, "/blog.html")
36 changes: 26 additions & 10 deletions exhibition/tests/test_serve.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def get_server(self, settings):
self.server = httpd

def test_fetch_file(self):
settings = Config({"deploy_path": self.tmp_dir.name})
settings = Config({"deploy_path": self.tmp_dir.name, "content_path": self.tmp_dir.name})
self.get_server(settings)

self.client.request("GET", "/style.css")
Expand All @@ -108,7 +108,8 @@ def test_fetch_file(self):
self.assertEqual(content, CSS_CONTENTS.encode())

def test_fetch_file_with_prefix(self):
settings = Config({"deploy_path": self.tmp_dir.name, "base_url": "/bob/"})
settings = Config({"deploy_path": self.tmp_dir.name, "content_path": self.tmp_dir.name,
"base_url": "/bob/"})
self.get_server(settings)

self.client.request("GET", "/bob/style.css")
Expand All @@ -122,7 +123,7 @@ def test_fetch_file_with_prefix(self):
self.assertEqual(response.status, 404)

def test_index(self):
settings = Config({"deploy_path": self.tmp_dir.name})
settings = Config({"deploy_path": self.tmp_dir.name, "content_path": self.tmp_dir.name})
self.get_server(settings)

self.client.request("GET", "/")
Expand All @@ -132,15 +133,15 @@ def test_index(self):
self.assertEqual(content, INDEX_CONTENTS.encode())

def test_404(self):
settings = Config({"deploy_path": self.tmp_dir.name})
settings = Config({"deploy_path": self.tmp_dir.name, "content_path": self.tmp_dir.name})
self.get_server(settings)

self.client.request("GET", "/not-existing.html")
response = self.client.getresponse()
self.assertEqual(response.status, 404)

def test_stripped_extension(self):
settings = Config({"deploy_path": self.tmp_dir.name})
settings = Config({"deploy_path": self.tmp_dir.name, "content_path": self.tmp_dir.name})
self.get_server(settings)

self.client.request("GET", "/page")
Expand All @@ -149,8 +150,23 @@ def test_stripped_extension(self):
content = response.read()
self.assertEqual(content, PAGE_CONTENTS.encode())

def test_custom_stripped_extension(self):
settings = Config({"deploy_path": self.tmp_dir.name, "content_path": self.tmp_dir.name,
"strip_exts": ".css"})
self.get_server(settings)

self.client.request("GET", "/style")
response = self.client.getresponse()
self.assertEqual(response.status, 200)
content = response.read()
self.assertEqual(content, CSS_CONTENTS.encode())

self.client.request("GET", "/page")
response = self.client.getresponse()
self.assertEqual(response.status, 404)

def test_stripped_extension_and_trailing_slash(self):
settings = Config({"deploy_path": self.tmp_dir.name})
settings = Config({"deploy_path": self.tmp_dir.name, "content_path": self.tmp_dir.name})
self.get_server(settings)

self.client.request("GET", "/page/")
Expand All @@ -160,7 +176,7 @@ def test_stripped_extension_and_trailing_slash(self):
self.assertEqual(content, PAGE_CONTENTS.encode())

def test_stripped_extension_with_extension(self):
settings = Config({"deploy_path": self.tmp_dir.name})
settings = Config({"deploy_path": self.tmp_dir.name, "content_path": self.tmp_dir.name})
self.get_server(settings)

self.client.request("GET", "/page.html")
Expand All @@ -170,15 +186,15 @@ def test_stripped_extension_with_extension(self):
self.assertEqual(content, PAGE_CONTENTS.encode())

def test_stripped_extension_not_exists(self):
settings = Config({"deploy_path": self.tmp_dir.name})
settings = Config({"deploy_path": self.tmp_dir.name, "content_path": self.tmp_dir.name})
self.get_server(settings)

self.client.request("GET", "/unknown")
response = self.client.getresponse()
self.assertEqual(response.status, 404)

def test_dir_index(self):
settings = Config({"deploy_path": self.tmp_dir.name})
settings = Config({"deploy_path": self.tmp_dir.name, "content_path": self.tmp_dir.name})
self.get_server(settings)

self.client.request("GET", "/blog/")
Expand All @@ -188,7 +204,7 @@ def test_dir_index(self):
self.assertEqual(content, BLOG_INDEX_CONTENTS.encode())

def test_dir_without_index(self):
settings = Config({"deploy_path": self.tmp_dir.name})
settings = Config({"deploy_path": self.tmp_dir.name, "content_path": self.tmp_dir.name})
index = pathlib.Path(self.blog_dir, "index.html")
index.unlink()
self.assertFalse(index.exists())
Expand Down
64 changes: 41 additions & 23 deletions exhibition/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,43 @@ def gen(settings):
item.render()


class ExhibitionBaseHTTPRequestHandler(SimpleHTTPRequestHandler):
def _sanitise_path(self, path):
""" Strip leading and trailing / as well as base_url, if preset """
path = path.strip("/")
if self._settings.get("base_url"):
base = self._settings["base_url"].strip("/")
if not path.startswith(base):
return
path = path.lstrip(base).strip("/")

return path

def translate_path(self, path):
path = self._sanitise_path(path)
root_node = Node.from_path(pathlib.Path(self._settings["content_path"]),
meta=self._settings)

try:
node = root_node.get_from_path(pathlib.PurePath(path).parent or path)
except (OSError, TypeError):
return ""

path = pathlib.Path(self._settings["deploy_path"], path)

if not (path.exists() or path.suffix):
for ext in node.strip_exts:
new_path = path.with_suffix(ext)
if new_path.exists():
return str(new_path)
elif path.is_dir():
new_path = pathlib.Path(path, Node._index_file)
if new_path.exists():
return str(new_path)

return str(path)


def serve(settings):
"""
Serves the generated site from ``deploy_path``
Expand All @@ -52,31 +89,12 @@ def serve(settings):
"""
logger = logging.getLogger("exhibition.server")

class ExhibitionHTTPRequestHandler(SimpleHTTPRequestHandler):
def translate_path(self, path):
path = path.strip("/")
if settings.get("base_url"):
base = settings["base_url"].strip("/")
if not path.startswith(base):
return ""
path = path.lstrip(base).strip("/")

path = pathlib.Path(settings["deploy_path"], path)

if not (path.exists() or path.suffix):
for ext in Node._strip_exts:
new_path = path.with_suffix(ext)
if new_path.exists():
return str(new_path)
elif path.is_dir():
new_path = pathlib.Path(path, Node._index_file)
if new_path.exists():
return str(new_path)

return str(path)

server_address = ('localhost', 8000)

# this is quite ewwww, but whatever.
class ExhibitionHTTPRequestHandler(ExhibitionBaseHTTPRequestHandler):
_settings = settings

httpd = HTTPServer(server_address, ExhibitionHTTPRequestHandler)

logger.warning("Listening on http://%s:%s", *server_address)
Expand Down

0 comments on commit dab1b95

Please sign in to comment.