rbexy 0.2.2 → 1.0.1

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: 9b5edab6a6162d1d195a6e72652727a6be94934f6759eeaa4425ca252fc2be6a
4
- data.tar.gz: 77112d7e4c606ad909d34777c1a549c904acef758cb64705aec70fea5ef9bf35
3
+ metadata.gz: 203d3f0d86ddbf38e11477ca082fec2bd1e4bc1e2843b4a74e08ea593f35a276
4
+ data.tar.gz: b41f015a023954be101bc01339eeee51f13893fc39918906a4e9d62b4bbfca6c
5
5
  SHA512:
6
- metadata.gz: b018d547ad94191051c9dbf4dec6dc6032c71f55d5d9cbc96f0ceb5993ddec97f047a9301868499bb0fe038ca93fed7bb40af1c7a46b0d97c27cbceb1b70ddf7
7
- data.tar.gz: 16058ae7865b3090b1975c66619787ab368268cc1c7dba50fb4899c0c206b0865de96b7e66c708eb65405b48b610d50d6b8cf701c3a04424e72f5a2ac69cee0d
6
+ metadata.gz: d038e97f116998e004329938a371d510c7c96d034ab16a1bab0755971397d9020eb7c0459e2bf4f5e95acf9d9a6008b0d9cab208a36f9cab6d2fb7db8e953652
7
+ data.tar.gz: 503b6f70c7e2c7cfaa76e5dc77382cba4c6ecead1b7837d1870a025a71cf638a09bb1f96a5c74a7b8ac8aa7ef9cbcc030d1d109214fb8406d65eb46211c6fb3b
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.2)
4
+ rbexy (1.0.1)
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,
@@ -45,14 +42,16 @@ module Rbexy
45
42
  end
46
43
 
47
44
  def call
48
- replace_lookup_context
49
- view_renderer.render(self, partial: component_name, &nil)
50
- ensure
51
- 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
52
51
  end
53
52
 
54
53
  def content
55
- content_block ? view_context.capture(self, &content_block) : ""
54
+ content_block ? content_block.call : ""
56
55
  end
57
56
 
58
57
  def create_context(name, value)
@@ -66,40 +65,13 @@ module Rbexy
66
65
  raise(ContextNotFound, "no parent context `#{name}`")
67
66
  end
68
67
 
69
- def view_renderer
70
- view_context.view_renderer
71
- end
72
-
73
- def component_name
74
- self.class.name.underscore
68
+ def compiled_method_container
69
+ Rbexy::Component
75
70
  end
76
71
 
77
72
  private
78
73
 
79
- attr_reader :view_context, :content_block, :old_lookup_context
80
-
81
- def replace_lookup_context
82
- unless view_renderer.lookup_context.is_a? Rbexy::Component::LookupContext
83
- @old_lookup_context = view_renderer.lookup_context
84
- view_renderer.lookup_context = build_lookup_context(old_lookup_context)
85
- end
86
-
87
- (view_renderer.lookup_context.component_name_stack ||= []) << component_name
88
- end
89
-
90
- def restore_lookup_context
91
- return unless old_lookup_context
92
- view_renderer.lookup_context = old_lookup_context
93
- @old_lookup_context = nil
94
- end
95
-
96
- def build_lookup_context(existing_context)
97
- paths = existing_context.view_paths.dup.unshift(
98
- *Rbexy.configuration.template_paths.map { |p| ActionView::OptimizedFileSystemResolver.new(p) }
99
- )
100
-
101
- LookupContext.new(paths, LookupContext.details_hash(existing_context))
102
- end
74
+ attr_reader :view_context, :content_block
103
75
 
104
76
  def method_missing(meth, *args, &block)
105
77
  if view_context.respond_to?(meth)
@@ -108,5 +80,10 @@ module Rbexy
108
80
  super
109
81
  end
110
82
  end
83
+
84
+ def clean_template_backtrace(backtrace)
85
+ return backtrace if Rbexy.configuration.debug
86
+ BacktraceCleaner.new(backtrace).call
87
+ end
111
88
  end
112
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.2"
2
+ VERSION = "1.0.1"
3
3
  end
@@ -7,5 +7,17 @@ module Rbexy
7
7
  def rbexy_context
8
8
  @rbexy_context ||= [{}]
9
9
  end
10
+
11
+ def rbexy_prep_output(*value)
12
+ return if value.length == 0
13
+ value = value.first
14
+
15
+ value = rbexy_is_html_safe_array?(value) ? value.join.html_safe : value
16
+ [nil, false].include?(value) ? "" : value.to_s
17
+ end
18
+
19
+ def rbexy_is_html_safe_array?(value)
20
+ value.is_a?(Array) && value.all? { |v| v.respond_to?(:html_safe?) && v.html_safe? }
21
+ end
10
22
  end
11
23
  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.2
4
+ version: 1.0.1
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-18 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