bemer 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +12 -0
  3. data/.overcommit.yml +59 -0
  4. data/.rspec +4 -0
  5. data/.rubocop.yml +12 -1
  6. data/.rubocop_todo.yml +3 -6
  7. data/Gemfile +6 -0
  8. data/LICENSE +21 -0
  9. data/LICENSE-RU +23 -0
  10. data/README.md +2 -10
  11. data/Rakefile +8 -1
  12. data/bemer.gemspec +19 -6
  13. data/lib/bemer.rb +93 -2
  14. data/lib/bemer/asset_matcher.rb +21 -0
  15. data/lib/bemer/builders.rb +23 -0
  16. data/lib/bemer/builders/tag/element.rb +22 -0
  17. data/lib/bemer/builders/template.rb +57 -0
  18. data/lib/bemer/builders/template_list.rb +56 -0
  19. data/lib/bemer/builders/tree.rb +35 -0
  20. data/lib/bemer/builders/tree/element.rb +22 -0
  21. data/lib/bemer/common_template.rb +29 -0
  22. data/lib/bemer/component.rb +19 -0
  23. data/lib/bemer/component_pack.rb +39 -0
  24. data/lib/bemer/configuration.rb +36 -0
  25. data/lib/bemer/context.rb +44 -0
  26. data/lib/bemer/context_extentions.rb +12 -0
  27. data/lib/bemer/context_extentions/structure.rb +32 -0
  28. data/lib/bemer/context_extentions/template.rb +15 -0
  29. data/lib/bemer/default_template_list.rb +35 -0
  30. data/lib/bemer/entity.rb +94 -0
  31. data/lib/bemer/entity_builder.rb +126 -0
  32. data/lib/bemer/helpers.rb +41 -0
  33. data/lib/bemer/mixin_list.rb +74 -0
  34. data/lib/bemer/modifier_list.rb +68 -0
  35. data/lib/bemer/pipeline.rb +85 -0
  36. data/lib/bemer/pipeline/handler.rb +132 -0
  37. data/lib/bemer/predicate.rb +63 -0
  38. data/lib/bemer/railtie.rb +36 -0
  39. data/lib/bemer/renderer.rb +15 -0
  40. data/lib/bemer/tag.rb +35 -0
  41. data/lib/bemer/tag_builder.rb +11 -0
  42. data/lib/bemer/template.rb +90 -0
  43. data/lib/bemer/template_catalog.rb +42 -0
  44. data/lib/bemer/template_catalog/drawer.rb +31 -0
  45. data/lib/bemer/template_list.rb +62 -0
  46. data/lib/bemer/tree.rb +143 -0
  47. data/lib/bemer/tree/base_node.rb +49 -0
  48. data/lib/bemer/tree/node.rb +129 -0
  49. data/lib/bemer/tree/text_node.rb +11 -0
  50. data/lib/bemer/version.rb +1 -1
  51. data/spec/bemer/entity_spec.rb +37 -0
  52. data/spec/bemer/mixin_list_spec.rb +99 -0
  53. data/spec/bemer/modifier_list_spec.rb +83 -0
  54. data/spec/bemer_spec.rb +36 -0
  55. data/spec/dummy/Rakefile +8 -0
  56. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  57. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  58. data/spec/dummy/app/helpers/application_helper.rb +4 -0
  59. data/spec/dummy/app/jobs/application_job.rb +4 -0
  60. data/spec/dummy/app/mailers/application_mailer.rb +6 -0
  61. data/spec/dummy/app/models/application_record.rb +5 -0
  62. data/spec/dummy/app/models/concerns/.keep +0 -0
  63. data/spec/dummy/app/views/layouts/application.html.erb +13 -0
  64. data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
  65. data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
  66. data/spec/dummy/bin/bundle +5 -0
  67. data/spec/dummy/bin/rails +6 -0
  68. data/spec/dummy/bin/rake +6 -0
  69. data/spec/dummy/bin/setup +39 -0
  70. data/spec/dummy/bin/update +31 -0
  71. data/spec/dummy/bin/yarn +13 -0
  72. data/spec/dummy/config.ru +7 -0
  73. data/spec/dummy/config/application.rb +27 -0
  74. data/spec/dummy/config/boot.rb +7 -0
  75. data/spec/dummy/config/cable.yml +10 -0
  76. data/spec/dummy/config/database.yml +25 -0
  77. data/spec/dummy/config/environment.rb +7 -0
  78. data/spec/dummy/config/environments/development.rb +51 -0
  79. data/spec/dummy/config/environments/production.rb +84 -0
  80. data/spec/dummy/config/environments/test.rb +44 -0
  81. data/spec/dummy/config/initializers/application_controller_renderer.rb +7 -0
  82. data/spec/dummy/config/initializers/backtrace_silencers.rb +8 -0
  83. data/spec/dummy/config/initializers/bemer.rb +7 -0
  84. data/spec/dummy/config/initializers/cookies_serializer.rb +7 -0
  85. data/spec/dummy/config/initializers/filter_parameter_logging.rb +6 -0
  86. data/spec/dummy/config/initializers/inflections.rb +17 -0
  87. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  88. data/spec/dummy/config/initializers/wrap_parameters.rb +16 -0
  89. data/spec/dummy/config/locales/en.yml +33 -0
  90. data/spec/dummy/config/puma.rb +58 -0
  91. data/spec/dummy/config/routes.rb +5 -0
  92. data/spec/dummy/config/secrets.yml +32 -0
  93. data/spec/dummy/config/spring.rb +8 -0
  94. data/spec/dummy/lib/assets/.keep +0 -0
  95. data/spec/dummy/log/.keep +0 -0
  96. data/spec/dummy/package.json +5 -0
  97. data/spec/dummy/public/404.html +67 -0
  98. data/spec/dummy/public/422.html +67 -0
  99. data/spec/dummy/public/500.html +66 -0
  100. data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
  101. data/spec/dummy/public/apple-touch-icon.png +0 -0
  102. data/spec/dummy/public/favicon.ico +0 -0
  103. data/spec/rails_helper.rb +37 -0
  104. data/spec/spec_helper.rb +41 -0
  105. metadata +301 -7
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/core_ext/object/blank'
4
+
5
+ module Bemer
6
+ class Pipeline
7
+ class Handler
8
+ def initialize(templates)
9
+ @priorities = Pipeline::MODES.map { |mode| [mode, {}] }.to_h
10
+ @container = Pipeline::MODES.map { |mode| [mode, []] }.to_h
11
+
12
+ prioritize(templates)
13
+ end
14
+
15
+ def apply!(mode, node)
16
+ return if node.applied_modes[mode].present? || !allowable_mode?(mode)
17
+
18
+ apply_template!(mode, node)
19
+ end
20
+
21
+ def apply_next(current_template, node, **params)
22
+ position = priorities[current_template.mode][current_template.object_id] + 1
23
+
24
+ apply_template(current_template.mode, node, position, params)
25
+ end
26
+
27
+ def apply(mode, current_template, node, **params)
28
+ return unless allowable_mode?(mode) && compatible_modes?(mode, current_template.mode)
29
+
30
+ if current_template.mode.eql?(mode)
31
+ apply_next(current_template, node, params)
32
+ else
33
+ apply_template(mode, node, params)
34
+ end
35
+ end
36
+
37
+ protected
38
+
39
+ attr_reader :priorities, :container
40
+
41
+ def compatible_modes?(mode, current_mode)
42
+ return true if Pipeline::STRUCTURE_RELATED_MODES.include?(current_mode)
43
+
44
+ !Pipeline::STRUCTURE_RELATED_MODES.include?(mode)
45
+ end
46
+
47
+ def allowable_mode?(mode)
48
+ Pipeline::MODES.include?(mode)
49
+ end
50
+
51
+ def apply_template(mode, node, position = 0, **params)
52
+ template = find_template(mode, node, position)
53
+ old_params = Hash[node.params]
54
+
55
+ node.params.merge!(params)
56
+
57
+ output = template.nil? ? CommonTemplate.new(mode).apply!(node) : template.apply(node)
58
+ node.params = old_params
59
+
60
+ output
61
+ end
62
+
63
+ def apply_template!(mode, node)
64
+ node.applied_modes[mode] = true
65
+ template = find_template(mode, node)
66
+ output = template ? template.apply!(node) : nil
67
+
68
+ disable_related_modes!(mode, node)
69
+
70
+ output
71
+ end
72
+
73
+ def disable_related_modes!(mode, node)
74
+ return unless [Pipeline::TAG_MODE, Pipeline::BEM_MODE].include?(mode)
75
+
76
+ if Pipeline::TAG_MODE.eql?(mode)
77
+ disable_tag_related_modes!(node)
78
+ else
79
+ disable_bem_related_modes!(node)
80
+ end
81
+ end
82
+
83
+ def disable_tag_related_modes!(node)
84
+ return if node.entity_builder.tag.present?
85
+
86
+ Pipeline::TAG_RELATED_MODES.each { |mode| node.applied_modes[mode] = true }
87
+ end
88
+
89
+ def disable_bem_related_modes!(node)
90
+ return if node.entity_builder.bem?
91
+
92
+ Pipeline::BEM_RELATED_MODES.each { |mode| node.applied_modes[mode] = true }
93
+ end
94
+
95
+ def prioritize(templates) # rubocop:disable Metrics/AbcSize
96
+ wildcard_container = Pipeline::MODES.map { |mode| [mode, []] }.to_h
97
+
98
+ templates.each do |template|
99
+ next wildcard_container[template.mode].push(template) if template.wildcard?
100
+
101
+ priorities[template.mode][template.object_id] = container[template.mode].length
102
+
103
+ container[template.mode].push(template)
104
+ end
105
+
106
+ wildcard_container.each do |mode, wildcard_templates|
107
+ prepend_wildcard_templates(mode, wildcard_templates)
108
+ end
109
+ end
110
+
111
+ def prepend_wildcard_templates(mode, templates)
112
+ return if templates.empty?
113
+
114
+ templates.reverse_each do |template|
115
+ priorities[mode].each { |id, priority| priorities[mode][id] = priority + 1 }
116
+
117
+ priorities[mode][template.object_id] = 0
118
+
119
+ container[mode].unshift(template)
120
+ end
121
+ end
122
+
123
+ def find_template(mode, node, start = 0)
124
+ templates = container[mode][start..-1]
125
+
126
+ return if templates.empty?
127
+
128
+ templates.detect { |template| template.match?(node) }
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bemer
4
+ class Predicate
5
+ def initialize(**options)
6
+ @block = options[:block]
7
+ @condition = options[:condition].nil? ? true : options[:condition]
8
+ @element = options[:elem]
9
+ @mask = build_mask(options[:block], options[:elem])
10
+ @mixins = MixinList.new(options[:mix])
11
+ @modifiers = ModifierList.new(:block, :elem, options[:mods])
12
+ @wildcard = nil
13
+ end
14
+
15
+ def match?(node)
16
+ condition?(node) && mix?(node.mix) && mods?(node.mods)
17
+ end
18
+
19
+ def name_match?(name)
20
+ Bemer.can_use_new_matcher? ? mask.match?(name) : mask =~ name
21
+ end
22
+
23
+ def wildcard?
24
+ return wildcard unless wildcard.nil?
25
+
26
+ @wildcard = name.include?('*')
27
+ end
28
+
29
+ protected
30
+
31
+ attr_reader :block, :element, :condition, :mask, :mixins, :name, :wildcard
32
+
33
+ def condition?(node)
34
+ return condition unless condition.respond_to?(:call)
35
+
36
+ condition.call Context.new(node)
37
+ end
38
+
39
+ def mix?(mix)
40
+ (mixins.to_a - mix).empty?
41
+ end
42
+
43
+ def mods?(mods)
44
+ return false if modifiers.keys.length > mods.keys.length
45
+
46
+ modifiers.all? do |name, value|
47
+ val = mods[name]
48
+
49
+ val.nil? ? false : (Array(value) - Array(val)).empty?
50
+ end
51
+ end
52
+
53
+ def modifiers
54
+ @modifiers.to_h
55
+ end
56
+
57
+ def build_mask(block, element)
58
+ @name = Bemer.entity_name(block, element)
59
+
60
+ name.eql?('*') ? /^((?!#{Bemer.element_name_separator}).)*$/ : /^#{name.gsub('*', '.*')}$/
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bemer
4
+ class Railtie < ::Rails::Railtie
5
+ config.eager_load_namespaces << Bemer if config.respond_to?(:eager_load_namespaces)
6
+
7
+ config.after_initialize do
8
+ ActionController::Base.prepend_view_path([Bemer.path, *Bemer.paths])
9
+ ActionMailer::Base.prepend_view_path([Bemer.path, *Bemer.paths])
10
+ end
11
+
12
+ initializer 'bemer.helpers' do
13
+ ActiveSupport.on_load(:action_view) { include Bemer::Helpers }
14
+ end
15
+
16
+ initializer_name =
17
+ case ::Rails::VERSION::MAJOR
18
+ when 5 then :append_assets_path
19
+ when 3..4 then :load_config_initializers
20
+ end
21
+
22
+ initializer 'bemer.prepend_asset_paths', group: :all, after: initializer_name do |app|
23
+ next unless defined?(::Sprockets) && Bemer.prepend_asset_paths?
24
+
25
+ app.config.assets.paths.unshift(Bemer.path.to_s, *Bemer.asset_paths)
26
+ end
27
+
28
+ initializer 'bemer.assets_precompile', group: :all, after: :load_config_initializers do |app|
29
+ next if ::Rails::VERSION::MAJOR > 3 || !defined?(::Sprockets)
30
+
31
+ asset_matcher = Bemer::AssetMatcher.new(app.config.assets.precompile.shift)
32
+
33
+ app.config.assets.precompile.unshift(asset_matcher)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'action_view/helpers/tag_helper'
4
+
5
+ module Bemer
6
+ class Renderer
7
+ include ::ActionView::Helpers::TagHelper
8
+
9
+ def render(builder)
10
+ return builder.content if builder.tag.blank?
11
+
12
+ content_tag(builder.tag, builder.content, builder.attrs)
13
+ end
14
+ end
15
+ end
data/lib/bemer/tag.rb ADDED
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ module Bemer
6
+ class Tag
7
+ extend Forwardable
8
+
9
+ def initialize(block = '', element = nil, **options, &content)
10
+ @bem_cascade = options[:bem_cascade]
11
+ @tag_builder = TagBuilder.new(block, element, options, &content)
12
+ @renderer = Renderer.new
13
+ end
14
+
15
+ def render
16
+ tag_builder.content = capture_content
17
+
18
+ renderer.render(tag_builder)
19
+ end
20
+
21
+ protected
22
+
23
+ attr_reader :bem_cascade, :renderer, :tag_builder
24
+
25
+ def_delegators :tag_builder, :block, :block?, :content
26
+
27
+ def capture_content
28
+ return content unless content.respond_to?(:call)
29
+
30
+ builder = Builders::Tag::Element.new(block, bem_cascade) if block?
31
+
32
+ content.binding.receiver.capture(builder, &content)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bemer
4
+ class TagBuilder < EntityBuilder
5
+ protected
6
+
7
+ def bem_enabled_fully?
8
+ bem_cascade && bem
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ module Bemer
6
+ class Template < CommonTemplate
7
+ extend Forwardable
8
+
9
+ def_delegators :predicate, :wildcard?, :match?, :name_match?
10
+
11
+ def initialize(mode, body, predicate)
12
+ super(mode)
13
+
14
+ @add_mode = !@mode.eql?(mode)
15
+ @body = body
16
+ @method = [@mode, '='].join.to_sym
17
+ @predicate = predicate
18
+ end
19
+
20
+ def apply(node)
21
+ case mode
22
+ when Pipeline::REPLACE_MODE then replace(node)
23
+ when Pipeline::CONTENT_MODE then capture_content(node)
24
+ else node.entity_builder.public_send(method, capture_body(node), false)
25
+ end
26
+ end
27
+
28
+ def apply!(node)
29
+ return replace!(node) if Pipeline::REPLACE_MODE.eql?(mode)
30
+
31
+ content = Pipeline::CONTENT_MODE.eql?(mode) ? capture_content!(node) : capture_body(node)
32
+
33
+ node.entity_builder.public_send(method, content)
34
+ end
35
+
36
+ protected
37
+
38
+ attr_reader :add_mode, :body, :method, :predicate
39
+
40
+ alias add_mode? add_mode
41
+
42
+ def capture_content(node)
43
+ return body unless body.respond_to?(:call)
44
+
45
+ builder = Builders::Tree.new(node.tree)
46
+
47
+ body.binding.receiver.capture(build_context(node), builder, &body)
48
+ end
49
+
50
+ def capture_body(node)
51
+ output = body.respond_to?(:call) ? body.call(build_context(node)) : body
52
+
53
+ return output unless add_mode?
54
+
55
+ normalized_output = node.entity_builder.public_send(method, output, false)
56
+
57
+ [*node.apply(mode, self), *normalized_output]
58
+ end
59
+
60
+ def replace(node)
61
+ output = capture_content(node)
62
+
63
+ return if output.blank?
64
+
65
+ node.replacers.unshift Tree::TextNode.new(node.tree, output)
66
+
67
+ nil
68
+ end
69
+
70
+ def replace!(node)
71
+ node.need_replace = true
72
+
73
+ node.replace_parent_and_execute { replace(node) }
74
+ end
75
+
76
+ def capture_content!(node)
77
+ node.content_replaced = true
78
+
79
+ node.replace_parent_and_execute { capture_content(node) }
80
+ end
81
+
82
+ def build_context(node)
83
+ context = Context.new(node, self).extend(ContextExtentions::Template)
84
+
85
+ return context unless Pipeline::STRUCTURE_RELATED_MODES.include?(mode)
86
+
87
+ context.extend(ContextExtentions::Structure)
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/dependencies/autoload'
4
+
5
+ module Bemer
6
+ class TemplateCatalog
7
+ extend ActiveSupport::Autoload
8
+
9
+ eager_autoload do
10
+ autoload :Drawer
11
+ end
12
+
13
+ attr_reader :owner
14
+
15
+ def initialize(owner)
16
+ @drawers = {}
17
+ @owner = owner
18
+ @template_queue = []
19
+ end
20
+
21
+ def compiled_templates
22
+ template_queue = @template_queue
23
+ @template_queue = []
24
+
25
+ template_queue.flat_map { |id| drawers[id].compiled_templates }
26
+ end
27
+
28
+ def add(path, cached = false, &block)
29
+ return unless block_given?
30
+
31
+ id = [block.source_location, path].join(':')
32
+
33
+ @template_queue << id
34
+
35
+ drawers[id] = Drawer.new(cached, &block) unless drawers.key?(id) && drawers[id].cached?
36
+ end
37
+
38
+ protected
39
+
40
+ attr_reader :drawers
41
+ end
42
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bemer
4
+ class TemplateCatalog
5
+ class Drawer
6
+ attr_reader :cached
7
+
8
+ alias cached? cached
9
+
10
+ def initialize(cached = false, &block)
11
+ @block = block
12
+ @cached = cached
13
+ end
14
+
15
+ def compiled_templates
16
+ @compiled_templates ||= begin
17
+ new_templates = []
18
+ builder = Builders::TemplateList.new(new_templates)
19
+
20
+ block.binding.receiver.capture(builder, &block)
21
+
22
+ new_templates
23
+ end
24
+ end
25
+
26
+ protected
27
+
28
+ attr_reader :block
29
+ end
30
+ end
31
+ end