rux-rails 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ describe "html safety" do
4
+ it "escapes HTML" do
5
+ render_inline(HtmlSafetyComponent.new(value: "<p>Foo</p>"))
6
+ expect(rendered_content).to eq(
7
+ "<div>&lt;p&gt;Foo&lt;/p&gt;</div>"
8
+ )
9
+ end
10
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,11 +1,8 @@
1
- require 'pry-byebug'
2
-
3
1
  ENV['RAILS_ENV'] ||= 'test'
4
2
 
5
3
  require 'rails'
6
- require 'rux-rails'
7
-
8
4
  require 'action_controller/railtie'
5
+ require 'rux-rails'
9
6
 
10
7
  Dir.chdir(File.join(*%w(spec dummy))) do
11
8
  require File.expand_path(File.join(*%w(dummy config application)), __dir__)
@@ -14,13 +11,65 @@ end
14
11
 
15
12
  require 'rspec/rails'
16
13
 
17
- module SpecHelpers
18
- extend RSpec::SharedContext
14
+ module RuxRails
15
+ module SpecHelpers
16
+ extend RSpec::SharedContext
17
+ include Capybara::RSpecMatchers
18
+
19
+ let(:app) { Rails.application }
20
+
21
+ before(:each) do
22
+ Dir.glob("spec/dummy/app/components/*.rb").each do |f|
23
+ File.unlink(f)
24
+ end
25
+ end
26
+
27
+ def with_file_contents(path, contents)
28
+ old_contents = ::File.read(path)
29
+ ::File.write(path, contents)
30
+ yield
31
+ ensure
32
+ ::File.write(path, old_contents)
33
+ end
34
+
35
+ attr_reader :rendered_content
36
+
37
+ def page
38
+ @page ||= Capybara::Node::Simple.new(rendered_content)
39
+ end
40
+
41
+ def render_inline(component, **args, &block)
42
+ @page = nil
43
+ @rendered_content =
44
+ if Rails.version.to_f >= 6.1
45
+ vc_test_controller.view_context.render(component, args, &block)
46
+ else
47
+ vc_test_controller.view_context.render_component(component, &block)
48
+ end
49
+
50
+ Nokogiri::HTML.fragment(@rendered_content)
51
+ end
52
+
53
+ def vc_test_controller
54
+ @vc_test_controller ||= __vc_test_helpers_build_controller(ViewComponent::Base.test_controller.constantize)
55
+ end
56
+
57
+ def __vc_test_helpers_build_controller(klass)
58
+ klass.new.tap { |c| c.request = vc_test_request }.extend(Rails.application.routes.url_helpers)
59
+ end
19
60
 
20
- let(:app) { Rails.application }
61
+ def vc_test_request
62
+ @vc_test_request ||=
63
+ begin
64
+ out = ActionDispatch::TestRequest.create
65
+ out.session = ActionController::TestSession.new
66
+ out
67
+ end
68
+ end
69
+ end
21
70
  end
22
71
 
23
72
  RSpec.configure do |config|
24
- config.include(SpecHelpers)
73
+ config.include(RuxRails::SpecHelpers)
25
74
  config.include(Capybara::RSpecMatchers, type: :request)
26
75
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rux-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cameron Dutro
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-06-08 00:00:00.000000000 Z
11
+ date: 2023-11-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rux
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.0'
19
+ version: '1.2'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.0'
26
+ version: '1.2'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: railties
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -58,6 +58,20 @@ dependencies:
58
58
  - - "<"
59
59
  - !ruby/object:Gem::Version
60
60
  version: '4'
61
+ - !ruby/object:Gem::Dependency
62
+ name: onload
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '1.0'
68
+ type: :runtime
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '1.0'
61
75
  description: Rux view components on Rails.
62
76
  email:
63
77
  - camertron@gmail.com
@@ -75,13 +89,9 @@ files:
75
89
  - lib/rux-rails/components/audio.rb
76
90
  - lib/rux-rails/components/image.rb
77
91
  - lib/rux-rails/components/video.rb
