proscenium 0.10.0-aarch64-linux → 0.11.0-aarch64-linux

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +207 -45
  3. data/lib/proscenium/builder.rb +41 -19
  4. data/lib/proscenium/css_module/path.rb +31 -0
  5. data/lib/proscenium/css_module/transformer.rb +82 -0
  6. data/lib/proscenium/css_module.rb +12 -25
  7. data/lib/proscenium/ensure_loaded.rb +27 -0
  8. data/lib/proscenium/ext/proscenium +0 -0
  9. data/lib/proscenium/ext/proscenium.h +3 -2
  10. data/lib/proscenium/helper.rb +85 -0
  11. data/lib/proscenium/importer.rb +110 -0
  12. data/lib/proscenium/libs/react-manager/index.jsx +101 -0
  13. data/lib/proscenium/libs/react-manager/react.js +2 -0
  14. data/lib/proscenium/libs/test.js +1 -0
  15. data/lib/proscenium/middleware/base.rb +7 -7
  16. data/lib/proscenium/middleware/engines.rb +37 -0
  17. data/lib/proscenium/middleware/esbuild.rb +3 -5
  18. data/lib/proscenium/middleware/runtime.rb +18 -0
  19. data/lib/proscenium/middleware.rb +14 -4
  20. data/lib/proscenium/{side_load/monkey.rb → monkey.rb} +19 -15
  21. data/lib/proscenium/phlex/{resolve_css_modules.rb → css_modules.rb} +28 -16
  22. data/lib/proscenium/phlex/react_component.rb +26 -27
  23. data/lib/proscenium/phlex.rb +11 -30
  24. data/lib/proscenium/railtie.rb +44 -46
  25. data/lib/proscenium/react_componentable.rb +95 -0
  26. data/lib/proscenium/resolver.rb +37 -0
  27. data/lib/proscenium/side_load.rb +13 -73
  28. data/lib/proscenium/source_path.rb +15 -0
  29. data/lib/proscenium/templates/rescues/build_error.html.erb +30 -0
  30. data/lib/proscenium/utils.rb +13 -0
  31. data/lib/proscenium/version.rb +1 -1
  32. data/lib/proscenium/view_component/css_modules.rb +11 -0
  33. data/lib/proscenium/view_component/react_component.rb +15 -15
  34. data/lib/proscenium/view_component/sideload.rb +4 -0
  35. data/lib/proscenium/view_component.rb +8 -38
  36. data/lib/proscenium.rb +22 -68
  37. metadata +23 -30
  38. data/lib/proscenium/componentable.rb +0 -63
  39. data/lib/proscenium/css_module/class_names_resolver.rb +0 -66
  40. data/lib/proscenium/css_module/resolver.rb +0 -76
  41. data/lib/proscenium/current.rb +0 -9
  42. data/lib/proscenium/phlex/component_concerns.rb +0 -9
  43. data/lib/proscenium/phlex/page.rb +0 -62
  44. data/lib/proscenium/side_load/ensure_loaded.rb +0 -25
  45. data/lib/proscenium/side_load/helper.rb +0 -41
  46. data/lib/proscenium/view_component/tag_builder.rb +0 -23
@@ -1,15 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Proscenium
4
- module Phlex::ResolveCssModules
5
- extend ActiveSupport::Concern
4
+ module Phlex::CssModules
5
+ include Proscenium::CssModule
6
6
 
7
- class_methods do
8
- attr_accessor :side_load_cache
7
+ def self.included(base)
8
+ base.extend CssModule::Path
9
+ base.extend ClassMethods
10
+ end
11
+
12
+ module ClassMethods
13
+ # Set of CSS module paths that have been resolved after being transformed from 'class' HTML
14
+ # attributes. See #process_attributes. This is here because Phlex caches attributes. Which
15
+ # means while the CSS class names will be transformed, any resolved paths will be lost in
16
+ # subsequent requests.
17
+ attr_accessor :resolved_css_module_paths
9
18
  end
10
19
 
11
20
  def before_template
