rbexy 0.2.1 → 1.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4cf2789f8c063a3b67dc6c4da86979b89214679833c8be14bee61d52b1a17f3b
4
- data.tar.gz: 89d4d684fb473837be4b7c239e034ec6d27f9b754978cb6bfeb7b77774310e9b
3
+ metadata.gz: 20306c3d1c288a652a8a9d4d455c9378a22165ef7c07240e234159794cd02d18
4
+ data.tar.gz: 4c8e6b19cd8f47744aba8e2850fbc73425678252628a7c6c46e31abf66937e97
5
5
  SHA512:
6
- metadata.gz: cf0cd9cbf4fc983f6f7213da1babd5e6e334f86c3bd9a3f129f4889d69390946e858a29bc9cede2ffe36e510c6573fea1eb8e55b2e1f504964305a765efd3236
7
- data.tar.gz: fc8090cb79071025b6f3ab491bdff3558a22dccfe43350830f36e6a8ba965e318b39e86bd07aec73659db428a4c223a115ccb3684b30a819e7dc5b577951d04b
6
+ metadata.gz: 70920c8f7a9da04edccbe8de30b6b12ad10e9bfe3d6a4657d412e74cad1f23c705c3bd213c81d15859b18457917f4dd5672030fd4d0072dd06bbf894c2d8eb15
7
+ data.tar.gz: 0aa89cce16b4011a91b2ffeb89472ec687b40bcd9f97f061dd85e7f01507fe034e4b3388fb14da066921c98846187f3c77b154b3d1d03b145716a7e50e9a4657
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
- spec/dummy/log/
1
+ spec/dummy/log/*
2
2
  pkg/
3
- spec/dummy/tmp/
3
+ spec/dummy/tmp/*
4
+ spec/dummy/db/*
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rbexy (0.2.1)
4
+ rbexy (1.0.0)
5
5
  actionview (>= 5.0, < 7.0)
6
6
  activesupport (>= 5.0, < 7.0)
7
7
  railties (>= 5.0, < 7.0)
@@ -201,7 +201,7 @@ PLATFORMS
201
201
  DEPENDENCIES
202
202
  guard-rspec (~> 4.7, >= 4.7.3)
203
203
  pry-byebug
204
- rails (>= 5.0, < 7.0)
204
+ rails (>= 6.0, < 7.0)
205
205
  rake
206
206
  rbexy!
207
207
  rspec (~> 3.9)
data/README.md CHANGED
@@ -48,6 +48,8 @@ Add it to your Gemfile and `bundle install`:
48
48
  gem "rbexy"
49
49
  ```
50
50
 
51
+ _From 1.0 onward, we only support Rails 6. If you're using Rails 5, use the 0.x releases._
52
+
51
53
  In `config/application.rb`:
52
54
 
53
55
  ```ruby
@@ -6,6 +6,7 @@ volumes:
6
6
  services:
7
7
  rbexy:
8
8
  build: .
9
+ image: rbexy
9
10
  volumes:
10
11
  - .:/app
11
12
  - bundle:/usr/local/bundle
@@ -13,3 +14,14 @@ services:
13
14
  - $HOME/.gitconfig:/root/.gitconfig:ro
14
15
  - $HOME/.gem/credentials:/root/.gem/credentials
15
16
  working_dir: /app
17
+ dummy:
18
+ image: rbexy
19
+ volumes:
20
+ - .:/app
21
+ - bundle:/usr/local/bundle
22
+ working_dir: /app/spec/dummy/
23
+ command: ./start.sh
24
+ ports:
25
+ - 3000:3000
26
+ environment:
27
+ - RAILS_LOG_STDOUT=1
@@ -6,7 +6,6 @@ module Rbexy
6
6
  autoload :Nodes, "rbexy/nodes"
7
7
  autoload :Runtime, "rbexy/runtime"
8
8
  autoload :HashMash, "rbexy/hash_mash"
9
- autoload :OutputBuffer, "rbexy/output_buffer"
10
9
  autoload :ComponentTagBuilder, "rbexy/component_tag_builder"
11
10
  autoload :ViewContextHelper, "rbexy/view_context_helper"
12
11
  autoload :Configuration, "rbexy/configuration"
@@ -1,27 +1,24 @@
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
5
- class LookupContext < ActionView::LookupContext
6
- attr_accessor :component_name_stack
7
-
8
- def self.details_hash(context)
9
- context.registered_details.each_with_object({}) do |key, details_hash|
10
- value = key == :locale ? [context.locale] : context.send(key)
11
- details_hash[key] = value
12
- end
13
- end
6
+ autoload :BacktraceCleaner, "rbexy/component/backtrace_cleaner"
14
7
 
15
- # We override any calls to args_for_lookup and set partial=false so that
16
- # the lookup context doesn't automatically add a `_` prefix to the
17
- # template path, since we're using the Rails partial-rendering
18
- # functionality but don't want our templates prefixed with a `_`
19
- def args_for_lookup(name, prefixes, partial, keys, details_options)
20
- partial = false if component_name_stack.include?(name)
21
- super(name, prefixes, partial, keys, details_options)
8
+ class TemplatePath < String
9
+ def to_s
10
+ self
22
11
  end
23
12
  end
24
13
 
14
+ def self.component_name
15
+ name.underscore
16
+ end
17
+
18
+ def component_name
19
+ self.class.component_name
20
+ end
21
+
25
22
  def initialize(view_context, **props)
26
23
  super(
27
24
  view_context.lookup_context,
@@ -40,20 +37,21 @@ module Rbexy
40
37
  def setup(**props); end
41
38
 
42
39
  def render(&block)
43
- @content = nil
44
40
  @content_block = block_given? ? block : nil
45
41
  call
46
42
  end
47
43
 
48
44
  def call
49
- replace_lookup_context
50
- view_renderer.render(self, partial: component_name, &nil)
51
- ensure
52
- restore_lookup_context
45
+ path = TemplatePath.new(component_name)
46
+ template = view_context.lookup_context.find(path)
47
+ template.render(self, {})
48
+ rescue ActionView::Template::Error => error
49
+ error.set_backtrace clean_template_backtrace(error.backtrace)
50
+ raise error
53
51
  end
54
52
 
55
53
  def content
56
- @content ||= content_block ? view_context.capture(self, &content_block) : ""
54
+ content_block ? content_block.call : ""
57
55
  end
58
56
 
59
57
  def create_context(name, value)
@@ -67,40 +65,13 @@ module Rbexy
67
65
  raise(ContextNotFound, "no parent context `#{name}`")
68
66
  end
69
67
 
70
- def view_renderer
71
- view_context.view_renderer
72
- end
73
-
74
- def component_name
75
- self.class.name.underscore
68
+ def compiled_method_container
69
+ Rbexy::Component
76
70
  end
77
71
 
78
72
  private
79
73
 
80
- attr_reader :view_context, :content_block, :old_lookup_context
81
-
82
- def replace_lookup_context
83
- unless view_renderer.lookup_context.is_a? Rbexy::Component::LookupContext
84
- @old_lookup_context = view_renderer.lookup_context
85
- view_renderer.lookup_context = build_lookup_context(old_lookup_context)
86
- end
87
-
88
- (view_renderer.lookup_context.component_name_stack ||= []) << component_name
89
- end
90
-
91
- def restore_lookup_context
92
- return unless old_lookup_context
93
- view_renderer.lookup_context = old_lookup_context
94
- @old_lookup_context = nil
95
- end
96
-
97
- def build_lookup_context(existing_context)
98
- paths = existing_context.view_paths.dup.unshift(
99
- *Rbexy.configuration.template_paths.map { |p| ActionView::OptimizedFileSystemResolver.new(p) }
100
- )
101
-
102
- LookupContext.new(paths, LookupContext.details_hash(existing_context))
103
- end
74
+ attr_reader :view_context, :content_block
104
75
 
105
76
  def method_missing(meth, *args, &block)
106
77
  if view_context.respond_to?(meth)
@@ -109,5 +80,10 @@ module Rbexy
109
80
  super
110
81
  end
111
82
  end
83
+
84
+ def clean_template_backtrace(backtrace)
85
+ return backtrace if Rbexy.configuration.debug
86
+ BacktraceCleaner.new(backtrace).call
87
+ end
112
88
  end
113
89
  end
@@ -0,0 +1,59 @@
1
+ module Rbexy
2
+ class Component
3
+ class BacktraceCleaner
4
+ attr_reader :backtrace
5
+
6
+ def initialize(backtrace)
7
+ @backtrace = backtrace
8
+ @found_templates = {}
9
+ end
10
+
11
+ def call
12
+ backtrace
13
+ .reject(&method(:internal_implementation_detail?))
14
+ .map(&method(:strip_rbx_internals_block_mention))
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :found_templates
20
+
21
+ def internal_implementation_detail?(line)
22
+ if template = template_name_if_rbx_internals(line)
23
+ redundant_internal_block?(line, template)
24
+ else
25
+ internal_method_call?(line)
26
+ end
27
+ end
28
+
29
+ def internal_method_call?(line)
30
+ line =~ /lib\/rbexy\/.*\.rb/ ||
31
+ line =~ /lib\/action_view\/.*\.rb/ ||
32
+ line =~ /lib\/active_support\/notifications\.rb/
33
+ end
34
+
35
+ def redundant_internal_block?(line, template)
36
+ if found_templates[template]
37
+ true
38
+ else
39
+ found_templates[template] = true
40
+ false
41
+ end
42
+ end
43
+
44
+ def strip_rbx_internals_block_mention(line)
45
+ if template_name_if_rbx_internals(line)
46
+ line.gsub(/block (\(\d+ levels\))? ?in /, "")
47
+ else
48
+ line
49
+ end
50
+ end
51
+
52
+ def template_name_if_rbx_internals(line)
53
+ if /\/(?<template>[^\/]*)\.rbx:\d+:in `(block |_)/ =~ line
54
+ template
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -2,6 +2,8 @@ module Rbexy
2
2
  class Configuration
3
3
  attr_accessor :component_provider
4
4
  attr_accessor :template_paths
5
+ attr_accessor :enable_context
6
+ attr_accessor :debug
5
7
 
6
8
  def template_paths
7
9
  @template_paths ||= []
@@ -22,7 +22,7 @@ module Rbexy
22
22
  text_content: /[^<{#]+/,
23
23
  comment: /^\p{Blank}*#.*(\n|\z)/,
24
24
  whitespace: /\s+/,
25
- attr: /[A-Za-z0-9\-_\.]+/,
25
+ attr: /[A-Za-z0-9\-_\.:]+/,
26
26
  open_attr_splat: /{\*\*/,
