bem-site / bem-forum-content-ru

Content BEM forum for Russian speak users
MIT License
56 stars 6 forks source link

Не правильно отрабатывает bemhtml шаблон #37

Open 4ok opened 10 years ago

4ok commented 10 years ago
block('content')
(
    tag()('article'),

    elemMatch()
    (
        tag()(this.ctx.elem)
    )
);

Никак не пойму, почему не отрабатывает этот шаблон, а конкретно не подхватывает tag()('article'), если убрать из шаблона elemMatch() то тэг проставляется

tadatuta commented 10 years ago

Насколько я понимаю, elemMatch добавляет дополнительные предикаты к предикатам про элемент и когда там оказывается пусто, то этот матчер в результате отрабатывает как на все элементы, так и на сам блок. И т.к. в контексте блока this.ctx.elem оказывается undefined, блок content так и остается div-ом. Судить баг ли это не возьмусь. Призываю @veged.

А чтобы прямо сейчас заработало как ожидается, можно написать, например, так

block('content')(
    tag()('article'),
    elemMatch(function() { return this.ctx.elem; })(
        tag()(function() {
            return this.elem;
        })
    )
);
veged commented 10 years ago

багом можно считать тот факт, что на пустой elemMatch никто не ругается — можно завести issue в bem-xjst

tadatuta commented 10 years ago

Завел https://github.com/bem/bem-xjst/issues/18

4ok commented 10 years ago

А как правильно составить шаблон, после того как поправят баг? Я думаю понятно, что я хотел получить в итоге

veged commented 10 years ago

правильный шаблон уже написал @tadatuta (если мы оба правильно поняли, что хотелось получить в итоге)

4ok commented 10 years ago

Спасибо, разобрался

qfox commented 10 years ago