12
- self.class.side_load_cache ||= Set.new
21
+ self.class.resolved_css_module_paths ||= Concurrent::Set.new
22
+ super
23
+ end
24
+
25
+ def after_template
26
+ self.class.resolved_css_module_paths.each do |path|
27
+ Proscenium::Importer.import path
28
+ end
29
+
13
30
  super
14
31
  end
15
32
 
@@ -44,24 +61,19 @@ module Proscenium
44
61
  # end
45
62
  # end
46
63
  #
47
- # The given class name should be underscored, and the resulting CSS module name will be
48
- # `camelCased` with a lower case first character.
49
- #
50
64
  # @raise [Proscenium::CssModule::Resolver::NotFound] If a CSS module file is not found for the
51
65
  # Phlex class file path.
52
66
  def process_attributes(**attributes)
53
67
  if attributes.key?(:class) && (attributes[:class] = tokens(attributes[:class])).include?('@')
54
- resolver = CssModule::ClassNamesResolver.new(attributes[:class], path)
55
- self.class.side_load_cache.merge resolver.stylesheets
56
- attributes[:class] = resolver.class_names
68
+ names = attributes[:class].is_a?(Array) ? attributes[:class] : attributes[:class].split
69
+
70
+ attributes[:class] = cssm.class_names(*names).map do |name, path|
71
+ self.class.resolved_css_module_paths << path if path
72
+ name
73
+ end
57
74
  end
58
75
 
59
76
  attributes
60
77
  end
61
-
62
- def after_template
63
- super
64
- self.class.side_load_cache&.each { |path| SideLoad.append! path, :css }
65
- end
66
78
  end
67
79
  end
@@ -1,33 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- #
4
- # Renders a <div> for use with React components, with data attributes specifying the component path
5
- # and props.
6
- #
7
- # If a block is given, it will be yielded within the div, allowing for a custom "loading" UI. If no
8
- # block is given, then a "loading..." text will be rendered. It is intended that the component is
9
- # mounted to this div, and the loading UI will then be replaced with the component's rendered
10
- # output.
11
- #
12
- # You can pass props to the component in the `:props` keyword argument.
13
- #
14
- class Proscenium::Phlex::ReactComponent < Proscenium::Phlex
15
- self.abstract_class = true
16
-
17
- include Proscenium::Componentable
18
- include Proscenium::Phlex::ComponentConcerns::CssModules
19
-
20
- # Override this to provide your own loading UI.
3
+ module Proscenium
4
+ # Renders a <div> for use with React components, with data attributes specifying the component
5
+ # path and props.
21
6
  #
22
- # @example
23
- # def template(**attributes, &block)
24
- # super do
25
- # 'Look at me! I am loading now...'
26
- # end
27
- # end
7
+ # If a block is given, it will be yielded within the div, allowing for a custom "loading" UI. If
8
+ # no block is given, then a "loading..." text will be rendered. It is intended that the component
9
+ # is mounted to this div, and the loading UI will then be replaced with the component's rendered
10
+ # output.
28
11
  #
29
- # @yield the given block to a `div` within the top level component div.
30
- def template(**attributes, &block)
31
- send root_tag, **{ data: data_attributes }.deep_merge(attributes), &block
12
+ # You can pass props to the component in the `:props` keyword argument.
13
+ class Phlex::ReactComponent < Phlex
14
+ self.abstract_class = true
15
+
16
+ include ReactComponentable
17
+
18
+ # Override this to provide your own loading UI.
19
+ #
20
+ # @example
21
+ # def template(**attributes, &block)
22
+ # super do
23
+ # 'Look at me! I am loading now...'
24
+ # end
25
+ # end
26
+ #
27
+ # @yield the given block to a `div` within the top level component div.
28
+ def template(**attributes, &block)
29
+ send root_tag, **{ data: data_attributes }.deep_merge(attributes), &block
30
+ end
32
31
  end
33
32
  end
@@ -5,54 +5,35 @@ require 'phlex-rails'
5
5
  module Proscenium
