Properly sort out pre-commit hook handling

What this means is that the source code of SimpleCMS now passes all the the
pre-commit hooks, and that the code generated by `simplecms` _also_ passes
all the pre-commit hooks.
main
Jaap Joris Vens 2022-01-29 12:56:45 +01:00
rodzic 924031f3ac
commit 74c896b86e
21 zmienionych plików z 340 dodań i 347 usunięć

Wyświetl plik

@ -1,7 +1,7 @@
repos:
- repo: https://github.com/rtts/djhtml
rev: v1.4.9
rev: v1.4.11
hooks:
- id: djhtml
@ -12,16 +12,17 @@ repos:
args: ['--in-place', '--remove-all-unused-imports']
- repo: https://github.com/pycqa/isort
rev: 5.9.1
rev: 5.10.1
hooks:
- id: isort
- repo: https://github.com/pycqa/flake8
rev: 3.9.2
hooks:
- id: flake8
args: ['--resolve-all-configs', '--line-length', '88']
- repo: https://github.com/psf/black
rev: 21.6b0
rev: 21.12b0
hooks:
- id: black
- repo: https://github.com/pycqa/flake8
rev: 4.0.1
hooks:
- id: flake8

Wyświetl plik

@ -45,8 +45,8 @@ Here's an example `views.py` of an app using SimpleCMS:
And here is the contents of `hello.html`:
<section type="helloworld">
<h1>{{message}}</h1>
{{section.content}}
<h1>{{ message }}</h1>
{{ section.content }}
</section>
Everytime a section needs to be rendered, SimpleCMS will call the
@ -72,17 +72,7 @@ SimpleCMS includes a variety of useful template tags, default *Page*
and *Section* models, and all the other boilerplate code needed for
new projects.
One notable inclusion is the `eval` template tag. It will pass its
argument first through Django's templating system and then through
Markdown, making for instance the following possible. (Disclaimer: use
with caution!)
Welcome to **{% now 'Y' %}!**
Another useful feature is the automatic compilation of `SCSS` files to
`CSS` files using a custom middleware.
## Feedback and support
We would love to hear from you! Feel free to [open an
issue](../../issues) or [send us an email](mailto:jj+cms@rtts.eu).
issue](../../issues) or [send us an email](mailto:cms@jj.rtts.eu).

Wyświetl plik

@ -1,2 +1,2 @@
__version__ = "1.0.6"
__version__ = "1.0.7"
default_app_config = "cms.apps.CmsConfig"

Wyświetl plik