27
27
  attr_assignment: /=/,
28
28
  double_quote: /"/,
@@ -237,7 +237,6 @@ module Rbexy
237
237
  end
238
238
 
239
239
  def potential_expression_inner_tag
240
- # binding.pry
241
240
  if self.curr_expr =~ Patterns.expression_internal_tag_prefixes
242
241
  tokens << [:EXPRESSION_BODY, curr_expr]
243
242
  self.curr_expr = ""
@@ -18,11 +18,7 @@ module Rbexy
18
18
  end
19
19
 
20
20
  def compile
21
- [
22
- "Rbexy::OutputBuffer.new.tap { |output|",
23
- children.map(&:compile).map { |c| "output << (#{c})"}.join(";"),
24
- "}.html_safe"
25
- ].join(" ")
21
+ "#{children.map(&:compile).map { |c| "@output_buffer << rbexy_prep_output(#{c})"}.join(";")};@output_buffer"
26
22
  end
27
23
  end
28
24
 
@@ -75,23 +71,24 @@ module Rbexy
75
71
  base_tag = "rbexy_tag.#{Util.safe_tag_name(name)}(#{compile_members})"
76
72
  tag = if children.length > 0
77
73
  [
78
- "#{base_tag} {",
79
- "Rbexy::OutputBuffer.new.tap { |output|",
80
- children.map(&:compile).map { |c| "output << (#{c})"}.join(";"),
81
- "}.html_safe",
82
- "}"
83
- ].join(" ")
74
+ "#{base_tag} { capture {",
75
+ children.map(&:compile).map { |c| "@output_buffer << rbexy_prep_output(#{c})" }.join(";"),
76
+ "} }"
77
+ ].join
84
78
  else