6
6
  class Phlex < ::Phlex::HTML
7
7
  extend ActiveSupport::Autoload
8
- include Proscenium::CssModule
9
8
 
10
- autoload :Page
9
+ autoload :CssModules
11
10
  autoload :ReactComponent
12
- autoload :ResolveCssModules
13
- autoload :ComponentConcerns
14
11
 
15
12
  extend ::Phlex::Rails::HelperMacros
16
13
  include ::Phlex::Rails::Helpers::JavaScriptIncludeTag
17
14
  include ::Phlex::Rails::Helpers::StyleSheetLinkTag
15
+ include Proscenium::SourcePath
16
+ include CssModules
18
17
 
19
- define_output_helper :side_load_stylesheets
20
- define_output_helper :side_load_javascripts
18
+ define_output_helper :side_load_stylesheets # deprecated
19
+ define_output_helper :include_stylesheets
20
+ define_output_helper :side_load_javascripts # deprecated
21
+ define_output_helper :include_javascripts
22
+ define_output_helper :declare_lazy_scripts
21
23
 
22
- # Side loads the class, and its super classes that respond to `.path`. Assign the
23
- # `abstract_class` class variable to any abstract class, and it will not be side loaded.
24
- # Additionally, if the class responds to `side_load`, then that method is called.
25
24
  module Sideload
26
25
  def before_template
27
- klass = self.class
28
-
29
- if !klass.abstract_class && respond_to?(:side_load, true)
30
- side_load
31
- klass = klass.superclass
32
- end
33
-
34
- while !klass.abstract_class && klass.respond_to?(:path) && klass.path
35
- Proscenium::SideLoad.append klass.path
36
- klass = klass.superclass
37
- end
26
+ Proscenium::SideLoad.sideload_inheritance_chain self
38
27
 
39
28
  super
40
29
  end
41
30
  end
42
31
 
43
32
  class << self
44
- attr_accessor :path, :abstract_class
33
+ attr_accessor :abstract_class
45
34
 
46
35
  def inherited(child)
47
- unless child.path
48
- child.path = if caller_locations(1, 1).first.label == 'inherited'
49
- Pathname.new caller_locations(2, 1).first.path
50
- else
51
- Pathname.new caller_locations(1, 1).first.path
52
- end
53
- end
54
-
55
- child.prepend Sideload if Rails.application.config.proscenium.side_load
36
+ child.prepend Sideload
56
37
 
57
38
  super
58
39
  end
@@ -6,14 +6,6 @@ require 'proscenium/log_subscriber'
6
6
  ENV['RAILS_ENV'] = Rails.env
7
7
 
8
8
  module Proscenium
9
- FILE_EXTENSIONS = ['js', 'mjs', 'ts', 'jsx', 'tsx', 'css', 'js.map', 'mjs.map', 'jsx.map',
10
- 'ts.map', 'tsx.map', 'css.map'].freeze
11
-
12
- APPLICATION_INCLUDE_PATHS = ['config', 'app/assets', 'app/views', 'lib', 'node_modules'].freeze
13
-
14
- # Environment variables that should always be passed to the builder.
15
- DEFAULT_ENV_VARS = Set['RAILS_ENV', 'NODE_ENV'].freeze
16
-
17
9
  class << self
18
10
  def config
19
11
  @config ||= Railtie.config.proscenium
@@ -26,66 +18,72 @@ module Proscenium
26
18
  config.proscenium = ActiveSupport::OrderedOptions.new
27
19
  config.proscenium.debug = false
28
20
  config.proscenium.side_load = true
29
- config.proscenium.code_splitting = false
21
+ config.proscenium.code_splitting = true
22
+
23
+ # TODO: implement!
30
24
  config.proscenium.cache_query_string = Rails.env.production? && ENV.fetch('REVISION', nil)
31
25
  config.proscenium.cache_max_age = 2_592_000 # 30 days
32
- config.proscenium.include_paths = Set.new(APPLICATION_INCLUDE_PATHS)
33
26
 
