rbexy 2.0.0.beta2 → 2.0.0.beta7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e56bce79c603651029c2d7949028d0d9fcaf5922cf3935576a58b7bf9dd68c79
4
- data.tar.gz: d3abd4a15687d4b9a625468ce480f4b4f3cc6f170233007193a6fcf9b1da48fe
3
+ metadata.gz: 97a3fcda88057f9554949f70bbc5dcd30910354e6eb4a4cd1a83e0b53bd30322
4
+ data.tar.gz: fe5f40ad4f4820233e2c555e9c0d13852774c75e40d03e1ac6357157e87d6a22
5
5
  SHA512:
6
- metadata.gz: 0fc85f83715174cc86621989a2237c968d3824f92b9d39adcf89f5971f2865d032ccc5fca313eba44396b90bf3d0b3ba99054dbdb4006e63dc1f2b6e18907cf9
7
- data.tar.gz: c89aed3077b4e0e57d58ec8c40f76a57bf086754fbd17f38f873c1a2f553f0d4d0dfc0b6a182f14f860de7ddf8a0be3b521b9062738c1c1dc35f60ce1074f847
6
+ metadata.gz: d819a2cdd14c75b73b0fc88aabb57cff4666232601a0cf7aeb554a7841313bfdb9f6a56f43d672bec493e14b4a5fcd6f01613ead04ea1609d36e75368352576a
7
+ data.tar.gz: 18c2566b9851268b689e43800dd0a9876224c181cd089750416fc2202c65613cc57717c3a354c5da90cecbd6cb2258ec911e56aff59dc44739ee1bf14b5bccce
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rbexy (2.0.0.beta2)
4
+ rbexy (2.0.0.beta7)
5
5
  actionview (>= 6.0, < 7.0)
6
6
  activesupport (>= 6.0, < 7.0)
7
7
 
data/README.md CHANGED
@@ -301,7 +301,7 @@ By default, we'll look for a template file in the same directory as the class an
301
301
  <h1>{@title}</h1>