У нас в доках везде примеры match(this.ctx.something === 'something'), хотя это нужно оборачивать в функцию. ;-(

veged commented 10 years ago

в функцию нужно оборачивать, если предикат может иногда падать из-за undefined — но this.ctx обеспечивается в самом базисе (https://github.com/bem/bem-core/blob/v2/common.blocks/i-bem/i-bem.bemhtml#L164), а вот предикаты типа this.bla.bla нужно оборачивать, т.к. this.bla может не быть

qfox commented 10 years ago

Только что пытался собирать проект в enb, который нормально собирался тулзами, и this.ctx там не всегда был объектом. В т.ч. он был и массивом, и undefined. Это баг? Посмотреть почему?

veged commented 10 years ago

да, давай разберёмся (лучше в отдельном тиките) — потому что я вот привёл исходники базовых шаблонов и вроде как оно всегда должно быть

tadatuta commented 10 years ago

@zxqfox предположу, что разница где-то в используемых версиях bem-xjst. ENB или bem-tools сами по себе как-то влиять на шаблоны не должны.

в случае bem-tools обычно bem-xjst поставляется в виде npm-депендов bem-core, а при использовании ENB — ставится на уровне проекта.

qfox commented 10 years ago

Так. В enb генератором bem-core с зависимостью "enb-bemxjst": "1.2.0",, на уровне проекта та же версия. И "bem-xjst": "0.4.0",.

В случае bem-tools та же версия, и ошибка при сборке не повторяется.

копаю дальше.

tadatuta commented 10 years ago

@zxqfox еще могу предположить отличия на уровне технологии для сборки deps-ов. если для ENB используется deps, можно попробовать перейти на deps-old — это порт логики из bem-tools. пока других идей нет.

qfox commented 10 years ago

@tadatuta а что вообще должно быть в this.ctx, когда на входе из bemjson.js в content пришла строка или массив?

({
  block: 'something', // typeof this.ctx === 'object' ?
  content: [ // typeof this.ctx === 'array' ?
    'Hello World!' // typeof this.ctx === 'string' ?
  ]
})

/cc @veged

tadatuta commented 10 years ago

@zxqfox Не уверен, что понимаю вопрос, но в this.ctx рекурсивно падают узлы BEMJSON-дерева, дальше перебираются матчеры и выполняется тело сматчившегося шаблона.

Т.е. в твоем примере при матче на block('something') в this.ctx окажется весь узел, который ты приводишь. При матче по моде content, вернется содержимое this.ctx.content, в приведенном примере это массив. Для него выполнится рекурсивный apply на каждый элемент (в этот момент в this.ctx окажется строка), отработает матч на isSimple() и в буфер приплюсуется 'Hello World!'.

qfox commented 10 years ago

@tadatuta Да, вопрос был поставлен некорректно. Спасибо за подтверждение.

В общем, там кардинально отличаются сгенерированные bemhtml.js. В bem-tools match(thix.ctx.type === 'something') превращается в if (__$ctx.ctx.type ..., в enb оно остается как есть, и поэтому валится.

Если сделать так: match(this.ctx && this.ctx.type === "something")(, или завернуть в function, то начинает отрабатывать и перестает валится. Считать ли это багом или нет — сложно сходу сказать, не понятно, как именно оно должно работать.

Но факт остается фактом — match(thix.ctx.type === 'something') в enb валится, когда в thix.ctx по какой-то причине приходит undefined. С тулзами такой проблемы нет.

4ok commented 10 years ago

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

4ok commented 10 years ago

И вообще, elemMatch условие не должно попадать, когда this.ctx.elem пустой, а в приведеном шаблоне от @tadatuta это не так

qfox commented 10 years ago

@4ok ну как, если оборачивать условие соответствия в колбек — то контекст туда нормально передается, у меня проблема в том, что this.ctx === undefined, и сам шаблон падает при запуске.

т.е. в elemMatch(условие)(инструкции) не пустое условие, как в твоем случае, а такое, что генерирует код, падающий при запуске. но только при сборке в enb.

tadatuta commented 10 years ago

@zxqfox Леша, а уж не в том ли дело, что bem-tools в твоем случае компилируют шаблоны в продакшен-режиме, а ENB — в дев?

qfox commented 10 years ago

@tadatuta угу, есть и такая вероятность. Пишу тест для bem-xjst, надеюсь поймать ошибку.

4ok commented 10 years ago

Прод или дев, ведь не должно быть разницы, разве нет?

tadatuta commented 10 years ago

@4ok отличие в режимах сборки в том, что в дев-режиме шаблоны просто конкатенируются с кодом «ядра» и друг с другом, а в продакшен-режиме происходит компиляция исходного кода в более эффективный с точки зрения скорости выполнения.

в результате, как и написал @zxqfox, имеем следующую ситуацию: в продакшене block('something').match(thix.ctx.type === 'something') превратится в (псевдокод) if(__$ctx.block === 'something' && __$ctx.type === 'something'), а в дев-режиме, как и должно быть в JS, сначала выполнится операция thix.ctx.type === 'something', а потом ее результат будет передан в match(). т.е. это сравнение будет выполняться даже тогда, когда не выполняется матч на block('something'), т.к. обрабатывается apply() на какой-то совсем другой узел.

qfox commented 10 years ago

@tadatuta простите, не отписал, но дело в этом, да. Будем как-то что-то делать? Вообще, надо бы.

mursya commented 10 years ago

если вы хотите задать вопрос команде, то ставьте еще и метку asktheteam ;) спасибо!

qfox commented 10 years ago

https://github.com/zxqfox/bem-xjst/commit/c7aef30944893f57d54708ba7c97b711ef0d65c1 В общем, вот. Как чинить пока не знаю. При чем, валится как в продакшн, так и в дев режимах.

tadatuta commented 10 years ago

@zxqfox так мы специально сделали, чтобы все матчи, содержащие обращение к полям this требовали анонимной функции — это гарантирует работоспособность шаблона в dev-режиме.

qfox commented 10 years ago

@tadatuta тогда надо документацию поправить. но с анонимками будет сложно оптимизировать

qfox commented 10 years ago

@tadatuta ну и да, конкретно этот тест от флага оптимизации не меняет поведения и не перестает падать.

qfox commented 10 years ago

После обсуждений с @andrewblond и @veged выяснили, что надо обновить доку и синтаксис match(this.ctx.something) зависит от базовых используемых блоков и лучше всегда кастомные вещи заворачивать в анонимную функцию. т.е. не match(this.ctx), a match(function () { return thix.ctx; })