Skip to content
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

BEMXJST: extend() mode fix #180 #363

Merged
merged 1 commit into from
Oct 20, 2016
Merged
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
51 changes: 50 additions & 1 deletion docs/en/5-templates-syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ Result of BEMHTML templating:
* [cls](#cls)
* [replace](#replace)
* [wrap](#wrap)
* [extend](#extend)
* [User-defined modes](#user-defined-modes)

#### def
Expand Down Expand Up @@ -540,6 +541,53 @@ Result of BEMHTML templating:
<div class="wrap"><div class="quote">Docendo discimus</div></div>
```

#### extend

`extend` mode allows you to extend context of template.

Example:

```js
// BEMJSON
{ block: 'action' }
```

Templates:

```js
block('action').extend()({ 'ctx.type': 'Sale', sale: '50%' });
block('action').content()(function() {
return this.ctx.type + ' ' + this.sale;
});
```

Result of BEMHTML apply:

```html
<div class="action">Sale 50%</div>
```

`extend()` may used as a data proxy to all child nodes.

Example:

```js
// Templates
block('page').extend()({ meaning: 42 });
block('*').attrs()(function() { return { life: this.meaning }; });
```

```js
// BEMJSON
{ block: 'page', content: { block: 'wrap', content: { block: 'para' } }
```

```html
<div class="page" life="42"><div class="wrap" life="42"><div class="para"
life="42"></div></div></div>
```


## User-defined modes

You can define your own mode and use it in the template body. Example:
Expand Down Expand Up @@ -589,7 +637,8 @@ More information about [apply()](7-runtime.md#apply).

## BEMTREE

Only the [def](#def), [content](#content), [replace](#replace) and [wrap](#wrap) modes are used by the BEMTREE engine. User-defined modes can also be used. The other modes described in the documentation above can only be used in BEMHTML.
Only the [def](#def), [content](#content), [replace](#replace),
[extend](#extend) and [wrap](#wrap) modes are used by the BEMTREE engine. User-defined modes can also be used. The other modes described in the documentation above can only be used in BEMHTML.

***

Expand Down
49 changes: 48 additions & 1 deletion docs/ru/5-templates-syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ block('link')(
* [cls](#cls)
* [replace](#replace)
* [wrap](#wrap)
* [extend](#extend)
* [Пользовательские режимы](#Пользовательские-режимы)

#### def
Expand Down Expand Up @@ -536,6 +537,52 @@ block('quote').wrap()(function() {
</div>
```

#### extend

Доопределить контекст исполнения шаблонов.

Пример:

```js
// BEMJSON
{ block: 'action' }
```

Шаблоны:

```js
block('action').extend()({ 'ctx.type': 'Sale', sale: '50%' });
block('action').content()(function() {
return this.ctx.type + ' ' + this.sale;
});
```

Результат шаблонизации BEMHTML:

```html
<div class="action">Sale 50%</div>
```

`extend()` может использоваться для прокидывания данных во все дочерние узлы
через контекст выполнения шаблонов.

Пример:

```js
block('page').extend()({ meaning: 42 });
block('*').attrs()(function() { return { life: this.meaning }; });
```

```js
// BEMJSON
{ block: 'page', content: { block: 'wrap', content: { block: 'para' } }
```

```html
<div class="page" life="42"><div class="wrap" life="42"><div class="para"
life="42"></div></div></div>
```

## Пользовательские режимы

Вы можете определить свой режим и использовать его в теле шаблона.
Expand Down Expand Up @@ -586,7 +633,7 @@ block('control')(
## BEMTREE

Движком BEMTREE используются только режимы [def](#def), [content](#content) и
режимы-хелперы [replace](#replace) и [wrap](#wrap). Пользовательские режимы тоже могут быть использованы. Остальные режимы, описанные в документации выше, применимы только к BEMHTML.
режимы-хелперы [replace](#replace), [extend](#extend) и [wrap](#wrap). Пользовательские режимы тоже могут быть использованы. Остальные режимы, описанные в документации выше, применимы только к BEMHTML.

***

Expand Down
15 changes: 15 additions & 0 deletions lib/bemxjst/match.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ var tree = require('./tree');
var PropertyMatch = tree.PropertyMatch;
var AddMatch = tree.AddMatch;
var WrapMatch = tree.WrapMatch;
var ExtendMatch = tree.ExtendMatch;
var CustomMatch = tree.CustomMatch;

function MatchProperty(template, pred) {
Expand Down Expand Up @@ -57,6 +58,17 @@ MatchWrap.prototype.exec = function exec(context) {
return res;
};

function MatchExtend(template) {
this.template = template;
this.wrap = null;
}

MatchExtend.prototype.exec = function exec(context) {
var res = this.ext !== context.ctx;
this.ext = context.ctx;
return res;
};

function AddWrap(template, pred) {
this.template = template;
this.key = pred.key;
Expand All @@ -81,6 +93,9 @@ function MatchTemplate(mode, template) {
this.predicates[j] = new MatchNested(this, pred);
else
this.predicates[j] = new MatchProperty(this, pred);
} else if (pred instanceof ExtendMatch) {
j--;
postpone.push(new MatchExtend(this));
} else if (pred instanceof AddMatch) {
this.predicates[j] = new AddWrap(this, pred);
} else if (pred instanceof CustomMatch) {
Expand Down
12 changes: 6 additions & 6 deletions lib/bemxjst/tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ ExtendMatch.prototype.wrapBody = function wrapBody(body) {

var keys = Object.keys(body);
for (var i = 0; i < keys.length; i++)
changes['ctx.' + keys[i]] = body[keys[i]];
changes[keys[i]] = body[keys[i]];

return local(changes)(function preApplyCtx() {
return applyCtx(this.ctx);
Expand All @@ -122,7 +122,7 @@ ExtendMatch.prototype.wrapBody = function wrapBody(body) {
var obj = body.call(this);
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; i++)
changes['ctx.' + keys[i]] = obj[keys[i]];
changes[keys[i]] = obj[keys[i]];

return local(changes)(function preApplyCtx() {
return applyCtx(this.ctx);
Expand Down Expand Up @@ -399,10 +399,6 @@ Tree.prototype.applyMode = function applyMode(args, mode) {
return this.mode(mode);
};

Tree.prototype.wrap = function wrap() {
return this.def.apply(this, arguments).match(new WrapMatch(this.refs));
};

Tree.prototype.xjstOptions = function xjstOptions(options) {
this.queue.push(new Item(this, [
new CompilerOptions(options)
Expand Down Expand Up @@ -488,6 +484,10 @@ Tree.prototype.prependContent = function prependContent() {
.match(new AddMatch('prependContent', this.refs));
};

Tree.prototype.wrap = function wrap() {
return this.def.apply(this, arguments).match(new WrapMatch(this.refs));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

позиция этой декларации в файле разве что-то меняет?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zxqfox меняет моё отношение к файлу. Порядок хочу. Чтобы wrap, extend, replace были рядом.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

почему ты не хочешь отдельно делать стилические правки, а отдельно функциональные? религия?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zxqfox потому что не вижу смысла в такой бюрократии.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ты эту правку мог влить отдельно, поскольку она никак не влияет на бизнес логику, но «чтобы не разводить бюрократию» она теперь висит здесь и только путает. Это крайне нелогично.

};

Tree.prototype.replace = function replace() {
return this.def.apply(this, arguments).match(new ReplaceMatch(this.refs));
};
Expand Down
62 changes: 44 additions & 18 deletions test/modes-extend-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,33 +13,59 @@ describe('Modes extend', function() {

it('should support basic mode of operation', function () {
test(function() {
block('b1').content()('ok');
block('b1').elem('e').content()('extended');
block('b1').extend()(function() { return { elem: 'e' }; });
}, { block: 'b1' }, '<div class="b1__e">extended</div>');
block('b')(
extend()(function() { return { test: 'it work’s' }; }),
content()(function() { return this.test; })
);
}, { block: 'b' }, '<div class="b">it work’s</div>');
});

it('should support inline argument', function () {
test(function() {
block('b')(
extend()({ test: 'it work’s' }),
content()(function() { return this.test; })
);
}, { block: 'b' }, '<div class="b">it work’s</div>');
});

it('should have proper `this`', function () {
test(function() {
block('b1').content()('ok');
block('b1').elem('e').content()('extended');
block('b1').extend()(function() { return { elem: this.ctx.wtf }; });
}, { block: 'b1', wtf: 'e' }, '<div class="b1__e">extended</div>');
block('b')(
extend()(function() { return { test: this.ctx.wtf }; }),
content()(function() { return this.test; })
);
}, { block: 'b', wtf: 'it work’s' }, '<div class="b">it work’s</div>');
});

it('should work as a singular function', function () {
it('should proxy data', function () {
test(function() {
block('b1').content()('ok');
block('b1').elem('e').content()('extended');
block('b1')(extend()(function() { return { elem: 'e' }; }));
}, { block: 'b1' }, '<div class="b1__e">extended</div>');
block('b').extend()(function() { return { test: 42 }; });
block('*').attrs()(function() { return { life: this.test }; });
},
{
block: 'b',
content: { block: 'a', content: { block: 'c' } }
},
'<div class="b" life="42"><div class="a" life="42">' +
'<div class="c" life="42"></div></div></div>');
});

it('should support inline argument', function () {
it('should extend ctx', function () {
test(function() {
block('b').extend()(function() { return { 'ctx.content': 42 }; });
},
{ block: 'b' },
'<div class="b">42</div>');
});

it('should support applyNext', function () {
test(function() {
block('b1').content()('ok');
block('b1').elem('e').content()('extended');
block('b1').extend()({ elem: 'e' });
}, { block: 'b1' }, '<div class="b1__e">extended</div>');
block('b').extend()(function() { return { 'ctx.content': 1 }; });
block('b').extend()(function() {
return { 'ctx.attrs': { id: 'test' } }; });
},
{ block: 'b' },
'<div class="b" id="test">1</div>');
});
});