85
79
  base_tag
86
- end
80
+ end + ".html_safe"
87
81
 
88
- [
89
- "Rbexy::OutputBuffer.new.tap { |output|",
90
- "rbexy_context.push({}) if defined?(Rbexy::Component) && self.is_a?(Rbexy::Component);",
91
- "output << (#{tag});",
92
- "rbexy_context.pop if defined?(Rbexy::Component) && self.is_a?(Rbexy::Component);",
93
- "}.html_safe"
94
- ].join(" ")
82
+ if Rbexy.configuration.enable_context
83
+ [
84
+ "(",
85
+ "rbexy_context.push({});",
86
+ "#{tag}.tap { rbexy_context.pop }",
87
+ ")"
88
+ ].join
89
+ else
90
+ tag
91
+ end
95
92
  end
96
93
 
97
94
  def compile_members
@@ -3,5 +3,7 @@ module Rbexy
3
3
 
4
4
  module Rails
5
5
  autoload :Engine, "rbexy/rails/engine"
6
+ autoload :ControllerHelper, "rbexy/rails/controller_helper"
7
+ autoload :ComponentTemplateResolver, "rbexy/rails/component_template_resolver"
6
8
  end
7
9
  end
@@ -0,0 +1,35 @@
1
+ require "action_view"
2
+
3
+ module Rbexy
4
+ module Rails
5
+ class ComponentTemplateResolver < ActionView::FileSystemResolver
6
+ # Rails 6 requires us to override `_find_all` in order to hook
7
+ def _find_all(name, prefix, partial, details, key, locals)
8
+ find_templates(name, prefix, partial, details, locals)
9
+ end
10
+
11
+ # Rails 5 only requires `find_templates` (which tbh is the proper way
12
+ # to implement subclasses of ActionView::Resolver)
13
+ def find_templates(name, prefix, partial, details, locals = [])
14
+ return [] unless name.is_a? Rbexy::Component::TemplatePath
15
+
16
+ templates_path = File.join(@path, prefix, name)
17
+ extensions = details[:handlers].join(",")
18
+
19
+ Dir["#{templates_path}.*{#{extensions}}"].map do |template_path|
20
+ source = File.binread(template_path)
21
+ handler = ActionView::Template.handler_for_extension(File.extname(template_path)[1..-1])
22
+ virtual_path = ["rbexy_component", prefix, name].join("/")
23
+
24
+ ActionView::Template.new(
25
+ source,
26
+ template_path,
27
+ handler,
28
+ locals: [],
29
+ virtual_path: virtual_path
30
+ )
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,20 @@
1
+ require "active_support/concern"
2
+
3
+ module Rbexy
4
+ module Rails
5
+ module ControllerHelper
6
+ extend ActiveSupport::Concern
7
+
8
+ def rbexy_component_provider; end
9
+
10
+ class_methods do
11
+ def inherited(klass)
12
+ super
13
+ Rbexy.configuration.template_paths.each do |path|
14
+ prepend_view_path(Rbexy::Rails::ComponentTemplateResolver.new(path))
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -13,8 +13,7 @@ module Rbexy
13
13
  ActiveSupport.on_load :action_controller_base do
