Pull to refresh

Где моя кнопка?

Reading time3 min
Views751
«Где моя кнопка? Почему она неактивна?» Как часто программист может слышать эти слова от пользователей своего продукта? Скорее всего, более чем очень часто для того, чтобы задуматься, а может быть продукт сам должен отвечать на этот вопрос пользователей.

Вот приблизительно так выглядит обычная неактивная кнопка, которая, по какой-либо причине, в данный момент не доступна пользователю:

Неактивная молчаливая кнопка
Она безмолвна, не интерактивна и не представляет пользователю ни малейшей информации о том, почему она находится в этом состоянии и при каких условиях станет активной. А вот как могла бы выглядеть «облагороженная», более информативная неактивная кнопка:

Неактивная облагороженная кнопка

В чем же соль, спросите вы? А вот о том, как можно описывать правила на языке программирования Python, чтобы эти правила одновременно вычисляли статус активности кнопки и предоставляли отчет о причине в случае если кнопка должна быть деактивирована, я бы и хотел рассказать в этой статье.

Используя библиотеку собственного изготовления можно описать следующее правило на языке программирования Python:

from rules import _and_, _or_

# The rule
def can_user_add_comment(user, topic):
    return _and_(
        lambda: (user, “Вы не авторизованы”),
        lambda: (user.is_status_active(), “Ваша учетная запись заблокирована”),
        lambda: _or_(
            lambda: (user.is_in_admin_group(), “Вы не администратор”),
            lambda: _and_(
                lambda: (topic.is_status_open(), “тема закрыта”),
                lambda: (user.has_enough_karma_to_add_comment(), “у Вас не достаточно кармы для комментирования”),
                lambda: (user.is_below_comments_per_minute_quota(), “Вы комментируете слишком интенсивно, повторите попытку через пару минут”),
            ),
        ),
    )

Классы логических операций принимают операнды, обернутые в lambda, которые будут вызваны для вычисления значений при необходимости. Т.е. вычисления происходят по «укороченной схеме», например, _and_ вычисляет значения своих аргументов до первого False, _or_ — до первого True. Каждый операнд состоит из собственно тестируемого значения (первый элемент кортежа) и сообщения (второй элемент кортежа), которое используется когда тестируемое значение приняло значение False. Так же, при необходимости, можно опускать сообщения для не значимых частей правила (операндов) с точки зрения информирования пользователя.

Тогда использовать это правило, например в шаблоне Mako, можно следующим образом:

<%
    user = get_authenticated_user()
    granted = can_user_add_comment(user, current_topic)
%>
<input 
    type=”button” 
    value=”Добавить комментарий” 
    title=”${”Добавить комментарий” if granted else unicode(granted)}”
    ${“disabled” if not granted else “”}
/>

Как видно из примера, объект granted, полученный в результате вызова функции can_user_add_comment(), в булевом контексте сообщает статус активности кнопки, а в строковом контексте сообщает причину, если в булевом контексте он вернул значение False. Причина неактивности кнопки записывается в ее title и извлекается пользователем при наведении на нее курсора мыши. По умолчанию в отчет попадают только конкретные сообщения, результат вычисления правил которых оказался False. Но так же возможно сгенерировать полный древовидный отчет о том, какие логические ветки всего правила вернули значение False. Для этого надо вызвать granted.build_report() или granted.build_html_report().

В арсенале библиотеки доступны операции _and_, _or_, _not_ и _true_.

В результате поиска в сети мне не удалось обнаружить что-либо похожее, поэтому пришлось написать свою библиотеку. Первая версия была выпущена совсем недавно. Мне будет очень интересно узнать о вашем опыте решения этой маленькой, но иногда довольно актуальной, задачи.
Tags:
Hubs:
Total votes 54: ↑44 and ↓10+34
Comments31

Articles