78
- - lib/rux-rails/core_ext/kernel.rb
79
- - lib/rux-rails/core_ext/kernel_zeitwerk.rb
80
- - lib/rux-rails/ext/activesupport/dependencies.rb
81
- - lib/rux-rails/ext/bootsnap/autoload.rb
82
- - lib/rux-rails/ext/zeitwerk/loader.rb
92
+ - lib/rux-rails/output_buffer.rb
83
93
  - lib/rux-rails/railtie.rb
84
- - lib/rux-rails/safe_buffer.rb
94
+ - lib/rux-rails/rux_loader.rb
85
95
  - lib/rux-rails/tag_builder.rb
86
96
  - lib/rux-rails/tasks/transpile.rake
87
97
  - lib/rux-rails/template_handler.rb
@@ -91,10 +101,10 @@ files:
91
101
  - spec/controllers/home_controller_spec.rb
92
102
  - spec/dummy/app/assets/config/manifest.js
93
103
  - spec/dummy/app/assets/images/cat.png
94
- - spec/dummy/app/components/button.rb
95
104
  - spec/dummy/app/components/button.rux
96
- - spec/dummy/app/components/home_component.rb
97
105
  - spec/dummy/app/components/home_component.rux
106
+ - spec/dummy/app/components/html_safety_component.rb
107
+ - spec/dummy/app/components/html_safety_component.rux
98
108
  - spec/dummy/app/controllers/application_controller.rb
99
109
  - spec/dummy/app/controllers/home_controller.rb
100
110
  - spec/dummy/app/views/home/index.html.ruxt
@@ -103,11 +113,12 @@ files:
103
113
  - spec/dummy/config/routes.rb
104
114
  - spec/dummy/config/secrets.yml
105
115
  - spec/dummy/log/test.log
116
+ - spec/html_safety_spec.rb
106
117
  - spec/spec_helper.rb
107
118
  homepage: http://github.com/camertron/rux-rails
108
119
  licenses: []
109
120
  metadata: {}
110
- post_install_message:
121
+ post_install_message:
111
122
  rdoc_options: []
112
123
  require_paths:
113
124
  - lib
@@ -122,8 +133,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
122
133
  - !ruby/object:Gem::Version
123
134
  version: '0'
124
135
  requirements: []
125
- rubygems_version: 3.1.6
126
- signing_key:
136
+ rubygems_version: 3.4.1
137
+ signing_key:
127
138
  specification_version: 4
128
139
  summary: Rux view components on Rails.
129
140
  test_files: []