14
14
  helper Rbexy::ViewContextHelper
15
15
  helper_method :rbexy_component_provider
16
-
17
- def rbexy_component_provider; end
16
+ include ControllerHelper
18
17
  end
19
18
 
20
19
  if defined?(ViewComponent)
@@ -25,6 +24,7 @@ module Rbexy
25
24
  require "rbexy/component_providers/rbexy_provider"
26
25
  config.component_provider = Rbexy::ComponentProviders::RbexyProvider.new
27
26
  config.template_paths << ::Rails.root.join("app", "components")
27
+ config.enable_context = true
28
28
  end
29
29
  end
30
30
  end
@@ -34,6 +34,7 @@ module Rbexy
34
34
  end
35
35
 
36
36
  def evaluate(code)
37
+ @output_buffer = ActionView::OutputBuffer.new
37
38
  instance_eval(code)
38
39
  rescue => e
39
40
  e.set_backtrace(e.backtrace.map { |l| l.gsub("(eval)", "(rbx template string)") })
@@ -1,3 +1,3 @@
1
1
  module Rbexy
2
- VERSION = "0.2.1"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -7,5 +7,13 @@ module Rbexy
7
7
  def rbexy_context
8
8
  @rbexy_context ||= [{}]
9
9
  end
10
+
11
+ def rbexy_prep_output(*content)
12
+ return if content.length == 0
13
+ content = content.first
14
+
15
+ value = content.is_a?(Array) ? content.join.html_safe : content
16
+ [nil, false].include?(value) ? "" : value.to_s
17
+ end
10
18
  end