@ -36,11 +36,17 @@ def create_project(project_name, project_dir):
line = f"django-simplecms=={cms.__version__}"
print(line, file=f)
shutil.copytree(
os.path.dirname(example.__file__),
os.path.join(project_dir, project_name),
dirs_exist_ok=True,
example_dir = os.path.dirname(example.__file__)
app_dir = os.path.join(project_dir, project_name)
shutil.copytree(example_dir, app_dir, dirs_exist_ok=True)
shutil.move(
os.path.join(app_dir, "setup.cfg"), os.path.join(project_dir, "setup.cfg")
)
shutil.move(
os.path.join(app_dir, ".pre-commit-config.yaml"),
os.path.join(project_dir, ".pre-commit-config.yaml"),
)
with open(
os.open(
os.path.join(project_dir, "manage.py"), os.O_CREAT | os.O_WRONLY, 0o755
@ -48,36 +54,40 @@ def create_project(project_name, project_dir):
"w",
) as f:
print(
"#!/usr/bin/env python",
"import os, sys",
f"os.environ.setdefault('DJANGO_SETTINGS_MODULE', '{project_name}.settings')",
"from django.core.management import execute_from_command_line",
"execute_from_command_line(sys.argv)",
sep="\n",
f"""#!/usr/bin/env python
import os
import sys
from django.core.management import execute_from_command_line
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "{project_name}.settings")
execute_from_command_line(sys.argv)""",
file=f,
)
with open(os.path.join(project_dir, project_name, "wsgi.py"), "w") as f:
print(
"import os",
"from django.core.wsgi import get_wsgi_application",
f"os.environ.setdefault('DJANGO_SETTINGS_MODULE', '{project_name}.settings')",
"application = get_wsgi_application()",
sep="\n",
f"""import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "{project_name}.settings")
application = get_wsgi_application()""",
file=f,
)
with open(os.path.join(project_dir, ".gitignore"), "w") as f:
print("*.pyc\n__pycache__/", file=f)
print(
f'Successfully created project "{project_name}"',
"",
"Things to do next:",
"- create a database",
"- ./manage.py makemigrations",
"- ./manage.py migrate",
"- ./manage.py createsuperuser",
"- ./manage.py runserver",
sep="\n",
f"""
Successfully created project "{project_name}"
Things to do next:
- create a database
- ./manage.py makemigrations
- ./manage.py migrate
- ./manage.py createsuperuser
- ./manage.py runserver
"""
)

Wyświetl plik

@ -3,17 +3,17 @@
<!DOCTYPE html>
<html lang="{% get_current_language as lang%}{{lang}}">
<head>
<title>{% block title %}{% endblock %}</title>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="{% static 'cms/admin.scss.css' %}">
{% block extrahead %}{% endblock %}
</head>
<body>
{% block content %}
{% endblock %}
<head>
<title>{% block title %}{% endblock %}</title>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="{% static 'cms/admin.scss.css' %}">
{% block extrahead %}{% endblock %}
</head>
<body>
{% block content %}
{% endblock %}
{% block extrabody %}{% endblock %}
</body>
{% block extrabody %}{% endblock %}
</body>
</html>

Wyświetl plik

@ -4,113 +4,113 @@
{% block title %}{% trans 'Edit' %} {{form.instance}}{% endblock %}
{% block content %}
<form method="POST" enctype="multipart/form-data" class="cms" novalidate>
<div class="wrapper">
{% csrf_token %}
<form method="POST" enctype="multipart/form-data" class="cms" novalidate>
<div class="wrapper">
{% csrf_token %}
{% if form.errors %}
<div class="global_error">
{% trans 'Please correct the error(s) below and save again' %}
{% if form.errors %}
<div class="global_error">
{% trans 'Please correct the error(s) below and save again' %}
</div>
{% endif %}
{% include 'cms/form.html' %}
<div class="edit page">
<button><img src="{% static 'cms/save.png' %}" width="75"></button>
</div>
</div>
{% endif %}
{% include 'cms/form.html' %}
<div class="edit page">
<button><img src="{% static 'cms/save.png' %}" width="75"></button>
</div>
</div>
</form>
</form>
{% endblock %}
{% block extrabody %}
<script type="text/javascript" src="/static/admin/js/urlify.js"></script>
<script>
function addForm(node) {
let base = node.previousElementSibling;
let parent = node.parentNode;
let counter = parent.firstElementChild;
let extra_form = base.cloneNode(true);
let prefix = extra_form.id
<script type="text/javascript" src="/static/admin/js/urlify.js"></script>
<script>
function addForm(node) {
let base = node.previousElementSibling;
let parent = node.parentNode;
let counter = parent.firstElementChild;
let extra_form = base.cloneNode(true);
let prefix = extra_form.id
increment(extra_form, prefix, 'id');
for (let el of extra_form.querySelectorAll(`*[id^=${prefix}]`)) {
increment(el, prefix, 'id');
}
for (let el of extra_form.querySelectorAll(`*[name^=${prefix}]`)) {
increment(el, prefix, 'name');
}
for (let el of extra_form.querySelectorAll(`*[id^=id_${prefix}]`)) {
increment(el, 'id_' + prefix, 'id');
}
for (let el of extra_form.querySelectorAll(`*[for^=id_${prefix}]`)) {
increment(el, 'id_' + prefix, 'for');
}
for (let el of extra_form.querySelectorAll(`*[id^=formfield_${prefix}]`)) {
increment(el, 'formfield_' + prefix, 'id');
increment(extra_form, prefix, 'id');
for (let el of extra_form.querySelectorAll(`*[id^=${prefix}]`)) {
increment(el, prefix, 'id');
}
for (let el of extra_form.querySelectorAll(`*[name^=${prefix}]`)) {
increment(el, prefix, 'name');
}
for (let el of extra_form.querySelectorAll(`*[id^=id_${prefix}]`)) {
increment(el, 'id_' + prefix, 'id');
}
for (let el of extra_form.querySelectorAll(`*[for^=id_${prefix}]`)) {
increment(el, 'id_' + prefix, 'for');
}
for (let el of extra_form.querySelectorAll(`*[id^=formfield_${prefix}]`)) {
increment(el, 'formfield_' + prefix, 'id');
}
node.remove();
parent.appendChild(extra_form);
parent.appendChild(node);
base.hidden = false;
counter.value = parseInt(counter.value) + 1;
setEventHandlers();
resizeTextareas();
}
node.remove();
parent.appendChild(extra_form);
parent.appendChild(node);
base.hidden = false;
counter.value = parseInt(counter.value) + 1;
setEventHandlers();
resizeTextareas();
}
function increment(node, prefix, attr) {
let re = RegExp(`${prefix}(.*)`);
let matches = node.getAttribute(attr).match(re);
if (matches) {
let suffix = matches[1];
let name = prefix.replace(/-\d+$/, '');
let index = parseInt(prefix.replace(/.*-/, ''));
index++;
let new_prefix = `${name}-${index}`;
node.setAttribute(attr, new_prefix + suffix);
}
}
function resizeTextareas() {
let tx = document.getElementsByTagName('textarea');
for (let i = 0; i < tx.length; i++) {
tx[i].style.height = (tx[i].scrollHeight) + 1 + 'px';
tx[i].addEventListener('input', function() {
this.style.height = (this.scrollHeight) + 1 + 'px'; // why the 1???
});
}
}
function showRelevantFields(form, type) {
let fields_per_type = {{fields_per_type|safe}};
for (let field of form.querySelectorAll(`fieldset#${form.id} > div.formfield`)) {
if (!field.id.endsWith('DELETE')) {
field.hidden = true;
function increment(node, prefix, attr) {
let re = RegExp(`${prefix}(.*)`);
let matches = node.getAttribute(attr).match(re);
if (matches) {
let suffix = matches[1];
let name = prefix.replace(/-\d+$/, '');
let index = parseInt(prefix.replace(/.*-/, ''));
index++;
let new_prefix = `${name}-${index}`;
node.setAttribute(attr, new_prefix + suffix);
}
}
for (let name of fields_per_type[type]) {
document.getElementById(`formfield_${form.id}-${name}`).hidden = false;
}
}
function setEventHandlers() {
for (let typefield of document.querySelectorAll('select[name$=-type]')) {
let formId = typefield.name.replace(/-type$/, '');
let form = document.getElementById(formId);
let type = typefield.value.toLowerCase();
showRelevantFields(form, type);
typefield.addEventListener('input', function() {
type = typefield.value.toLowerCase();
function resizeTextareas() {
let tx = document.getElementsByTagName('textarea');
for (let i = 0; i < tx.length; i++) {
tx[i].style.height = (tx[i].scrollHeight) + 1 + 'px';
tx[i].addEventListener('input', function() {
this.style.height = (this.scrollHeight) + 1 + 'px'; // why the 1???
});
}
}
function showRelevantFields(form, type) {
let fields_per_type = {{fields_per_type|safe}};
for (let field of form.querySelectorAll(`fieldset#${form.id} > div.formfield`)) {
if (!field.id.endsWith('DELETE')) {
field.hidden = true;
}
}
for (let name of fields_per_type[type]) {
document.getElementById(`formfield_${form.id}-${name}`).hidden = false;
}
}
function setEventHandlers() {
for (let typefield of document.querySelectorAll('select[name$=-type]')) {
let formId = typefield.name.replace(/-type$/, '');
let form = document.getElementById(formId);
let type = typefield.value.toLowerCase();
showRelevantFields(form, type);
resizeTextareas();
});
typefield.addEventListener('input', function() {
type = typefield.value.toLowerCase();
showRelevantFields(form, type);
resizeTextareas();
});
}
}
}
document.addEventListener("DOMContentLoaded", function(event) {
setEventHandlers();
resizeTextareas();
});
</script>
document.addEventListener("DOMContentLoaded", function(event) {
setEventHandlers();
resizeTextareas();
});
</script>
{% endblock %}

Wyświetl plik

@ -1,23 +1,23 @@
{% load static %}
<fieldset id="{{form.prefix}}" class="form" {% if forloop.last %}hidden{% endif %}>
{{form.media}}
{% for field in form.hidden_fields %}
{{field}}
{% endfor %}
{{form.media}}
{% for field in form.hidden_fields %}
{{field}}
{% endfor %}
{% for field in form.visible_fields %}
{% include 'cms/formfield.html' with field=field %}
{% endfor %}
{% for field in form.visible_fields %}
{% include 'cms/formfield.html' with field=field %}
{% endfor %}
{% for formset in form.formsets %}
<div class="formfield {{formset.name}}" id="formfield_{{formset.prefix}}">
{{formset.management_form}}
{% for form in formset %}
{% include 'cms/form.html' %}
{% endfor %}
<img onclick="addForm(this, '{{formset.prefix}}')" src="{% static 'cms/add.png' %}" width="75">
</div>
{% endfor %}
{% for formset in form.formsets %}
<div class="formfield {{formset.name}}" id="formfield_{{formset.prefix}}">
{{formset.management_form}}
{% for form in formset %}
{% include 'cms/form.html' %}
{% endfor %}
<img onclick="addForm(this, '{{formset.prefix}}')" src="{% static 'cms/add.png' %}" width="75">
</div>
{% endfor %}
</fieldset>

Wyświetl plik

@ -1,31 +1,31 @@
{% if field.name == 'DELETE' and not form.instance.pk %}
{% else %}
<div class="formfield {{field.name}}{% if field.errors %} error{% endif %}" id="formfield_{{field.html_name}}">
<div class="formfield {{field.name}}{% if field.errors %} error{% endif %}" id="formfield_{{field.html_name}}">
{% if field.errors %}
<div class="errors">
{{field.errors}}
</div>
{% endif %}
{% if field.errors %}
<div class="errors">
{{field.errors}}
</div>
{% endif %}
{% if field.field.widget.input_type == 'checkbox' %}
<div class="input">
{{field}} {{field.label_tag}}
</div>
{% else %}
<div class="label">
{{field.label_tag}}
</div>
<div class="input">
{{field}}
</div>
{% endif %}
{% if field.field.widget.input_type == 'checkbox' %}
<div class="input">
{{field}} {{field.label_tag}}
</div>
{% else %}
<div class="label">
{{field.label_tag}}
</div>
<div class="input">
{{field}}
</div>
{% endif %}
{% if field.help_text %}
<div class="helptext">
{{field.help_text}}
</div>
{% endif %}
</div>
{% if field.help_text %}
<div class="helptext">
{{field.help_text}}
</div>
{% endif %}
</div>
{% endif %}

Wyświetl plik

@ -4,9 +4,9 @@
{% block title %}{{block.super}} - {{page.title}}{% endblock %}
{% block content %}
{% for section in sections %}
{% include_section section %}
{% endfor %}
{% for section in sections %}
{% include_section section %}
{% endfor %}
{% editpage '<img src="/static/cms/edit.png">' %}
{% editpage '<img src="/static/cms/edit.png">' %}
{% endblock %}

Wyświetl plik

@ -3,19 +3,19 @@
{% load i18n %}
{% block content %}
<form method="post" class="cms" novalidate>
<div class="wrapper" style="max-width: 300px">
{% csrf_token %}
<fieldset>
{{form.non_field_errors}}
{% for field in form %}
{% include 'cms/formfield.html' with field=field %}
{% endfor %}
</fieldset>
</div>
<form method="post" class="cms" novalidate>
<div class="wrapper" style="max-width: 300px">
{% csrf_token %}
<fieldset>
{{form.non_field_errors}}
{% for field in form %}
{% include 'cms/formfield.html' with field=field %}
{% endfor %}
</fieldset>
</div>
<div class="edit page">
<button><img src="{% static 'cms/save.png' %}"></button>
</div>
</form>
<div class="edit page">
<button><img src="{% static 'cms/save.png' %}"></button>
</div>
</form>
{% endblock %}

Wyświetl plik

@ -1,3 +1 @@
from django.test import TestCase
# Create your tests here.

Wyświetl plik

@ -0,0 +1,2 @@
[settings]
known_third_party=cms

Wyświetl plik

@ -15,13 +15,14 @@ repos:
rev: main
hooks:
- id: isort
- repo: https://github.com/pycqa/flake8
rev: master
hooks:
- id: flake8
args: ['--line-length', '88']
- repo: https://github.com/psf/black
rev: main
hooks:
- id: black
- repo: https://github.com/pycqa/flake8
rev: main
hooks:
- id: flake8

Wyświetl plik

@ -1,25 +1,16 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
from cms.decorators import page_model, section_model
from cms.models import BasePage, BaseSection
from django.db import models
from django.utils.translation import gettext_lazy as _
@page_model
class Page(BasePage):
"""Add custom fields here. Already existing fields: title, slug,
number, menu
"""
pass
@section_model
class Section(BaseSection):
"""Add custom fields here. Already existing fields: title, type,
number, content, image, video, href
"""
page = models.ForeignKey(Page, related_name="sections", on_delete=models.PROTECT)

Wyświetl plik

@ -3,77 +3,77 @@
<!DOCTYPE html>
<html lang="{{lang}}">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="{% static 'favicon.png' %}">
<link rel="stylesheet" href="{% static 'main.scss.css' %}">
<link rel="stylesheet" href="{% static 'hamburgers.css' %}">
<title>{% block title %}Awesome Website{% endblock %}</title>
{% block extrahead %}{% endblock %}
</head>
<body>
{% block main %}
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="{% static 'favicon.png' %}">
<link rel="stylesheet" href="{% static 'main.scss.css' %}">
<link rel="stylesheet" href="{% static 'hamburgers.css' %}">
<title>{% block title %}Awesome Website{% endblock %}</title>
{% block extrahead %}{% endblock %}
</head>
<body>
{% block main %}
<header>
{% block header %}
<h1><a href="/">Awesome Website</a></h1>
{% endblock %}
</header>
<nav>
{% block nav %}
{% if pages %}
<ul id="menu">
{% for p in pages %}
<li><a href="{% if p.slug %}{% url 'cms:page' p.slug %}{% else %}{% url 'cms:page' %}{% endif %}" {% if p.pk == page.pk %}class="current"{% endif %}>{{p.title}}</a></li>
{% endfor %}
{% if perms.cms_page_create %}
<li><a class="edit" href="{% url 'cms:createpage' %}">+ {% trans 'new page' %}</a></li>
{% endif %}
</ul>
{% endif %}
<button class="hamburger hamburger--collapse" id='hamburger'>
<span class="hamburger-box">
<span class="hamburger-inner"></span>
</span>
</button>
{% endblock %}
</nav>
<article>
{% block content %}
{% endblock %}
</article>
<footer>
{% block footer %}
{% endblock %}
</footer>
<header>
{% block header %}
<h1><a href="/">Awesome Website</a></h1>
{% endblock %}
</header>
<script>
document.addEventListener("DOMContentLoaded", function(event) {
var hamburger = document.getElementById('hamburger');
var menu = document.getElementById('menu');
hamburger.addEventListener('click', function(e) {
hamburger.classList.toggle('is-active');
menu.classList.toggle('visible');
});
<nav>
{% block nav %}
{% if pages %}
<ul id="menu">
{% for p in pages %}
<li><a href="{% if p.slug %}{% url 'cms:page' p.slug %}{% else %}{% url 'cms:page' %}{% endif %}" {% if p.pk == page.pk %}class="current"{% endif %}>{{p.title}}</a></li>
{% endfor %}
{% if perms.cms_page_create %}
<li><a class="edit" href="{% url 'cms:createpage' %}">+ {% trans 'new page' %}</a></li>
{% endif %}
</ul>
{% endif %}
<button class="hamburger hamburger--collapse" id='hamburger'>
<span class="hamburger-box">
<span class="hamburger-inner"></span>
</span>
</button>
{% endblock %}
</nav>
<article>
{% block content %}
{% endblock %}
</article>
<footer>
{% block footer %}
{% endblock %}
</footer>
{% endblock %}
<script>
document.addEventListener("DOMContentLoaded", function(event) {
var hamburger = document.getElementById('hamburger');
var menu = document.getElementById('menu');
hamburger.addEventListener('click', function(e) {
hamburger.classList.toggle('is-active');
menu.classList.toggle('visible');
});
var links = document.querySelectorAll('a');
for (var link of links) {
var a = new RegExp('/' + window.location.host + '/');
if (!a.test(link.href)) {
link.addEventListener('click', function(event) {
event.preventDefault();
event.stopPropagation();
window.open(this.href, '_blank');
});
}
}
});
</script>
{% block extrabody %}{% endblock %}
</body>
var links = document.querySelectorAll('a');
for (var link of links) {
var a = new RegExp('/' + window.location.host + '/');
if (!a.test(link.href)) {
link.addEventListener('click', function(event) {
event.preventDefault();
event.stopPropagation();
window.open(this.href, '_blank');
});
}
}
});
</script>
{% block extrabody %}{% endblock %}
</body>
</html>

Wyświetl plik

@ -1,20 +1,20 @@
{% load i18n cms %}
<section class="contact">
<div class="wrapper">
<div class="title">
<h1>{{section.title}}</h1>
<div class="wrapper">
<div class="title">
<h1>{{section.title}}</h1>
</div>
<div class="form">
<form method="post" class="cms">
{% csrf_token %}
{% for field in form %}
{% include 'cms/formfield.html' with field=field %}
{% endfor %}
<button class="button" name="section" value="{{section.pk}}">{% trans 'Send' %}</button>
</form>
</div>
</div>
<div class="form">
<form method="post" class="cms">
{% csrf_token %}
{% for field in form %}
{% include 'cms/formfield.html' with field=field %}
{% endfor %}
<button class="button" name="section" value="{{section.pk}}">{% trans 'Send' %}</button>
</form>
</div>
</div>
{% editsection '<img src="/static/cms/edit.png">' %}
{% editsection '<img src="/static/cms/edit.png">' %}
</section>

Wyświetl plik

@ -1,15 +1,15 @@
{% load thumbnail i18n cms %}
<section class="images">
<div class="images">
{% for image in section.images.all %}
<div class="image">
<div>
<img src="{% thumbnail image.image 700x700 %}">
</div>
</div>
{% endfor %}
</div>
<div class="images">
{% for image in section.images.all %}
<div class="image">
<div>
<img src="{% thumbnail image.image 700x700 %}">
</div>
</div>
{% endfor %}
</div>
{% editsection '<img src="/static/cms/edit.png">' %}
{% editsection '<img src="/static/cms/edit.png">' %}
</section>

Wyświetl plik

@ -1,17 +1,17 @@
{% load i18n cms %}
<section class="text">
<div class="wrapper">
<div class="title">
<h1>
{{section.title}}
</h1>
<div class="wrapper">
<div class="title">
<h1>
{{section.title}}
</h1>
</div>
<div class="content">
{% eval section.content %}
</div>
</div>
<div class="content">
{% eval section.content %}
</div>
</div>
{% editsection '<img src="/static/cms/edit.png">' %}
{% editsection '<img src="/static/cms/edit.png">' %}
</section>

Wyświetl plik

@ -1,13 +1,13 @@
{% load embed_video_tags i18n cms %}
<section class="video">
{% if section.video %}
<div class="video">
<div class="iframe">
{% video section.video '800x600' %}
</div>
</div>
{% endif %}
{% if section.video %}
<div class="video">
<div class="iframe">
{% video section.video '800x600' %}
</div>
</div>
{% endif %}
{% editsection '<img src="/static/cms/edit.png">' %}
{% editsection '<img src="/static/cms/edit.png">' %}
</section>

Wyświetl plik

@ -1,8 +1,7 @@
from django.utils.translation import gettext_lazy as _
from cms.decorators import section_view
from cms.forms import ContactForm
from cms.views import SectionFormView, SectionView
from django.utils.translation import gettext_lazy as _
@section_view

Wyświetl plik

@ -34,6 +34,7 @@ setup(
"easy-thumbnails",
"libsass",
"markdown",
"pre-commit",
"psycopg2",
"pylibmc",
],