@@ -1,67 +0,0 @@
1
- module RuxRails
2
- module LoadPatch
3
- def load(file, *args)
4
- # ActiveSupport::Dependencies adds an extra .rb to the end
5
- if file.end_with?('.rux.rb')
6
- file = file.chomp('.rb')
7
- end
8
-
9
- if file.end_with?('.rux')
10
- tmpl = Rux::File.new(file)
11
- tmpl.write if RuxRails.transpile_on_load?
12
-
13
- return super(tmpl.default_outfile, *args)
14
- end
15
-
16
- super(file, *args)
17
- end
18
- end
19
-
20
- module RequirePatch
21
- def require(file)
22
- # check to see if there's a .rux file somewhere on the load path
23
- path = nil
24
- rux_file = file.end_with?('.rux') ? file : "#{file}.rux"
25
-
26
- if File.absolute_path(rux_file) == rux_file && File.exist?(rux_file)
27
- path = rux_file
28
- elsif rux_file.start_with?(".#{File::SEPARATOR}")
29
- abs_path = File.expand_path(rux_file)
30
- path = abs_path if File.exist?(abs_path)
31
- else
32
- $LOAD_PATH.each do |lp|
33
- check_path = File.expand_path(File.join(lp, rux_file))
34
-
35
- if File.exist?(check_path)
36
- path = check_path
37
- break
38
- end
39
- end
40
- end
41
-
42
- return super(file) unless path
43
- return false if $LOADED_FEATURES.include?(path)
44
-
45
- # Must call the Kernel.load class method here because that's the one
46
- # activesupport doesn't mess with, and in fact the one activesupport
47
- # itself uses to actually load files. In case you were curious,
48
- # activesupport redefines Object#load and Object#require i.e. the
49
- # instance versions that get inherited by all other objects. Yeah,
50
- # it's pretty awful stuff.
51
- Kernel.load(path)
52
- $LOADED_FEATURES << path
53
-
54
- return true
55
- end
56
- end
57
- end
58
-
59
- module Kernel
60
- class << self
61
- prepend RuxRails::LoadPatch
62
- end
63
- end
64
-
65
- class Object
66
- prepend RuxRails::RequirePatch
67
- end
@@ -1,63 +0,0 @@
1
- require 'zeitwerk'
2
-
3
- module Kernel
4
- alias_method :rux_orig_require, :require
5
- alias_method :rux_orig_load, :load
6
-
7
- def load(file, *args)
8
- if File.extname(file) == '.rux'
9
- tmpl = Rux::File.new(file)
10
- tmpl.write if RuxRails.transpile_on_load?
11
-
12
- # I don't understand why, but it's necessary to delete the constant
13
- # in order to load the .ruxc file. Otherwise you get an error about
14
- # an uninitialized constant, and it's like... yeah, I _know_ it's
15
- # uninitialized, that's why I'm loading this file. Whatevs.
16
- loader = Zeitwerk::Registry.loader_for(file)
17
- parent, cname = loader.send(:autoloads)[file]
18
- parent.send(:remove_const, cname)
19
-
20
- return rux_orig_load(tmpl.default_outfile, *args)
21
- end
22
-
23
- rux_orig_load(file, *args)
24
- end
25
-
26
- def require(file)
27
- path = nil
28
- loader = Zeitwerk::Registry.loader_for(file)
29
- rux_file = file.end_with?('.rux') ? file : "#{file}.rux"
30
-
31
- if File.absolute_path(rux_file) == rux_file && File.exist?(rux_file)
32
- path = rux_file
33
- elsif rux_file.start_with?(".#{File::SEPARATOR}")
34
- abs_path = File.expand_path(rux_file)
35
- path = abs_path if File.exist?(abs_path)
36
- else
37
- $LOAD_PATH.each do |lp|
38
- check_path = File.expand_path(File.join(lp, rux_file))
39
-
40
- if File.exist?(check_path)
41
- path = check_path
42
- break
43
- end
44
- end
45
- end
46
-
47
- unless path
48
- # This will be either Ruby's original require or bootsnap's monkeypatched
49
- # require in setups that use bootsnap. Lord help us with all these layers
50
- # of patches.
51
- return rux_orig_require(file)
52
- end
53
-
54
- return false if $LOADED_FEATURES.include?(path)
55
-
56
- load path
57
- $LOADED_FEATURES << path
58
-
59
- loader.on_file_autoloaded(path) if loader
60
-
61
- return true
62
- end
63
- end
@@ -1,47 +0,0 @@
1
- require 'active_support/dependencies'
2
-
3
- module RuxRails
4
- module ActiveSupportDependenciesPatch
5
- # Allow activesupport to find .rux files.
6
- def search_for_file(path_suffix)
7
- path_suffix_with_ext = path_suffix.sub(/(\.rux)?$/, '.rux'.freeze)
8
-
9
- autoload_paths.each do |root|
10
- path = File.join(root, path_suffix_with_ext)
11
- return path if File.file?(path)
12
- end
13
-
14
- super
15
- end
16
-
17
- # For some reason, using autoload and a patched Kernel#load doesn't work
18
- # by itself for automatically loading .rux files. Due to what I can only
19
- # surmise is one of the side-effects of autoload, requiring any .rux file
20
- # that's been marked by autoload will result in a NameError, i.e. Ruby
21
- # reports the constant isn't defined. Pretty surprising considering we're
22
- # literally in the process of _defining_ that constant. The trick is to
23
- # essentially undo the autoload by removing the constant just before
24
- # loading the .rux file that defines it.
25
- def load_missing_constant(from_mod, const_name)
26
- if require_path = from_mod.autoload?(const_name)
27
- path = search_for_file(require_path)
28
-
29
- if path && path.end_with?('.rux')
30
- from_mod.send(:remove_const, const_name)
31
- require require_path
32
- return from_mod.const_get(const_name)
33
- end
34
- end
35
-
36
- super
37
- end
38
- end
39
- end
40
-
41
- module ActiveSupport
42
- module Dependencies
43
- class << self
44
- prepend RuxRails::ActiveSupportDependenciesPatch
45
- end
46
- end
47
- end
@@ -1,27 +0,0 @@
1
- module RuxRails
2
- module BootsnapAutoloadPatch
3
- def autoload(const, path)
4
- # Bootsnap monkeypatches Module.autoload in order to leverage its load
5
- # path cache, which effectively converts a relative path into an absolute
6
- # one without incurring the cost of searching the load path.
7
- # Unfortunately, if a .rux file has already been transpiled, the cache
8
- # seems to always return the corresponding .rb file. Bootsnap's autoload
9
- # patch passes the .rb file to Ruby's original autoload, effectively
10
- # wiping out the previous autoload that pointed to the .rux file. To
11
- # fix this we have to intercept the cache lookup and force autoloading
12
- # the .rux file if one exists.
13
- cached_path = Bootsnap::LoadPathCache.load_path_cache.find(path)
14
- cached_rux_path = "#{cached_path.chomp('.rb')}.rux"
15
-
16
- if File.file?(cached_rux_path)
17
- autoload_without_bootsnap(const, cached_rux_path)
18
- else
19
- super
20
- end
21
- end
22
- end
23
- end
24
-
25
- class Module
26
- prepend RuxRails::BootsnapAutoloadPatch
27
- end
@@ -1,35 +0,0 @@
1
- require 'zeitwerk'
2
-
3
- module RuxRails
4
- module ZeitwerkLoaderPatch
5
- private
6
-
7
- def ruby?(path)
8
- super || path.end_with?('.rux')
9
- end
10
-
11
- def autoload_file(parent, cname, file)
12
- if file.end_with?('.rux')
13
- # Some older versions of Zeitwerk very naïvely try to remove only the
14
- # last 3 characters in an attempt to strip off the .rb file extension,
15
- # while newer ones only remove it if it's actually there. This line is
16
- # necessary to remove the trailing leftover period for older versions,
17
- # and remove the entire .rux extension for newer versions.
18
- cname = cname.to_s.chomp('.').chomp('.rux').to_sym
19
- else
20
- # if there is a corresponding .rux file, autoload it instead of the .rb
21
- # file
22
- rux_file = "#{file.chomp('.rb')}.rux"
23
- file = rux_file if File.exist?(rux_file)
24
- end
25
-
26
- super
27
- end
28
- end
29
- end
30
-
31
- module Zeitwerk
32
- class Loader
33
- prepend RuxRails::ZeitwerkLoaderPatch
34
- end
35
- end
@@ -1,13 +0,0 @@
1
- require 'active_support'
2
-
3
- module RuxRails
4
- class SafeBuffer < ActiveSupport::SafeBuffer
5
- def <<(value)
6
- if value.is_a?(Array)
7
- super(value.map { |v| html_escape_interpolated_argument(v) }.join.html_safe)
8
- else
9
- super
10
- end
11
- end
12
- end
13
- end
@@ -1,15 +0,0 @@
1
- class Button < ViewComponent::Base
2
- attr_reader(:outline, :size, :disabled)
3
-
4
- def initialize(outline:, size:, disabled:)
5
- @outline = outline
6
- @size = size
7
- @disabled = disabled
8
- end
9
-
10
- def call
11
- Rux.tag("button") {
12
- content
13
- }
14
- end
15
- end
@@ -1,12 +0,0 @@
1
- class HomeComponent < ViewComponent::Base
2
- def call
3
- Rux.tag("div", { class: "container" }) {
4
- Rux.create_buffer.tap { |_rux_buf_|
5
- _rux_buf_ << render(Image.new(src: "cat.png", size: "40"))
6
- _rux_buf_ << render(Button.new(outline: true, disabled: "true", size: :large)) {
7
- "Click Me!"
8
- }
9
- }.to_s
10
- }
11
- end
12
- end