11
19
  end
@@ -29,7 +29,7 @@ Gem::Specification.new do |spec|
29
29
  spec.add_dependency "actionview", ">= 5.0", "< 7.0"
30
30
  spec.add_dependency "railties", ">= 5.0", "< 7.0"
31
31
 
32
- spec.add_development_dependency "rails", ">= 5.0", "< 7.0"
32
+ spec.add_development_dependency "rails", ">= 6.0", "< 7.0"
33
33
  spec.add_development_dependency "rspec", "~> 3.9"
34
34
  spec.add_development_dependency "guard-rspec", "~> 4.7", ">= 4.7.3"
35
35
  spec.add_development_dependency "rspec-rails", "~> 4.0", ">= 4.0.1"
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: 0.2.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Giancola
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-10-17 00:00:00.000000000 Z
11
+ date: 2020-11-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -76,7 +76,7 @@ dependencies:
76
76
  requirements:
77
77
  - - ">="
78
78
  - !ruby/object:Gem::Version
79
- version: '5.0'
79
+ version: '6.0'
80
80
  - - "<"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '7.0'
@@ -86,7 +86,7 @@ dependencies:
86
86
  requirements:
87
87
  - - ">="
88
88
  - !ruby/object:Gem::Version
89
- version: '5.0'
89
+ version: '6.0'
90
90
  - - "<"
91
91
  - !ruby/object:Gem::Version
92
92
  version: '7.0'
@@ -224,6 +224,7 @@ files:
224
224
  - example.rb
225
225
  - lib/rbexy.rb
226
226
  - lib/rbexy/component.rb
227
+ - lib/rbexy/component/backtrace_cleaner.rb
227
228
  - lib/rbexy/component_providers/namespaced_rbexy_provider.rb
228
229
  - lib/rbexy/component_providers/rbexy_provider.rb
229
230
  - lib/rbexy/component_providers/view_component_provider.rb
@@ -232,9 +233,10 @@ files:
232
233
  - lib/rbexy/hash_mash.rb
233
234
  - lib/rbexy/lexer.rb
234
235
  - lib/rbexy/nodes.rb
235
- - lib/rbexy/output_buffer.rb
236
236
  - lib/rbexy/parser.rb
237
237
  - lib/rbexy/rails.rb
238
+ - lib/rbexy/rails/component_template_resolver.rb
239
+ - lib/rbexy/rails/controller_helper.rb
238
240
  - lib/rbexy/rails/engine.rb
239
241
  - lib/rbexy/runtime.rb
240
242
  - lib/rbexy/version.rb
@@ -1,8 +0,0 @@
1
- module Rbexy
2
- class OutputBuffer < String
3
- def <<(content)
4
- value = content.is_a?(Array) ? content.join : content
5
- super([nil, false].include?(value) ? "" : value.to_s)
6
- end
7
- end
8
- end