34
27
  # List of environment variable names that should be passed to the builder, which will then be
35
28
  # passed to esbuild's `Define` option. Being explicit about which environment variables are
36
29
  # defined means a faster build, as esbuild will have less to do.
37
30
  config.proscenium.env_vars = Set.new
38
31
 
39
- # A hash of gems that can be side loaded. Assets from gems listed here can be side loaded.
40
- #
41
- # Because side loading uses URL paths, any gem dependencies that side load assets will fail,
42
- # because the URL path will be relative to the application's root, and not the gem's root. By
43
- # specifying a list of gems that can be side loaded, Proscenium will be able to resolve the URL
44
- # path to the gem's root, and side load the asset.
32
+ # Rails engines to expose and allow Proscenium to serve their assets.
45
33
  #
46
- # Side loading gems rely on NPM and a package.json file in the gem root. This ensures that any
47
- # dependencies are resolved correctly. This is required even if your gem has no package
48
- # dependencies.
34
+ # A Rails engine that has assets, can add Proscenium as a gem dependency, and then add itself
35
+ # to this list. Proscenium will then serve the engine's assets at the URL path beginning with
36
+ # the engine name.
49
37
  #
50
38
  # Example:
51
- # config.proscenium.side_load_gems['mygem'] = {
52
- # root: gem_root,
53
- # package_name: 'mygem'
54
- # }
55
- config.proscenium.side_load_gems = {}
56
-
57
- initializer 'proscenium.configuration' do |app|
58
- options = app.config.proscenium
59
- options.include_paths = Set.new(APPLICATION_INCLUDE_PATHS) if options.include_paths.blank?
39
+ # class Gem1::Engine < ::Rails::Engine
40
+ # config.proscenium.engines << self
41
+ # end
42
+ config.proscenium.engines = Set.new
43
+
44
+ config.action_dispatch.rescue_templates = {
45
+ 'Proscenium::Builder::BuildError' => 'build_error'
46
+ }
47
+
48
+ initializer 'proscenium.debugging' do
49
+ if Rails.gem_version >= Gem::Version.new('7.1.0')
50
+ tpl_path = root.join('lib', 'proscenium', 'templates').to_s
51
+ ActionDispatch::DebugView::RESCUES_TEMPLATE_PATHS << tpl_path
52
+ end
60
53
  end
61
54
 
62
55
  initializer 'proscenium.middleware' do |app|
63
- app.middleware.insert_after ActionDispatch::Static, Proscenium::Middleware
64
- app.middleware.insert_after ActionDispatch::Static, Rack::ETag, 'no-cache'
65
- app.middleware.insert_after ActionDispatch::Static, Rack::ConditionalGet
56
+ app.middleware.insert_after ActionDispatch::Static, Middleware
57
+ # app.middleware.insert_after ActionDispatch::Static, Rack::ETag, 'no-cache'
58
+ # app.middleware.insert_after ActionDispatch::Static, Rack::ConditionalGet
66
59
  end
67
60
 
68
- initializer 'proscenium.side_loading' do |app|
69
- if app.config.proscenium.side_load
70
- Proscenium::Current.loaded ||= SideLoad::EXTENSIONS.to_h { |e| [e, Set.new] }
71
-
72
- ActiveSupport.on_load(:action_view) do
73
- ActionView::Base.include Proscenium::SideLoad::Helper
74
-
75
- ActionView::TemplateRenderer.prepend SideLoad::Monkey::TemplateRenderer
76
- ActionView::PartialRenderer.prepend SideLoad::Monkey::PartialRenderer
77
- end
78
-
79
- ActiveSupport.on_load(:action_controller) do
80
- ActionController::Base.include Proscenium::SideLoad::EnsureLoaded
81
- end
61
+ initializer 'proscenium.monkey_patches' do
62
+ ActiveSupport.on_load(:action_view) do
63
+ ActionView::TemplateRenderer.prepend Monkey::TemplateRenderer
64
+ ActionView::PartialRenderer.prepend Monkey::PartialRenderer
82
65
  end
