rails_blocks 0.5.4.pre.alpha.pre.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +15 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +10 -0
  5. data/Gemfile +10 -0
  6. data/Guardfile +43 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +31 -0
  9. data/Rakefile +12 -0
  10. data/app/assets/javascripts/block.coffee +98 -0
  11. data/app/assets/javascripts/jblocks.coffee +95 -0
  12. data/app/assets/javascripts/jquery_bem.coffee +4 -0
  13. data/app/assets/javascripts/jquery_bem_copy.coffee +486 -0
  14. data/app/assets/javascripts/main.coffee +10 -0
  15. data/app/assets/javascripts/rails_blocks.js +5 -0
  16. data/app/assets/stylesheets/rails_blocks.sass +94 -0
  17. data/app/helpers/block_helper.rb +122 -0
  18. data/app/views/rails_blocks/element.slim +2 -0
  19. data/coffeelint.json +12 -0
  20. data/config/spring.rb +1 -0
  21. data/config.ru +7 -0
  22. data/lib/rails_blocks/blocks/block.rb +28 -0
  23. data/lib/rails_blocks/blocks/element.rb +6 -0
  24. data/lib/rails_blocks/blocks/renderer.rb +13 -0
  25. data/lib/rails_blocks/configuration.rb +27 -0
  26. data/lib/rails_blocks/engine.rb +29 -0
  27. data/lib/rails_blocks/exceptions.rb +4 -0
  28. data/lib/rails_blocks/initializers/rails_blocks.rb +0 -0
  29. data/lib/rails_blocks/levels.rb +7 -0
  30. data/lib/rails_blocks/names.rb +60 -0
  31. data/lib/rails_blocks/path.rb +108 -0
  32. data/lib/rails_blocks/version.rb +3 -0
  33. data/lib/rails_blocks.rb +24 -0
  34. data/lib/tasks/rails_blocks.rake +8 -0
  35. data/rails_blocks.gemspec +39 -0
  36. data/spec/helpers/block_helper_spec.rb +58 -0
  37. data/spec/internal/app/blocks/app/block3/_mod1.slim +0 -0
  38. data/spec/internal/app/blocks/app/block3/_mod2_value2.slim +0 -0
  39. data/spec/internal/app/blocks/app/block3/block3.slim +0 -0
  40. data/spec/internal/app/blocks/common/block1/_mod1.slim +0 -0
  41. data/spec/internal/app/blocks/common/block1/_mod2_value2.slim +0 -0
  42. data/spec/internal/app/blocks/common/block1/block1.slim +2 -0
  43. data/spec/internal/app/blocks/common/block2/__elem1.slim +0 -0
  44. data/spec/internal/app/blocks/common/block2/__elem1_mod1.slim +0 -0
  45. data/spec/internal/app/blocks/common/block2/__elem1_mod2_val2.slim +0 -0
  46. data/spec/internal/app/blocks/common/block2/block2.slim +0 -0
  47. data/spec/internal/app/blocks/common/block3/_mod1.slim +0 -0
  48. data/spec/internal/app/blocks/common/block3/_mod2_value2.slim +0 -0
  49. data/spec/internal/app/blocks/common/block3/block3.slim +0 -0
  50. data/spec/internal/app/controllers/admin/admin_test_controller.rb +2 -0
  51. data/spec/internal/app/controllers/admin_controller.rb +2 -0
  52. data/spec/internal/app/controllers/application_controller.rb +2 -0
  53. data/spec/internal/config/database.yml +3 -0
  54. data/spec/internal/config/initializers/rails_blocks.rb +7 -0
  55. data/spec/internal/config/routes.rb +3 -0
  56. data/spec/internal/db/schema.rb +3 -0
  57. data/spec/internal/log/.gitignore +1 -0
  58. data/spec/internal/log/test.log +8 -0
  59. data/spec/internal/public/favicon.ico +0 -0
  60. data/spec/rails_blocks/blocks/block_spec.rb +11 -0
  61. data/spec/rails_blocks/configuration_spec.rb +29 -0
  62. data/spec/rails_blocks/names_spec.rb +66 -0
  63. data/spec/rails_blocks/path_spec.rb +70 -0
  64. data/spec/rails_blocks_spec.rb +104 -0
  65. data/spec/spec_helper.rb +112 -0
  66. metadata +368 -0