302
302
  ```
303
303
 
304
- You can call this component from another `.rbx` template file (`<PageHeader title="Hello" />`)—either one rendered by another component class or a Rails view file like `app/views/products/index.rbx`. Or you can call it from ERB (or any other template language) like `PageHeaderComponent.new(self, title: "Hello").render`.
304
+ You can call this component from another `.rbx` template file (`<PageHeader title="Hello" />`)—either one rendered by another component class or a Rails view file like `app/views/products/index.rbx`. Or you can call it from ERB (or any other template language) like `PageHeaderComponent.new(self, title: "Hello").render_in`.
305
305
 
306
306
  Your components and their templates run in the same context as traditional Rails views, so you have access to all of the view helpers you're used to as well as any custom helpers you've defined in `app/helpers/`.
307
307
 
@@ -0,0 +1,17 @@
1
+ module Rbexy
2
+ class CacheComponent < Rbexy::Component
3
+ def setup(key:)
4
+ @key = key
5
+ end
6
+
7
+ def call
8
+ @current_template = view_context.instance_variable_get(:@current_template)
9
+
10
+ capture do
11
+ cache @key, virtual_path: @current_template.virtual_path do
12
+ @output_buffer << content
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,4 +1,5 @@
1
1
  require "action_view"
2
+ require "active_support/core_ext/class/attribute"
2
3
 
3
4
  module Rbexy
4
5
  class Component < ActionView::Base
@@ -10,12 +11,22 @@ module Rbexy
10
11
  end
11
12
  end
12
13
 
13
- def self.component_name
14
- name.underscore
14
+ class_attribute :component_file_location
15
+
16
+ def self.inherited(klass)
17
+ klass.component_file_location = caller_locations(1, 10).reject { |l| l.label == "inherited" }[0].absolute_path
15
18
  end
16
19
 
17
- def component_name
18
- self.class.component_name
20
+ def self.template_path
21
+ TemplatePath.new(component_name)
22
+ end
23
+
24
+ def self.call_component?
25
+ method_defined?(:call)
26
+ end
27
+
28
+ def self.component_name
29
+ name.underscore
19
30
  end
20
31
 
21
32
  def initialize(view_context, **props)
@@ -27,7 +38,7 @@ module Rbexy
27
38
 
28
39
  @view_context = view_context
29
40
 
30
- setup(**props)
41
+ after_initialize(**props)
31
42
  end
32
43
 
33
44
  # Override in your subclass to handle props, setup your component, etc.
@@ -35,18 +46,15 @@ module Rbexy
35
46
  # call super(view_context).
36
47
  def setup(**props); end
37
48
 
38
- def render(&block)
49
+ def render_in(_context = view_context, &block)
39
50
  @content_block = block_given? ? block : nil
40
- call
51
+ self.class.call_component? ? call : _render
41
52
  end
42
53
 
43
- def call
44
- path = TemplatePath.new(component_name)
45
- template = view_context.lookup_context.find(path)
46
- template.render(self, {})
47
- rescue ActionView::Template::Error => error
48
- error.set_backtrace clean_template_backtrace(error.backtrace)
49
- raise error
54
+ # Explicitly delegate `render` to the view_context rather than super,
55
+ # so `render partial: "..."` calls run in the expected context.
56
+ def render(*args)
57
+ view_context.render(*args)
50
58
  end
51
59
 
52
60
  def content
@@ -61,6 +69,15 @@ module Rbexy
61
69
 
62
70
  attr_reader :view_context, :content_block
63
71
 
72
+ def _render
73
+ path = self.class.template_path
74
+ template = view_context.lookup_context.find(path)
75
+ template.render(self, {})
76
+ rescue ActionView::Template::Error => error
77
+ error.set_backtrace clean_template_backtrace(error.backtrace)
78
+ raise error
79
+ end
80
+
64
81
  def method_missing(meth, *args, &block)
65
82
  view_context.send(meth, *args, &block)
66
83
  end
@@ -73,5 +90,9 @@ module Rbexy
73
90
  return backtrace if Rbexy.configuration.debug
74
91
  BacktraceCleaner.new(backtrace).call
75
92
  end
93
+
94
+ def after_initialize(**props)
95
+ setup(**props)
96
+ end
76
97
  end
77
98
  end
@@ -19,6 +19,13 @@ module Rbexy
19
19
  tspan tt u ul unknown use var video view wbr xmp
20
20
  ).to_set
21
21
 
22
+ def self.try_constantize
23
+ yield
24
+ rescue NameError => e
25
+ raise e unless e.message =~ /wrong constant name/ || e.message =~ /uninitialized constant/
26
+ nil
27
+ end
28
+
22
29
  attr_reader :component_namespaces
23
30
 
24
31
  def initialize
@@ -43,18 +50,11 @@ module Rbexy
43
50
  private
44
51
 
45
52
  def find(name)
46
- find!(name)
47
- rescue NameError => e
48
- raise e unless e.message =~ /wrong constant name/ || e.message =~ /uninitialized constant/
49
- nil
53
+ self.class.try_constantize { ActiveSupport::Inflector.constantize("#{name.gsub(".", "::")}Component") }
50
54
  end
51
55
 
52
56
  def matching_namespaces(template)
53
57
  component_namespaces.select { |path, ns| template.identifier.start_with?(path) }.values.flatten.uniq
54
58
  end
55
-
56
- def find!(name)
57
- ActiveSupport::Inflector.constantize("#{name.gsub(".", "::")}Component")
58
- end
59
59
  end
60
60
  end
@@ -22,7 +22,7 @@ module Rbexy
22
22
  def component_rendering_templates
23
23
  @component_rendering_templates ||= {
24
24
  children: "{capture{%{children}}}",
25
- component: "::%{component_class}.new(%{view_context},%{kwargs}).render%{children_block}"
25
+ component: "::%{component_class}.new(%{view_context},%{kwargs}).render_in%{children_block}"
26
26
  }
27
27
  end
28
28
  end
@@ -36,7 +36,7 @@ module Rbexy
36
36
  members.each_with_object("") do |member, result|
37
37
  case member
38
38
  when ExpressionGroup
39
- result << "**#{member.compile},"
39
+ result << "**#{member.compile}.transform_keys { |k| ActiveSupport::Inflector.underscore(k).to_sym },"
40
40
  when Newline
41
41
  result << member.compile
42
42
  else
@@ -1,9 +1,11 @@
1
1
  module Rbexy
2
2
  autoload :Component, "rbexy/component"
3
+ autoload :CacheComponent, "rbexy/cache_component"
3
4
 
4
5
  module Rails
5
6
  autoload :Engine, "rbexy/rails/engine"
6
7
  autoload :ControllerHelper, "rbexy/rails/controller_helper"
7
8
  autoload :ComponentTemplateResolver, "rbexy/rails/component_template_resolver"
9
+ autoload :RbxDependencyTracker, "rbexy/rails/rbx_dependency_tracker"
8
10
  end
9
11
  end
@@ -1,7 +1,14 @@
1
+ require "active_support/digest"
2
+
1
3
  module Rbexy
2
4
  module Rails
3
5
  class ComponentTemplateResolver < ActionView::FileSystemResolver
4
- VIRTUAL_ROOT = "rbexy_component".freeze
6
+ COMMENT_SYNTAX = {
7
+ rbx: "# %s",
8
+ erb: "<%%# %s %%>",
9
+ haml: "-# %s",
10
+ slim: "/ %s"
11
+ }
5
12
 
6
13
  # Rails 6 requires us to override `_find_all` in order to hook
7
14
  def _find_all(name, prefix, partial, details, key, locals)
@@ -14,22 +21,72 @@ module Rbexy
14
21
  return [] unless name.is_a? Rbexy::Component::TemplatePath
15
22
 
16
23
  templates_path = File.join(@path, prefix, name)
24
+ component_name = prefix.present? ? File.join(prefix, name) : name
25
+ virtual_path = Rbexy::Component::TemplatePath.new(component_name)
26
+
17
27
  extensions = details[:handlers].join(",")
28
+ templates = find_rbx_templates(templates_path, extensions, component_name, virtual_path)
29
+
30
+ if templates.none?
31
+ templates = find_call_component_cachebuster_templates(templates_path, component_name, virtual_path)
32
+ end
33
+
34
+ templates
35
+ end
18
36
 
37
+ def find_rbx_templates(templates_path, extensions, component_name, virtual_path)
19
38
  Dir["#{templates_path}.*{#{extensions}}"].map do |template_path|
20
39
  source = File.binread(template_path)
21
- handler = ActionView::Template.handler_for_extension(File.extname(template_path)[1..-1])
22
- virtual_path = File.join(VIRTUAL_ROOT, prefix, name)
40
+ extension = File.extname(template_path)[1..-1]
41
+ handler = ActionView::Template.handler_for_extension(extension)
23
42
 
24
43
  ActionView::Template.new(
25
- source,
44
+ "#{source}#{component_class_cachebuster(component_name, extension)}",
26
45
  template_path,
27
46
  handler,
47
+ format: extension.to_sym,
28
48
  locals: [],
29
49
  virtual_path: virtual_path
30
50
  )
31
51
  end
32
52
  end
53
+
54
+ def find_call_component_cachebuster_templates(templates_path, component_name, virtual_path)
55
+ component_class = find_component_class(component_name)
56
+ return [] unless component_class && component_class.call_component?
57
+
58
+ [
59
+ ActionView::Template.new(
60
+ cachebuster_digest_as_comment(component_class.component_file_location, :rbx),
61
+ "#{templates_path}.rbexycall",
62
+ ActionView::Template.handler_for_extension(:rbx),
63
+ format: :rbx,
64
+ locals: [],
65
+ virtual_path: virtual_path
66
+ )
67
+ ]
68
+ end
69
+
70
+ def component_class_cachebuster(component_name, template_format)
71
+ component_class = find_component_class(component_name)
72
+ return unless component_class
73
+
74
+ cachebuster_digest_as_comment(component_class.component_file_location, template_format)
75
+ end
76
+
77
+ def find_component_class(component_name)
78
+ Rbexy::ComponentResolver.try_constantize { component_name.classify.constantize }
79
+ end
80
+
81
+ def cachebuster_digest_as_comment(filename, format)
82
+ comment_template = COMMENT_SYNTAX[format.to_sym]
83
+ return "" unless comment_template
84
+
85
+ source = File.binread(filename)
86
+ digest = ActiveSupport::Digest.hexdigest(source)
87
+
88
+ "\n#{comment_template % digest}"
89
+ end
33
90
  end
34
91
  end
35
92
  end
@@ -1,12 +1,14 @@
1
1
  require "rbexy/rails"
2
+ require "action_view/dependency_tracker"
2
3
 
3
4
  module Rbexy
4
5
  module Rails
5
6
  class Engine < ::Rails::Engine
6
7
  initializer "rbexy" do |app|
7
- template_handler = proc { |template, source| Rbexy.compile(template) }
8
+ template_handler = proc { |template, source| Rbexy.compile(Rbexy::Template.new(source, template.identifier)) }
8
9
 
9
10
  ActionView::Template.register_template_handler(:rbx, template_handler)
11
+ ActionView::DependencyTracker.register_tracker(:rbx, RbxDependencyTracker)
10
12
 
11
13
  ActiveSupport.on_load :action_controller_base do
12
14
  include ControllerHelper
@@ -0,0 +1,37 @@
1
+ module Rbexy
2
+ module Rails
3
+ class RbxDependencyTracker
4
+ def self.supports_view_paths?
5
+ true
6
+ end
7
+
8
+ def self.call(name, template, view_paths = nil)
9
+ new(name, template, view_paths).dependencies
10
+ end
11
+
12
+ def initialize(name, template, view_paths = nil)
13
+ @name, @template, @view_paths = name, template, view_paths
14
+ end
15
+
16
+ def dependencies
17
+ rails_render_helper_dependencies + rbexy_dependencies
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :name, :template, :view_paths
23
+
24
+ def rails_render_helper_dependencies
25
+ ActionView::DependencyTracker::ERBTracker.call(name, template, view_paths)
26
+ end
27
+
28
+ def rbexy_dependencies
29
+ Lexer.new(template, Rbexy.configuration.element_resolver).tokenize
30
+ .select { |t| t[0] == :TAG_DETAILS && t[1][:type] == :component }
31
+ .map { |t| t[1][:component_class] }
32
+ .uniq
33
+ .map(&:template_path)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -11,7 +11,7 @@ module Rbexy
11
11
  end
12
12
 
13
13
  def self.splat_attrs(attrs_hash)
14
- tag_builder.tag_options(attrs_hash).html_safe
14
+ tag_builder.tag_options(attrs_hash)&.html_safe
15
15
  end
16
16
 
17
17
  def self.expr_out(*value)
@@ -1,3 +1,3 @@
1
1
  module Rbexy
2
- VERSION = "2.0.0.beta2"
2
+ VERSION = "2.0.0.beta7"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbexy
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.beta2
4
+ version: 2.0.0.beta7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Giancola
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-01-09 00:00:00.000000000 Z
11
+ date: 2021-01-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -217,6 +217,7 @@ files:
217
217
  - docker-compose.yml
218
218
  - lib/rbexy.rb
219
219
  - lib/rbexy/ast_transformer.rb
220
+ - lib/rbexy/cache_component.rb
220
221
  - lib/rbexy/component.rb
221
222
  - lib/rbexy/component/backtrace_cleaner.rb
222
223
  - lib/rbexy/component_context.rb
@@ -244,6 +245,7 @@ files:
244
245
  - lib/rbexy/rails/component_template_resolver.rb
245
246
  - lib/rbexy/rails/controller_helper.rb
246
247
  - lib/rbexy/rails/engine.rb
248
+ - lib/rbexy/rails/rbx_dependency_tracker.rb
247
249
  - lib/rbexy/refinements.rb
248
250
  - lib/rbexy/refinements/array.rb
249
251
  - lib/rbexy/refinements/array/find_map.rb