83
66
  end
84
67
 
85
68
  initializer 'proscenium.helper' do
86
69
  ActiveSupport.on_load(:action_view) do
87
- ActionView::Base.include Proscenium::Helper
70
+ ActionView::Base.include Helper
71
+ end
72
+
73
+ ActiveSupport.on_load(:action_controller) do
74
+ ActionController::Base.include EnsureLoaded
88
75
  end
89
76
  end
90
77
  end
91
78
  end
79
+
80
+ if Rails.gem_version < Gem::Version.new('7.1.0')
81
+ class ActionDispatch::DebugView
82
+ def initialize(assigns)
83
+ tpl_path = Proscenium::Railtie.root.join('lib', 'proscenium', 'templates').to_s
84
+ paths = [RESCUES_TEMPLATE_PATH, tpl_path]
85
+ lookup_context = ActionView::LookupContext.new(paths)
86
+ super(lookup_context, assigns, nil)
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Proscenium
4
+ module ReactComponentable
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ # @return [Hash] the props to pass to the React component.
9
+ attr_writer :props
10
+
11
+ # The HTML tag to use as the wrapping element for the component. You can reassign this in your
12
+ # component class to use a different tag:
13
+ #
14
+ # class MyComponent < Proscenium::ViewComponent::ReactComponent
15
+ # self.root_tag = :span
16
+ # end
17
+ #
18
+ # @return [Symbol]
19
+ class_attribute :root_tag, instance_predicate: false, default: :div
20
+
21
+ # By default, the template block (content) of the component will be server rendered as normal.
22
+ # However, when React hydrates and takes control of the component, it's content will be
23
+ # replaced by React with the JavaScript rendered content. Enabling this option will forward
24
+ # the server rendered content as the `children` prop passed to the React component.
25
+ #
26
+ # @example
27
+ #
28
+ # const Component = ({ children }) => {
29
+ # return <div dangerouslySetInnerHTML={{ __html: children }} />
30
+ # }
31
+ #
32
+ # @return [Boolean]
33
+ class_attribute :forward_children, default: false
34
+
35
+ # Lazy load the component using IntersectionObserver?
36
+ #
37
+ # @return [Boolean]
38
+ class_attribute :lazy, default: false
39
+
40
+ class_attribute :loader
41
+
42
+ # @return [String] the URL path to the component manager.
43
+ class_attribute :manager, default: '/@proscenium/react-manager/index.jsx'
44
+ end
45
+
46
+ class_methods do
47
+ def sideload
48
+ Importer.import manager
49
+ Importer.sideload source_path, lazy: true
50
+ end
51
+ end
52
+
53
+ # @param props: [Hash]
54
+ def initialize(lazy: self.class.lazy, loader: self.class.loader, props: {})
55
+ self.lazy = lazy
56
+ self.loader = loader
57
+ @props = props
58
+ end
59
+
60
+ # The absolute URL path to the javascript component.
61
+ def virtual_path
62
+ @virtual_path ||= Resolver.resolve self.class.source_path.sub_ext('.jsx').to_s
63
+ end
64
+
65
+ def props
66
+ @props ||= {}
67
+ end
68
+
69
+ private
70
+
71
+ def data_attributes
72
+ {
73
+ proscenium_component_path: Pathname.new(virtual_path).to_s,
74
+ proscenium_component_props: prepared_props,
75
+ proscenium_component_lazy: lazy
76
+ }.tap do |x|
77
+ x[:proscenium_component_forward_children] = true if forward_children?
78
+ end
79
+ end
80
+
81
+ def prepared_props
82
+ props.deep_transform_keys do |term|
83
+ # This ensures that the first letter after a slash is not capitalized.
84
+ string = term.to_s.split('/').map { |str| str.camelize :lower }.join('/')
85
+
86
+ # Reverses the effect of ActiveSupport::Inflector.camelize converting slashes into `::`.
87
+ string.gsub '::', '/'
88
+ end.to_json
89
+ end
90
+
91
+ def loader_component
92
+ render Loader::Component.new(loader, @html_class, data_attributes, tag: @html_tag)
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/current_attributes'
4
+
5
+ module Proscenium
6
+ class Resolver < ActiveSupport::CurrentAttributes
7
+ # TODO: cache this across requests in production.
8
+ attribute :resolved
9
+
10
+ # Resolve the given `path` to a URL path.
11
+ #
12
+ # @param path [String] Can be URL path, file system path, or bare specifier (ie. NPM package).
13
+ # @return [String] URL path.
14
+ #
15
+ # rubocop:disable Metrics/*
16
+ def self.resolve(path)
17
+ self.resolved ||= {}
18
+
19
+ self.resolved[path] ||= begin
20
+ if path.start_with?('./', '../')
21
+ raise ArgumentError, 'path must be an absolute file system or URL path'
22
+ end
23
+
24
+ if path.start_with?('@proscenium/')
25
+ "/#{path}"
26
+ elsif (engine = Proscenium.config.engines.find { |e| path.start_with? "#{e.root}/" })
27
+ path.sub(/^#{engine.root}/, "/#{engine.engine_name}")
28
+ elsif path.start_with?("#{Rails.root}/")
29
+ path.delete_prefix Rails.root.to_s
30
+ else
31
+ Builder.resolve path
32
+ end
33
+ end
34
+ end
35
+ # rubocop:enable Metrics/*
36
+ end
37
+ end
@@ -2,84 +2,24 @@
2
2
 
3
3
  module Proscenium
4
4
  class SideLoad
5
- extend ActiveSupport::Autoload
6
-
7
- NotIncludedError = Class.new(StandardError)
8
-
9
- autoload :Monkey
10
- autoload :Helper
11
- autoload :EnsureLoaded
12
-
13
- EXTENSIONS = %i[js css].freeze
14
- EXTENSION_MAP = {
15
- '.module.css' => :css,
16
- '.css' => :css,
17
- '.tsx' => :js,
18
- '.ts' => :js,
19
- '.jsx' => :js,
20
- '.js' => :js
21
- }.freeze
22
-
23
- attr_reader :path
24
-
25
5
  class << self
26
- # Side load the given asset `path`, by appending it to `Proscenium::Current.loaded`, which is
27
- # a Set of 'js' and 'css' asset paths. This is idempotent, so side loading will never include
28
- # duplicates.
6
+ # Side loads the class, and its super classes that respond to `.source_path`.
29
7
  #
30
- # @return [Array] appended URL paths
31
- def append(path, extension_map = EXTENSION_MAP)
32
- new(path, extension_map).append
33
- end
34
-
35
- # Side load the given `path` at `type`, without first resolving the path. This still respects
36
- # idempotency of `Proscenium::Current.loaded`.
8
+ # Assign the `abstract_class` class variable to any abstract class, and it will not be side
9
+ # loaded. Additionally, if the class responds to `#sideload?`, and it returns false, it will
10
+ # not be side loaded.
37
11
  #
38
- # @param path [String]
39
- # @param type [Symbol] :js or :css
40
- def append!(path, type)
41
- return if Proscenium::Current.loaded[type].include?(path)
42
-
43
- Proscenium::Current.loaded[type] << log(path)
44
- end
45
-
46
- def log(value)
47
- ActiveSupport::Notifications.instrument('sideload.proscenium', identifier: value)
48
-
49
- value
50
- end
51
- end
52
-
53
- # @param path [Pathname, String] The path of the file to be side loaded.
54
- # @param extension_map [Hash] File extensions to side load.
55
- def initialize(path, extension_map = EXTENSION_MAP)
56
- @path = (path.is_a?(Pathname) ? path : Rails.root.join(path)).sub_ext('')
57
- @extension_map = extension_map
58
-
59
- Proscenium::Current.loaded ||= EXTENSIONS.index_with { |_e| Set.new }
60
- end
61
-
62
- def append
63
- @extension_map.filter_map do |ext, type|
64
- next unless (resolved_path = resolve_path(path.sub_ext(ext)))
65
-
66
- # Make sure path is not already side loaded.
67
- unless Proscenium::Current.loaded[type].include?(resolved_path)
68
- Proscenium::Current.loaded[type] << log(resolved_path)
12
+ # If the class responds to `.sideload`, it will be called instead of the regular side loading.
13
+ # You can use this to customise what is side loaded.
14
+ def sideload_inheritance_chain(obj)
15
+ return if !Proscenium.config.side_load || (obj.respond_to?(:sideload?) && !obj.sideload?)
16
+
17
+ klass = obj.class
18
+ while klass.respond_to?(:source_path) && klass.source_path && !klass.abstract_class
19
+ klass.respond_to?(:sideload) ? klass.sideload : Importer.sideload(klass.source_path)
20
+ klass = klass.superclass
69
21
  end
70
-
71
- resolved_path
72
22
  end
73
23
  end
74
-
75
- private
76
-
77
- def log(...)
78
- self.class.log(...)
79
- end
80
-
81
- def resolve_path(path)
82
- path.exist? ? Utils.resolve_path(path.to_s) : nil
83
- end
84
24
  end
85
25
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Include this into any class to expose a `source_path` class and instance method, which will return
4
+ # the absolute file system path to the current object.
5
+ module Proscenium::SourcePath
6
+ def self.included(base)
7
+ base.extend ClassMethods
8
+ end
9
+
10
+ module ClassMethods
11
+ def source_path
12
+ @source_path ||= name.nil? ? nil : Pathname.new(const_source_location(name).first)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,30 @@
1
+ <header>
2
+ <h1>
3
+ <%= Rails.gem_version >= Gem::Version.new('7.1.0') ? @exception_wrapper.exception_class_name : @exception.class.to_s %>
4
+ <% if params_valid? && @request.parameters['controller'] %>
5
+ in <%= @request.parameters['controller'].camelize %>Controller<% if @request.parameters['action'] %>#<%= @request.parameters['action'] %><% end %>
6
+ <% end %>
7
+ </h1>
8
+ </header>
9
+
10
+ <main role="main" id="container">
11
+ <%= render "rescues/message_and_suggestions", exception: @exception, exception_wrapper: Rails.gem_version >= Gem::Version.new('7.1.0') ? @exception_wrapper : nil %>
12
+
13
+ <% if @exception.error['location'] %>
14
+ <div class="source">
15
+ <div class="data">
16
+ <pre>
17
+
18
+ <%= @exception.error['location']['file'] %>:<%= @exception.error['location']['line'] %>:<%= @exception.error['location']['column'] %>
19
+
20
+ <%= @exception.error['location']['line'].to_s.rjust 5 %> │ <%= @exception.error['location']['line_text'] %>
21
+ │ <%= (@exception.error['location']['length'] > 1 ? "~" * @exception.error['location']['length'] : "^").rjust(@exception.error['location']['column'] + @exception.error['location']['length']) %>
22
+ <%- if @exception.error['location']['suggestion'].present? -%> + │ <%= @exception.error['location']['suggestion'].rjust(@exception.error['location']['column'] + 1) %>
23
+ <% else %> <%- end -%>
24
+ </pre>
25
+ </div>
26
+ </div>
27
+ <% end %>
28
+
29
+ <%= render template: "rescues/_request_and_response" %>
30
+ </main>
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Proscenium
4
+ module Utils
5
+ module_function
6
+
7
+ # @param value [#to_s] The value to create the digest from. This will usually be a `Pathname`.
8
+ # @return [String] digest of the given value.
9
+ def digest(value)
10
+ Digest::SHA1.hexdigest(value.to_s)[..7]
11
+ end
12
+ end
13
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Proscenium
4
- VERSION = '0.10.0'
4
+ VERSION = '0.11.0'
5
5
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Proscenium
4
+ module ViewComponent::CssModules
5
+ include Proscenium::CssModule
6
+
7
+ def self.included(base)
8
+ base.extend CssModule::Path
9
+ end
10
+ end
11
+ end