@@ -0,0 +1,486 @@
1
+ ### @required jQuery ###
2
+
3
+ (($) ->
4
+
5
+ ###*
6
+ # Base BEM class.
7
+ # @constructor
8
+ ###
9
+
10
+ BEM = (config) ->
11
+
12
+ ###*
13
+ # Default configuration.
14
+ # @type {Object}
15
+ ###
16
+
17
+ @config = config or {}
18
+ @blockClassRe = @buildBlockClassRe()
19
+ @elemClassRe = @buildElemClassRe()
20
+ @modClassRe = @buildModClassRe()
21
+ return
22
+
23
+ ###*
24
+ # Get parent block of element.
25
+ # @public
26
+ #
27
+ # @param {Object} $this
28
+ # @return {Object}
29
+ ###
30
+
31
+ BEM::getBlock = ($this) ->
32
+ blockClass = @getBlockClass($this)
33
+ block = $this.closest('.' + blockClass)
34
+ block.selector = blockClass
35
+ block
36
+
37
+ ###*
38
+ # Switch block context.
39
+ # @public
40
+ #
41
+ # @param {Object} $this
42
+ # @param {String} block
43
+ # @param {String} [elem]
44
+ # @return {Object}
45
+ ###
46
+
47
+ BEM::switchBlock = ($this, block, elem) ->
48
+ `var elem`
49
+ elem = elem or null
50
+ if elem then ($this.selector = @buildSelector(
51
+ block: block
52
+ elem: elem)) else ($this.selector = @buildSelector(block: block))
53
+ $this
54
+
55
+ ###*
56
+ # Find element in block.
57
+ # @public
58
+ #
59
+ # @param {Object} $this DOM element
60
+ # @param {String} elemKey Element name
61
+ # @return {Object}
62
+ ###
63
+
64
+ BEM::findElem = ($this, elemKey) ->
65
+ blockClass = @getBlockClass($this)
66
+ elemName = @buildElemClass(blockClass, elemKey)
67
+ elem = $this.find('.' + elemName)
68
+ elem
69
+
70
+ ###*
71
+ # Get value of modifier.
72
+ # @public
73
+ #
74
+ # @param {Object} $this
75
+ # @param {String} modKey
76
+ # @return {String}
77
+ ###
78
+
79
+ BEM::getMod = ($this, modKey) ->
80
+ mods = @extractMods($this.first())
81
+ if mods[modKey] != undefined
82
+ return mods[modKey]
83
+ null
84
+
85
+ ###*
86
+ # Check modifier of element.
87
+ # @public
88
+ #
89
+ # @param {Object} $this
90
+ # @param {String} modKey
91
+ # @param {String} [modVal]
92
+ # @return {Boolean}
93
+ ###
94
+
95
+ BEM::hasMod = ($this, modKey, modVal) ->
96
+ mods = @extractMods($this.first())
97
+ if modVal
98
+ if mods[modKey] == modVal
99
+ return true
100
+ else
101
+ if mods[modKey]
102
+ return true
103
+ false
104
+
105
+ ###*
106
+ # Set modifier on element.
107
+ # @public
108
+ #
109
+ # @param {Object} $this
110
+ # @param {String} modKey
111
+ # @param {String} [modVal]
112
+ # @param {Object}
113
+ ###
114
+
115
+ BEM::setMod = ($this, modKey, modVal) ->
116
+ self = this
117
+ selector = $this.selector
118
+ $this.each ->
119
+ current = $(this)
120
+ current.selector = selector
121
+ mods = self.extractMods(current)
122
+ baseName = self.getBaseClass(current)
123
+ if mods[modKey] != undefined
124
+ oldModName = self.buildModClass(baseName, modKey, mods[modKey])
125
+ current.removeClass oldModName
126
+ if modVal != false
127
+ newModName = self.buildModClass(baseName, modKey, modVal)
128
+ current.addClass(newModName).trigger 'setmod', [
129
+ modKey
130
+ modVal
131
+ ]
132
+ return
133
+ $this
134
+
135
+ ###*
136
+ # Delete modifier on element.
137
+ # @public
138
+ #
139
+ # @param {Object} $this
140
+ # @param {String} modKey
141
+ # @param {String} [modVal]
142
+ # @param {Object}
143
+ ###
144
+
145
+ BEM::delMod = ($this, modKey, modVal) ->
146
+ self = this
147
+ selector = $this.selector
148
+ $this.each ->
149
+ `var modName`
150
+ current = $(this)
151
+ current.selector = selector
152
+ mods = self.extractMods(current)
153
+ baseName = self.getBaseClass(current)
154
+ if modVal
155
+ if mods[modKey] == modVal
156
+ modName = self.buildModClass(baseName, modKey, mods[modKey])
157
+ else
158
+ modName = self.buildModClass(baseName, modKey, mods[modKey])
159
+ current.removeClass(modName).trigger 'delmod', [
160
+ modKey
161
+ modVal
162
+ ]
163
+ return
164
+ $this
165
+
166
+ ###*
167
+ # Filtering elements by modifier.
168
+ # @public
169
+ #
170
+ # @param {Object} $this
171
+ # @param {String} modKey
172
+ # @param {String} [modVal]
173
+ # @param {Boolean} [inverse]
174
+ # @return {Object}
175
+ ###
176
+
177
+ BEM::byMod = ($this, modKey, modVal, inverse) ->
178
+ `var inverse`
179
+ `var modVal`
180
+ self = this
181
+ modVal = modVal or null
182
+ inverse = inverse or false
183
+ selector = $this.selector
184
+ result = $()
185
+ $this.each ->
186
+ `var modName`
187
+ current = $(this)
188
+ current.selector = selector
189
+ mods = self.extractMods(current)
190
+ baseName = self.getBaseClass(current)
191
+ if modVal
192
+ if mods[modKey] == modVal
193
+ modName = self.buildModClass(baseName, modKey, mods[modKey])
194
+ else
195
+ if mods[modKey] != undefined
196
+ modName = self.buildModClass(baseName, modKey, mods[modKey])
197
+ result = result.add(if inverse then current.not('.' + modName) else current.filter('.' + modName))
198
+ return
199
+ result.selector = selector
200
+ result
201
+
202
+ ###*
203
+ # Get block names from element.
204
+ # @protected
205
+ #
206
+ # @param {Object|String} $this
207
+ # @return {Object}
208
+ ###
209
+
210
+ BEM::extractBlocks = ($this) ->
211
+ self = this
212
+ result = []
213
+ selectors = @getClasses($this)
214
+ $.each selectors, (i, sel) ->
215
+ type = self.getClassType(sel)
216
+ if type == 'block'
217
+ result.push sel
218
+ else if type == 'elem'
219
+ elem = sel.split(self.config.elemPrefix)
220
+ result.push elem[0]
221
+ return
222
+ result
223
+
224
+ ###*
225
+ # Get element names from element.
226
+ # @protected
227
+ #
228
+ # @param {Object} $this
229
+ # @return {Object}
230
+ ###
231
+
232
+ BEM::extractElems = ($this) ->
233
+ self = this
234
+ result = []
235
+ $.each self.getClasses($this), (i, className) ->
236
+ if self.getClassType(className) == 'elem'
237
+ elemName = className.split(self.config.elemPrefix)
238
+ result.push elemName[1]
239
+ return
240
+ result
241
+
242
+ ###*
243
+ # Get modifiers from element.
244
+ # @protected
245
+ #
246
+ # @param {Object} $this
247
+ # @return {Object}
248
+ ###
249
+
250
+ BEM::extractMods = ($this) ->
251
+ self = this
252
+ result = {}
253
+ $this.each ->
254
+ `var $this`
255
+ $this = $(this)
256
+ $.each self.getClasses($this), (i, className) ->
257
+ `var modVal`
258
+ if self.getClassType(className) == 'mod'
259
+ re = self.buildModClassRe().exec(className)
260
+ modName = re[1].split(self.config.modDlmtr)
261
+ if modName[1] != undefined and modName[1] != false
262
+ modVal = modName[1]
263
+ else
264
+ modVal = true
265
+ result[modName[0]] = modVal
266
+ return
267
+ return
268
+ result
269
+
270
+ ###*
271
+ # Get classes names from element.
272
+ # @protected
273
+ #
274
+ # @param {Object} $this
275
+ # @return {Object}
276
+ ###
277
+
278
+ BEM::getClasses = ($this) ->
279
+ classes = undefined
280
+ result = []
281
+ if typeof $this == 'object'
282
+ if $this.selector.indexOf('.') == 0
283
+ classes = $this.selector.split('.')
284
+ else if $this.attr('class') != undefined
285
+ classes = $this.attr('class').split(' ')
286
+ else
287
+ return null
288
+ else
289
+ classes = $this.split('.')
290
+ $.each classes, (i, className) ->
291
+ if className != ''
292
+ result.push $.trim(className)
293
+ return
294
+ result
295
+
296
+ ###*
297
+ # Build regexp for blocks.
298
+ # @protected
299
+ #
300
+ # @return {RegExp}
301
+ ###
302
+
303
+ BEM::buildBlockClassRe = ->
304
+ new RegExp('^(' + @config.namePattern + ')$')
305
+
306
+ ###*
307
+ # Build regexp for elements.
308
+ # @protected
309
+ #
310
+ # @return {RegExp}
311
+ ###
312
+
313
+ BEM::buildElemClassRe = ->
314
+ new RegExp('^' + @config.namePattern + @config.elemPrefix + '(' + @config.namePattern + ')$')
315
+
316
+ ###*
317
+ # Build regexp for modifiers.
318
+ # @protected
319
+ #
320
+ # @return {RegExp}
321
+ ###
322
+
323
+ BEM::buildModClassRe = ->
324
+ new RegExp('^(?:' + @config.namePattern + '|' + @config.namePattern + @config.elemPrefix + @config.namePattern + ')' + @config.modPrefix + '(' + @config.namePattern + '((' + @config.modDlmtr + @config.namePattern + ')$|$))')
325
+
326
+ ###*
327
+ # Build class name for block.
328
+ # @protected
329
+ #
330
+ # @param {String} blockName
331
+ # @return {String}
332
+ ###
333
+
334
+ BEM::buildBlockClass = (blockName) ->
335
+ @config.blockPrefix + blockName
336
+
337
+ ###*
338
+ # Build class name for element.
339
+ # @protected
340
+ #
341
+ # @param {String} blockName
342
+ # @param {String} elemKey
343
+ # @return {String}
344
+ ###
345
+
346
+ BEM::buildElemClass = (blockName, elemKey) ->
347
+ blockName + @config.elemPrefix + elemKey
348
+
349
+ ###*
350
+ # Build class name for modifier.
351
+ # @protected
352
+ #
353
+ # @param {String} blockName
354
+ # @param {String} modKey
355
+ # @param {String} modVal
356
+ # @return {String}
357
+ ###
358
+
359
+ BEM::buildModClass = (baseClass, modKey, modVal) ->
360
+ if modVal != undefined and modVal != true
361
+ baseClass + @config.modPrefix + modKey + @config.modDlmtr + modVal
362
+ else
363
+ baseClass + @config.modPrefix + modKey
364
+
365
+ ###*
366
+ # Build selector from object or string.
367
+ # @private
368
+ #
369
+ # @param {String|Object}
370
+ # @param {String}
371
+ # @return {String}
372
+ ###
373
+
374
+ BEM::buildSelector = (selector, prefix) ->
375
+ `var prefix`
376
+ if prefix != ''
377
+ prefix = prefix or '.'
378
+ if typeof selector == 'object'
379
+ if selector.block != undefined
380
+ buildSelector = @buildBlockClass(selector.block)
381
+ if selector.elem != undefined
382
+ buildSelector = @buildElemClass(buildSelector, selector.elem)
383
+ if selector.mod != undefined
384
+ mod = selector.mod.split(':')
385
+ buildSelector = @buildModClass(buildSelector, mod[0], mod[1])
386
+ if buildSelector != undefined then prefix + buildSelector else prefix + selector
387
+
388
+ ###*
389
+ # Build class name for block.
390
+ # @protected
391
+ #
392
+ # @param {Object|String} $this
393
+ # @param {Number} [index]
394
+ # @return {String}
395
+ ###
396
+
397
+ BEM::getBlockClass = ($this, index) ->
398
+ `var index`
399
+ blockClasses = @extractBlocks($this)
400
+ index = index or 0
401
+ if index <= blockClasses.length - 1 then blockClasses[index] else null
402
+
403
+ ###*
404
+ # Get base class from element.
405
+ # @protected
406
+ #
407
+ # @param {Object} $this
408
+ # @return {String}
409
+ ###
410
+
411
+ BEM::getBaseClass = ($this) ->
412
+ self = this
413
+ baseClass = null
414
+ selectors = @getClasses($this)
415
+ $.each selectors, (i, sel) ->
416
+ classType = self.getClassType(sel)
417
+ if classType and classType != 'mod'
418
+ baseClass = sel
419
+ return
420
+ baseClass
421
+
422
+ ###*
423
+ # Get class type.
424
+ # @protected
425
+ #
426
+ # @param {String} className
427
+ # @return {String}
428
+ ###
429
+
430
+ BEM::getClassType = (className) ->
431
+ if @modClassRe.test(className)
432
+ return 'mod'
433
+ else if @elemClassRe.test(className)
434
+ return 'elem'
435
+ else if @blockClassRe.test(className)
436
+ return 'block'
437
+ null
438
+
439
+ ###*
440
+ # Create BEM instance.
441
+ ###
442
+
443
+ $.BEM = new BEM(
444
+ namePattern: '[a-zA-Z0-9-]+'
445
+ blockPrefix: 'b-'
446
+ elemPrefix: '__'
447
+ modPrefix: '--'
448
+ modDlmtr: '_')
449
+
450
+ ###*
451
+ # Extend jQuery object.
452
+ ###
453
+
454
+ $.fn.extend
455
+ block: ->
456
+ $.BEM.getBlock this
457
+ elem: (elemKey) ->
458
+ $.BEM.findElem this, elemKey
459
+ ctx: (block, elem) ->
460
+ $.BEM.switchBlock this, block, elem
461
+ mod: (modKey, modVal) ->
462
+ if typeof modVal == 'undefined'
463
+ modVal = null
464
+ if modVal == false
465
+ return $.BEM.delMod(this, modKey)
466
+ if modVal != null
467
+ $.BEM.setMod(this, modKey, modVal)
468
+ else
469
+ $.BEM.getMod(this, modKey)
470
+ setMod: (modKey, modVal) ->
471
+ $.BEM.setMod this, modKey, modVal
472
+ delMod: (modKey, modVal) ->
473
+ $.BEM.delMod this, modKey, modVal
474
+ hasMod: (modKey, modVal) ->
475
+ $.BEM.hasMod this, modKey, modVal
476
+ byMod: (modKey, modVal) ->
477
+ $.BEM.byMod this, modKey, modVal
478
+ byNotMod: (modKey, modVal) ->
479
+ $.BEM.byMod this, modKey, modVal, 'inverse'
480
+ toggleMod: (modKey, modVal1, modVal2) ->
481
+ if @hasMod(modKey, modVal1)
482
+ @delMod(modKey, modVal1).setMod modKey, modVal2
483
+ else
484
+ @delMod(modKey, modVal2).setMod modKey, modVal1
485
+ return
486
+ ) jQuery
@@ -0,0 +1,10 @@
1
+ $ ->
2
+ # $.extend $,
3
+ # RB:
4
+ # blocks: {}
5
+ # decl: (name, options) ->
6
+ # block = @createBlockDeclaration(name, options)
7
+ # if(options['init'] != undefined)
8
+ # options['init'].call(block)
9
+ # createBlockDeclaration: (name, options) ->
10
+ # return new Block name, options
@@ -0,0 +1,5 @@
1
+ //require 'jquery_bem_copy'
2
+ //=require 'jquery_bem'
3
+ //=require 'block'
4
+ //=require 'jblocks'
5
+ //=require 'main'
@@ -0,0 +1,94 @@
1
+ $bemBlockPrefix: 'b-'
2
+ $bemElementSeparator: '__'
3
+ $bemModifierSeparator: '--'
4
+
5
+ @function b-class($block)
6
+ @return ".#{$bemBlockPrefix+$block}"
7
+
8
+ @function selectorToString($selector)
9
+ $selector: inspect($selector) //cast to string
10
+ $selector: str-slice($selector, 2, -2) //remove bracket
11
+ @return $selector
12
+
13
+ @function containsModifier($selector)
14
+ $selector: selectorToString($selector)
15
+ @if str-index($selector, $bemModifierSeparator)
16
+ @return true
17
+ @else
18
+ @return false
19
+
20
+ @function containsPseudo($selector)
21
+ $selector: selectorToString($selector)
22
+ @if str-index($selector, ':')
23
+ @return true
24
+ @else
25
+ @return false
26
+
27
+ @function containsElement($selector)
28
+ $selector: selectorToString($selector)
29
+ @if str-index($selector, $bemElementSeparator)
30
+ @return true
31
+ @else
32
+ @return false
33
+
34
+ @function getBlock($selector)
35
+ $selector: selectorToString($selector)
36
+ $elementStart: str-index($selector, $bemElementSeparator)
37
+ $modifierStart: str-index($selector, $bemModifierSeparator)
38
+ $pseudoStart: str-index($selector, ':')
39
+ $start: if($elementStart, $elementStart - 1, if($modifierStart, $modifierStart - 1, $pseudoStart - 1))
40
+ @return str-slice($selector, 0, $start)
41
+
42
+ =b($block)
43
+ .#{$bemBlockPrefix+$block}
44
+ @content
45
+
46
+ =plus_b
47
+ @at-root
48
+ $block: &
49
+ #{$block} + #{$block}
50
+ @content
51
+
52
+ =hover_b
53
+ @at-root
54
+ $block: &
55
+ #{$block}:hover
56
+ @content
57
+
58
+ =e($element)
59
+ $selector: &
60
+ @at-root
61
+ @if containsModifier($selector) or containsPseudo($selector)
62
+ $block: getBlock($selector)
63
+ #{$selector}
64
+ #{$block+$bemElementSeparator+$element}
65
+ @content
66
+ @else
67
+ #{$selector+$bemElementSeparator+$element}
68
+ @content
69
+
70
+ =es($elements...)
71
+ @each $element in $elements
72
+ +e($element)
73
+ @content
74
+
75
+ =m($modifier)
76
+ @at-root
77
+ #{&}#{$bemModifierSeparator+$modifier}
78
+ @content
79
+
80
+ =icons-m($modifiers, $width, $height)
81
+ @for $i from 1 through length($modifiers)
82
+ +m(nth($modifiers, $i))
83
+ background-position: -($i - 1) * $width (0)
84
+ &:hover
85
+ background-position: -($i - 1)*$width (-$height)
86
+ &:active
87
+ background-position: -($i - 1) * $width (-2 * $height)
88
+
89
+ @function block-image-url($level, $image)
90
+ $selector: &
91
+ $selector: selectorToString($selector)
92
+ $block: if(containsModifier($selector) or containsPseudo($selector) or containsElement($selector), getBlock($selector), selectorToString($selector))
93
+ $block: str-slice($block, str-length($bemBlockPrefix) + 1)
94
+ @return image-url("#{$level}/#{$block}/images/#{$image}")
@@ -0,0 +1,122 @@
1
+ require 'rails_blocks/names'
2
+ require 'rails_blocks/path'
3
+
4
+ module BlockHelper
5
+ include RailsBlocks::Path
6
+ include RailsBlocks::Names
7
+
8
+ BEM_KEYS = [:tag, :class, :attrs, :mods, :mix, :data, :levels, :content]
9
+
10
+ def bem_page(options, &block)
11
+ @page_options = options
12
+ block_given? ? capture(&block) : ''
13
+ end
14
+
15
+ def page_options
16
+ defaults = {
17
+ levels: RailsBlocks.config.levels
18
+ }
19
+ defaults.merge @page_options || {}
20
+ end
21
+
22
+ #TODO бога ради, отрефактори это дерьмо
23
+ def b(b_name, options = {}, &block)
24
+ parent_block = context_block
25
+ push_context_block b_name
26
+ options = page_options.merge options
27
+ options[:parent_block] = parent_block if parent_block
28
+ template = block_template b_name, options
29
+ classes = block_classes b_name, options
30
+
31
+ result = element(classes, template, options, &block)
32
+ pop_context_block
33
+ result
34
+ end
35
+
36
+ def b_classes(b_name, options = {})
37
+ parent_block = context_block
38
+ push_context_block b_name
39
+ options = page_options.merge options
40
+ options[:parent_block] = parent_block if parent_block
41
+ classes = block_classes b_name, options
42
+ pop_context_block
43
+ classes
44
+ end
45
+
46
+ def b_context(b_name, &block)
47
+ push_context_block b_name
48
+ result = capture(&block)
49
+ pop_context_block
50
+ result
51
+ end
52
+
53
+ def e(e_name, options = {}, &block)
54
+ parent_block = options[:b] || context_block
55
+ raise RailsBlocks::NoBlockContextError unless parent_block
56
+ options = page_options.merge options
57
+ options[:parent_block] = parent_block
58
+ template = element_template parent_block, e_name, options
59
+ classes = element_classes(parent_block, e_name, options)
60
+ element(classes, template, options, &block)
61
+ end
62
+
63
+ def e_classes(e_name, options = {})
64
+ parent_block = options[:b] || context_block
65
+ raise RailsBlocks::NoBlockContextError unless parent_block
66
+ options = page_options.merge options
67
+ options[:parent_block] = parent_block
68
+ element_classes(parent_block, e_name, options).join(' ')
69
+ end
70
+
71
+ def element(classes, template, options, &block)
72
+ content = block ? capture(&block) : options[:content]
73
+ @attrs = {class: classes.join(' ')}
74
+ @attrs.merge! options[:attrs] if options[:attrs]
75
+ @attrs["data-bem"] = options[:data].to_json if options[:data]
76
+ @attrs[:tag] = options[:tag] || 'div'
77
+
78
+ template.nil? ? empty(content) : render(file: template, locals: {content: content, options: options})
79
+ end
80
+
81
+ #убрать после тестов производительности
82
+ # def empty(content)
83
+ # atrrs = @attrs.except(:tag).map do |key, value|
84
+ # key.to_s + '=\'' + value.to_s + "'"
85
+ # end
86
+ # "<#{@attrs[:tag]} #{atrrs.join(' ')}>#{content}</#{@attrs[:tag]}>".html_safe
87
+ # end
88
+
89
+ def empty(content)
90
+ content_tag bem_tag, content, bem_attrs_without_tag
91
+ end
92
+
93
+ def bem_tag
94
+ @attrs[:tag]
95
+ end
96
+
97
+ def bem_attrs
98
+ @attrs
99
+ end
100
+
101
+ def bem_attrs_without_tag
102
+ @attrs.except :tag
103
+ end
104
+
105
+ private
106
+
107
+ def blocks_stack
108
+ @blocks_stack = @blocks_stack || []
109
+ end
110
+
111
+ def context_block
112
+ blocks_stack.last
113
+ end
114
+
115
+ def push_context_block(b_name)
116
+ blocks_stack.push b_name
117
+ end
118
+
119
+ def pop_context_block
120
+ blocks_stack.pop
121
+ end
122
+ end
@@ -0,0 +1,2 @@
1
+ *bem_attrs
2
+ = content