brut 0.0.13 → 0.0.20
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 +4 -4
- data/Gemfile.lock +4 -6
- data/brut.gemspec +1 -3
- data/lib/brut/back_end.rb +7 -0
- data/lib/brut/cli/apps/scaffold.rb +16 -24
- data/lib/brut/framework/config.rb +4 -43
- data/lib/brut/framework/mcp.rb +1 -1
- data/lib/brut/front_end/asset_path_resolver.rb +15 -0
- data/lib/brut/front_end/component.rb +66 -234
- data/lib/brut/front_end/components/constraint_violations.rb +9 -9
- data/lib/brut/front_end/components/form_tag.rb +16 -28
- data/lib/brut/front_end/components/i18n_translations.rb +12 -13
- data/lib/brut/front_end/components/input.rb +0 -1
- data/lib/brut/front_end/components/inputs/csrf_token.rb +2 -2
- data/lib/brut/front_end/components/inputs/radio_button.rb +6 -6
- data/lib/brut/front_end/components/inputs/select.rb +13 -20
- data/lib/brut/front_end/components/inputs/text_field.rb +18 -33
- data/lib/brut/front_end/components/inputs/textarea.rb +11 -26
- data/lib/brut/front_end/components/locale_detection.rb +2 -2
- data/lib/brut/front_end/components/page_identifier.rb +3 -5
- data/lib/brut/front_end/components/{time.rb → time_tag.rb} +13 -10
- data/lib/brut/front_end/components/traceparent.rb +5 -6
- data/lib/brut/front_end/http_method.rb +4 -0
- data/lib/brut/front_end/inline_svg_locator.rb +21 -0
- data/lib/brut/front_end/layout.rb +3 -0
- data/lib/brut/front_end/page.rb +16 -29
- data/lib/brut/front_end/request_context.rb +13 -0
- data/lib/brut/front_end/routing.rb +3 -2
- data/lib/brut/front_end.rb +41 -0
- data/lib/brut/i18n/base_methods.rb +14 -8
- data/lib/brut/i18n/for_back_end.rb +5 -0
- data/lib/brut/i18n/for_cli.rb +2 -1
- data/lib/brut/i18n/for_html.rb +9 -1
- data/lib/brut/i18n.rb +1 -0
- data/lib/brut/sinatra_helpers.rb +12 -7
- data/lib/brut/spec_support/component_support.rb +9 -9
- data/lib/brut/spec_support/e2e_support.rb +4 -0
- data/lib/brut/spec_support/matchers/have_i18n_string.rb +5 -0
- data/lib/brut/spec_support/rspec_setup.rb +1 -0
- data/lib/brut/spec_support.rb +4 -3
- data/lib/brut/version.rb +1 -1
- data/lib/brut.rb +2 -46
- metadata +13 -41
- data/lib/brut/front_end/template.rb +0 -47
- data/lib/brut/front_end/templates/block_filter.rb +0 -61
- data/lib/brut/front_end/templates/erb_engine.rb +0 -26
- data/lib/brut/front_end/templates/erb_parser.rb +0 -84
- data/lib/brut/front_end/templates/escapable_filter.rb +0 -20
- data/lib/brut/front_end/templates/html_safe_string.rb +0 -68
- data/lib/brut/front_end/templates/locator.rb +0 -60
@@ -8,6 +8,19 @@
|
|
8
8
|
# and create an initializer for it that accepts the `clock:` keyword argument, the managed instance of {Clock} will be passed into it
|
9
9
|
# when Brut creates an instance of the class.
|
10
10
|
class Brut::FrontEnd::RequestContext
|
11
|
+
|
12
|
+
def self.current
|
13
|
+
Thread.current.thread_variable_get(:request_context)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Create an instance of klass injected with the request context.
|
17
|
+
def self.inject(klass, request_params: nil)
|
18
|
+
self.current.then { |request_context|
|
19
|
+
request_context.as_constructor_args(klass,request_params:)
|
20
|
+
}.then { |constructor_args|
|
21
|
+
klass.new(**constructor_args)
|
22
|
+
}
|
23
|
+
end
|
11
24
|
# Create a new RequestContext based on some of the information provided by Rack
|
12
25
|
#
|
13
26
|
# @param [Hash] env the Rack `env` object, as available to any middleware
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require "uri"
|
2
|
+
require "phlex"
|
2
3
|
|
3
4
|
# Holds the registered routes for this app.
|
4
5
|
class Brut::FrontEnd::Routing
|
@@ -218,11 +219,11 @@ private
|
|
218
219
|
end
|
219
220
|
uri = URI(joined_path)
|
220
221
|
uri.query = URI.encode_www_form(query_string_params)
|
221
|
-
uri
|
222
|
+
uri.extend(Phlex::SGML::SafeObject)
|
222
223
|
end
|
223
224
|
|
224
225
|
def url(**query_string_params)
|
225
|
-
request_context =
|
226
|
+
request_context = Brut::FrontEnd::RequestContext.current
|
226
227
|
path = self.path(**query_string_params)
|
227
228
|
host = if request_context
|
228
229
|
request_context[:host]
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# In Brut, the _front end_ is considered anything that interacts directly with a web browser or HTTP. This includes rendering HTML,
|
2
|
+
# managing JavaScript and CSS, and processing form submissions. It contrasts to {Brut::BackEnd}, which handles the business logic
|
3
|
+
# and database.
|
4
|
+
#
|
5
|
+
# You {Brut::App} defines pages, forms, and actions. A page is backed by a subclass of {Brut::FrontEnd::Page}, which provides
|
6
|
+
# dynamic data for rendering. A page can reference {Brut::FrontEnd::Component} subclasses to allow functional decomposition of front
|
7
|
+
# end logic and markup, as well as re-use. Both pages and components have ERB files that describe the HTML to be rendered.
|
8
|
+
#
|
9
|
+
# A {Brut::FrontEnd::Form} subclass defines a form that a browser will submit to your app. That
|
10
|
+
# submission is processed by a {Brut::FrontEnd::Handler} subclass. Handlers can also respond to other HTTP requests.
|
11
|
+
#
|
12
|
+
# In addition to responding to requests, you can subclass {Brut::FrontEnd::RouteHook} or {Brut::FrontEnd::Middleware} to perform
|
13
|
+
# further manipulation of the request.
|
14
|
+
#
|
15
|
+
# The entire front-end is based on Rack, so you should be able to achieve anything you need to.
|
16
|
+
module Brut::FrontEnd
|
17
|
+
autoload(:AssetMetadata, "brut/front_end/asset_metadata")
|
18
|
+
autoload(:AssetPathResolver, "brut/front_end/asset_path_resolver")
|
19
|
+
autoload(:Component, "brut/front_end/component")
|
20
|
+
autoload(:Components, "brut/front_end/component")
|
21
|
+
autoload(:Download, "brut/front_end/download")
|
22
|
+
autoload(:Flash, "brut/front_end/flash")
|
23
|
+
autoload(:Form, "brut/front_end/form")
|
24
|
+
autoload(:GenericResponse, "brut/front_end/generic_response")
|
25
|
+
autoload(:Handler, "brut/front_end/handler")
|
26
|
+
autoload(:Handlers, "brut/front_end/handler")
|
27
|
+
autoload(:HandlingResults, "brut/front_end/handling_results")
|
28
|
+
autoload(:HttpMethod, "brut/front_end/http_method")
|
29
|
+
autoload(:HttpStatus, "brut/front_end/http_status")
|
30
|
+
autoload(:InlineSvgLocator, "brut/front_end/inline_svg_locator")
|
31
|
+
autoload(:Layout, "brut/front_end/layout")
|
32
|
+
autoload(:Middleware, "brut/front_end/middleware")
|
33
|
+
autoload(:Middlewares, "brut/front_end/middleware")
|
34
|
+
autoload(:Page, "brut/front_end/page")
|
35
|
+
autoload(:Pages, "brut/front_end/page")
|
36
|
+
autoload(:RequestContext, "brut/front_end/request_context")
|
37
|
+
autoload(:RouteHook, "brut/front_end/route_hook")
|
38
|
+
autoload(:RouteHooks, "brut/front_end/route_hook")
|
39
|
+
autoload(:Routing, "brut/front_end/routing")
|
40
|
+
autoload(:Session, "brut/front_end/session")
|
41
|
+
end
|
@@ -1,6 +1,11 @@
|
|
1
|
-
# Interface for translations
|
2
|
-
#
|
3
|
-
#
|
1
|
+
# Interface for translations, preferred over Ruby's I18n classes. Note that this is a
|
2
|
+
# base module and not intended to be directly used in your classes. Include one of
|
3
|
+
# the other modules in this namespace:
|
4
|
+
#
|
5
|
+
# * {Brut::I18n::ForHTML} for components or pages, or anything use Phlex
|
6
|
+
# * {Brut::I18n::ForCLI} for CLI apps
|
7
|
+
# * {Brut::I18n::ForBackEnd} for back-end classes that aren't generating HTML
|
8
|
+
#
|
4
9
|
module Brut::I18n::BaseMethods
|
5
10
|
|
6
11
|
# Access a translation and insert interpolated elemens as needed. This will use the provided key to determine
|
@@ -101,7 +106,7 @@ module Brut::I18n::BaseMethods
|
|
101
106
|
# }
|
102
107
|
# # in your code for HomePage
|
103
108
|
# t(page: [ :captions, :new ]) # => New Widgets
|
104
|
-
def t(key=:look_in_rest,**rest)
|
109
|
+
def t(key=:look_in_rest,**rest,&block)
|
105
110
|
if key == :look_in_rest
|
106
111
|
|
107
112
|
page = rest.delete(:page)
|
@@ -126,13 +131,14 @@ module Brut::I18n::BaseMethods
|
|
126
131
|
key = Array(key).join('.')
|
127
132
|
key = [key,"general.#{key}"]
|
128
133
|
end
|
129
|
-
if
|
134
|
+
if !block.nil?
|
130
135
|
if rest[:block]
|
131
136
|
raise ArgumentError,"t was given a block and a block: param. You can't do both "
|
132
137
|
end
|
133
|
-
|
138
|
+
block_contents = safe(capture(&block))
|
139
|
+
rest[:block] = block_contents
|
134
140
|
end
|
135
|
-
|
141
|
+
t_direct(key,**rest)
|
136
142
|
rescue I18n::MissingInterpolationArgument => ex
|
137
143
|
if ex.key.to_s == "block"
|
138
144
|
raise ArgumentError,"One of the keys #{key.join(", ")} contained a %{block} interpolation value: '#{ex.string}'. This means you must use t_html *and* yield a block to it"
|
@@ -168,7 +174,7 @@ module Brut::I18n::BaseMethods
|
|
168
174
|
}
|
169
175
|
escaped_interpolated_values = interpolated_values.map { |key,value|
|
170
176
|
if value.kind_of?(String)
|
171
|
-
[ key,
|
177
|
+
[ key, CGI.escapeHTML(value) ]
|
172
178
|
else
|
173
179
|
[ key, value ]
|
174
180
|
end
|
data/lib/brut/i18n/for_cli.rb
CHANGED
data/lib/brut/i18n/for_html.rb
CHANGED
@@ -1,4 +1,12 @@
|
|
1
|
+
# I18n for components or pages, which are assumed to be Phlex components.
|
2
|
+
# To use this outside of a Phlex context, you must define these two
|
3
|
+
# methods to ensure proper HTML escaping happens:
|
4
|
+
#
|
5
|
+
# * `safe` to accept a string and return a string.
|
6
|
+
# * `capture` to accept a block and return its contents as a string.
|
1
7
|
module Brut::I18n::ForHTML
|
2
8
|
include Brut::I18n::BaseMethods
|
3
|
-
def
|
9
|
+
def t(...)
|
10
|
+
safe(super)
|
11
|
+
end
|
4
12
|
end
|
data/lib/brut/i18n.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# I18n holds all the code useful for translating and localizing information. It's based on Ruby's I18n.
|
2
2
|
module Brut::I18n
|
3
3
|
autoload(:BaseMethods, "brut/i18n/base_methods")
|
4
|
+
autoload(:ForBackEnd, "brut/i18n/for_back_end")
|
4
5
|
autoload(:ForCLI, "brut/i18n/for_cli")
|
5
6
|
autoload(:ForHTML, "brut/i18n/for_html")
|
6
7
|
autoload(:HTTPAcceptLanguage, "brut/i18n/http_accept_language")
|
data/lib/brut/sinatra_helpers.rb
CHANGED
@@ -77,15 +77,16 @@ module Brut::SinatraHelpers
|
|
77
77
|
|
78
78
|
Brut.container.instrumentation.span(page_class.name) do |span|
|
79
79
|
span.add_prefixed_attributes("brut", type: :page, class: page_class)
|
80
|
-
|
81
|
-
constructor_args = request_context.as_constructor_args(
|
80
|
+
constructor_args = Brut::FrontEnd::RequestContext.current.as_constructor_args(
|
82
81
|
page_class,
|
83
82
|
request_params: params,
|
84
83
|
route: brut_route,
|
85
84
|
)
|
86
85
|
span.add_prefixed_attributes("brut.initializer.args", constructor_args.map { |k,v| [k.to_s,v.class.name] }.to_h)
|
87
86
|
page_instance = page_class.new(**constructor_args)
|
87
|
+
|
88
88
|
result = page_instance.handle!
|
89
|
+
|
89
90
|
span.add_prefixed_attributes("brut", result_class: result.class)
|
90
91
|
case result
|
91
92
|
in URI => uri
|
@@ -177,7 +178,6 @@ module Brut::SinatraHelpers
|
|
177
178
|
form_class: form_class,
|
178
179
|
)
|
179
180
|
|
180
|
-
request_context = Thread.current.thread_variable_get(:request_context)
|
181
181
|
handler = handler_class.new
|
182
182
|
form = if form_class.nil?
|
183
183
|
nil
|
@@ -185,7 +185,7 @@ module Brut::SinatraHelpers
|
|
185
185
|
form_class.new(params: params)
|
186
186
|
end
|
187
187
|
|
188
|
-
process_args =
|
188
|
+
process_args = Brut::FrontEnd::RequestContext.current.as_method_args(handler,:handle,request_params: params,form: form,route:brut_route)
|
189
189
|
|
190
190
|
result = handler.handle!(**process_args)
|
191
191
|
|
@@ -193,12 +193,17 @@ module Brut::SinatraHelpers
|
|
193
193
|
in URI => uri
|
194
194
|
redirect to(uri.to_s)
|
195
195
|
in Brut::FrontEnd::Component => component_instance
|
196
|
-
|
197
|
-
in [
|
196
|
+
component_instance.call.to_s
|
197
|
+
in [
|
198
|
+
Brut::FrontEnd::Component => component_instance,
|
199
|
+
Brut::FrontEnd::HttpStatus => http_status,
|
200
|
+
]
|
201
|
+
|
198
202
|
[
|
199
203
|
http_status.to_i,
|
200
|
-
|
204
|
+
component_instance.call.to_s,
|
201
205
|
]
|
206
|
+
|
202
207
|
in Brut::FrontEnd::HttpStatus => http_status
|
203
208
|
http_status.to_i
|
204
209
|
in Brut::FrontEnd::Download => download
|
@@ -8,7 +8,7 @@ module Brut::SpecSupport::ComponentSupport
|
|
8
8
|
include Brut::SpecSupport::FlashSupport
|
9
9
|
include Brut::SpecSupport::SessionSupport
|
10
10
|
include Brut::SpecSupport::ClockSupport
|
11
|
-
include Brut::I18n::
|
11
|
+
include Brut::I18n::BaseMethods
|
12
12
|
|
13
13
|
# Render a component into its text representation. This mimics what happens when a component is used
|
14
14
|
# inside a template. You typically don't want this, but should use {#render_and_parse}, since that will
|
@@ -20,8 +20,13 @@ module Brut::SpecSupport::ComponentSupport
|
|
20
20
|
end
|
21
21
|
component.handle!
|
22
22
|
else
|
23
|
-
|
24
|
-
|
23
|
+
if block.nil?
|
24
|
+
component.call
|
25
|
+
else
|
26
|
+
component.call do
|
27
|
+
component.raw(component.safe(block.()))
|
28
|
+
end
|
29
|
+
end
|
25
30
|
end
|
26
31
|
end
|
27
32
|
|
@@ -41,7 +46,7 @@ module Brut::SpecSupport::ComponentSupport
|
|
41
46
|
# @return [Brut::SpecSupport::EnhancedNode] a wrapper around a Nokogiri node to provide convienience methods.
|
42
47
|
def render_and_parse(component,&block)
|
43
48
|
rendered_text = render(component,&block)
|
44
|
-
if !rendered_text.kind_of?(String)
|
49
|
+
if !rendered_text.kind_of?(String)
|
45
50
|
if rendered_text.kind_of?(URI::Generic)
|
46
51
|
raise "#{component.class} redirected to #{rendered_text} instead of rendering"
|
47
52
|
else
|
@@ -80,9 +85,4 @@ module Brut::SpecSupport::ComponentSupport
|
|
80
85
|
def routing_for(klass,**args)
|
81
86
|
Brut.container.routing.path(klass,**args)
|
82
87
|
end
|
83
|
-
|
84
|
-
# Escape HTML using the same code Brut uses for rendering templates.
|
85
|
-
def escape_html(...)
|
86
|
-
Brut::FrontEnd::Templates::EscapableFilter.escape_html(...)
|
87
|
-
end
|
88
88
|
end
|
@@ -80,6 +80,7 @@ class Brut::SpecSupport::RSpecSetup
|
|
80
80
|
@config.include Brut::SpecSupport::GeneralSupport
|
81
81
|
@config.include Brut::SpecSupport::ComponentSupport, component: true
|
82
82
|
@config.include Brut::SpecSupport::HandlerSupport, handler: true
|
83
|
+
@config.include Brut::SpecSupport::E2eSupport, e2e: true
|
83
84
|
@config.include Playwright::Test::Matchers, e2e: true
|
84
85
|
|
85
86
|
@config.around do |example|
|
data/lib/brut/spec_support.rb
CHANGED
@@ -5,11 +5,12 @@ module Brut
|
|
5
5
|
module SpecSupport
|
6
6
|
end
|
7
7
|
end
|
8
|
-
require_relative "spec_support/matcher"
|
9
8
|
require_relative "spec_support/component_support"
|
10
|
-
require_relative "spec_support/
|
11
|
-
require_relative "spec_support/general_support"
|
9
|
+
require_relative "spec_support/e2e_support"
|
12
10
|
require_relative "spec_support/e2e_test_server"
|
11
|
+
require_relative "spec_support/general_support"
|
12
|
+
require_relative "spec_support/handler_support"
|
13
|
+
require_relative "spec_support/matcher"
|
13
14
|
require_relative "spec_support/rspec_setup"
|
14
15
|
require_relative "factory_bot"
|
15
16
|
# Convention here is different. We don't want to autoload
|
data/lib/brut/version.rb
CHANGED
data/lib/brut.rb
CHANGED
@@ -9,52 +9,8 @@ require_relative "brut/framework"
|
|
9
9
|
#
|
10
10
|
# Have fun!
|
11
11
|
module Brut
|
12
|
-
|
13
|
-
|
14
|
-
# and database.
|
15
|
-
#
|
16
|
-
# You {Brut::App} defines pages, forms, and actions. A page is backed by a subclass of {Brut::FrontEnd::Page}, which provides
|
17
|
-
# dynamic data for rendering. A page can reference {Brut::FrontEnd::Component} subclasses to allow functional decomposition of front
|
18
|
-
# end logic and markup, as well as re-use. Both pages and components have ERB files that describe the HTML to be rendered.
|
19
|
-
#
|
20
|
-
# A {Brut::FrontEnd::Form} subclass defines a form that a browser will submit to your app. That
|
21
|
-
# submission is processed by a {Brut::FrontEnd::Handler} subclass. Handlers can also respond to other HTTP requests.
|
22
|
-
#
|
23
|
-
# In addition to responding to requests, you can subclass {Brut::FrontEnd::RouteHook} or {Brut::FrontEnd::Middleware} to perform
|
24
|
-
# further manipulation of the request.
|
25
|
-
#
|
26
|
-
# The entire front-end is based on Rack, so you should be able to achieve anything you need to.
|
27
|
-
module FrontEnd
|
28
|
-
autoload(:AssetMetadata, "brut/front_end/asset_metadata")
|
29
|
-
autoload(:Component, "brut/front_end/component")
|
30
|
-
autoload(:Components, "brut/front_end/component")
|
31
|
-
autoload(:Download, "brut/front_end/download")
|
32
|
-
autoload(:Flash, "brut/front_end/flash")
|
33
|
-
autoload(:Form, "brut/front_end/form")
|
34
|
-
autoload(:Handler, "brut/front_end/handler")
|
35
|
-
autoload(:Handlers, "brut/front_end/handler")
|
36
|
-
autoload(:HandlingResults, "brut/front_end/handling_results")
|
37
|
-
autoload(:HttpMethod, "brut/front_end/http_method")
|
38
|
-
autoload(:HttpStatus, "brut/front_end/http_status")
|
39
|
-
autoload(:GenericResponse, "brut/front_end/generic_response")
|
40
|
-
autoload(:Middleware, "brut/front_end/middleware")
|
41
|
-
autoload(:Middlewares, "brut/front_end/middleware")
|
42
|
-
autoload(:Page, "brut/front_end/page")
|
43
|
-
autoload(:Pages, "brut/front_end/page")
|
44
|
-
autoload(:RequestContext, "brut/front_end/request_context")
|
45
|
-
autoload(:RouteHook, "brut/front_end/route_hook")
|
46
|
-
autoload(:RouteHooks, "brut/front_end/route_hook")
|
47
|
-
autoload(:Routing, "brut/front_end/routing")
|
48
|
-
autoload(:Session, "brut/front_end/session")
|
49
|
-
end
|
50
|
-
# The _back end_ of a Brut app is where your app's business logic and database are managed. While the bulk of your Brut app's code
|
51
|
-
# will be in the back end, Brut is far less prescriptive about how to manage that than it is the front end.
|
52
|
-
module BackEnd
|
53
|
-
autoload(:Validators, "brut/back_end/validator")
|
54
|
-
autoload(:Sidekiq, "brut/back_end/sidekiq")
|
55
|
-
# Do not put SeedData here - it must be loaded only when needed
|
56
|
-
end
|
57
|
-
# I18n is where internationalization and localization support lives.
|
12
|
+
autoload(:FrontEnd, "brut/front_end")
|
13
|
+
autoload(:BackEnd, "brut/back_end")
|
58
14
|
autoload(:I18n, "brut/i18n")
|
59
15
|
autoload(:Instrumentation,"brut/instrumentation")
|
60
16
|
autoload(:SinatraHelpers, "brut/sinatra_helpers")
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: brut
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.20
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Bryant Copeland
|
@@ -80,7 +80,7 @@ dependencies:
|
|
80
80
|
- !ruby/object:Gem::Version
|
81
81
|
version: '0'
|
82
82
|
- !ruby/object:Gem::Dependency
|
83
|
-
name:
|
83
|
+
name: phlex
|
84
84
|
requirement: !ruby/object:Gem::Requirement
|
85
85
|
requirements:
|
86
86
|
- - ">="
|
@@ -94,7 +94,7 @@ dependencies:
|
|
94
94
|
- !ruby/object:Gem::Version
|
95
95
|
version: '0'
|
96
96
|
- !ruby/object:Gem::Dependency
|
97
|
-
name:
|
97
|
+
name: prism
|
98
98
|
requirement: !ruby/object:Gem::Requirement
|
99
99
|
requirements:
|
100
100
|
- - ">="
|
@@ -108,7 +108,7 @@ dependencies:
|
|
108
108
|
- !ruby/object:Gem::Version
|
109
109
|
version: '0'
|
110
110
|
- !ruby/object:Gem::Dependency
|
111
|
-
name:
|
111
|
+
name: rack-protection
|
112
112
|
requirement: !ruby/object:Gem::Requirement
|
113
113
|
requirements:
|
114
114
|
- - ">="
|
@@ -122,7 +122,7 @@ dependencies:
|
|
122
122
|
- !ruby/object:Gem::Version
|
123
123
|
version: '0'
|
124
124
|
- !ruby/object:Gem::Dependency
|
125
|
-
name:
|
125
|
+
name: rackup
|
126
126
|
requirement: !ruby/object:Gem::Requirement
|
127
127
|
requirements:
|
128
128
|
- - ">="
|
@@ -177,34 +177,6 @@ dependencies:
|
|
177
177
|
- - ">="
|
178
178
|
- !ruby/object:Gem::Version
|
179
179
|
version: '0'
|
180
|
-
- !ruby/object:Gem::Dependency
|
181
|
-
name: temple
|
182
|
-
requirement: !ruby/object:Gem::Requirement
|
183
|
-
requirements:
|
184
|
-
- - ">="
|
185
|
-
- !ruby/object:Gem::Version
|
186
|
-
version: '0'
|
187
|
-
type: :runtime
|
188
|
-
prerelease: false
|
189
|
-
version_requirements: !ruby/object:Gem::Requirement
|
190
|
-
requirements:
|
191
|
-
- - ">="
|
192
|
-
- !ruby/object:Gem::Version
|
193
|
-
version: '0'
|
194
|
-
- !ruby/object:Gem::Dependency
|
195
|
-
name: tilt
|
196
|
-
requirement: !ruby/object:Gem::Requirement
|
197
|
-
requirements:
|
198
|
-
- - ">="
|
199
|
-
- !ruby/object:Gem::Version
|
200
|
-
version: '0'
|
201
|
-
type: :runtime
|
202
|
-
prerelease: false
|
203
|
-
version_requirements: !ruby/object:Gem::Requirement
|
204
|
-
requirements:
|
205
|
-
- - ">="
|
206
|
-
- !ruby/object:Gem::Version
|
207
|
-
version: '0'
|
208
180
|
- !ruby/object:Gem::Dependency
|
209
181
|
name: tzinfo
|
210
182
|
requirement: !ruby/object:Gem::Requirement
|
@@ -454,6 +426,7 @@ files:
|
|
454
426
|
- dx/start
|
455
427
|
- dx/stop
|
456
428
|
- lib/brut.rb
|
429
|
+
- lib/brut/back_end.rb
|
457
430
|
- lib/brut/back_end/seed_data.rb
|
458
431
|
- lib/brut/back_end/sidekiq.rb
|
459
432
|
- lib/brut/back_end/sidekiq/middlewares.rb
|
@@ -491,7 +464,9 @@ files:
|
|
491
464
|
- lib/brut/framework/mcp.rb
|
492
465
|
- lib/brut/framework/patch_semantic_logger.rb
|
493
466
|
- lib/brut/framework/project_environment.rb
|
467
|
+
- lib/brut/front_end.rb
|
494
468
|
- lib/brut/front_end/asset_metadata.rb
|
469
|
+
- lib/brut/front_end/asset_path_resolver.rb
|
495
470
|
- lib/brut/front_end/component.rb
|
496
471
|
- lib/brut/front_end/components/constraint_violations.rb
|
497
472
|
- lib/brut/front_end/components/form_tag.rb
|
@@ -504,7 +479,7 @@ files:
|
|
504
479
|
- lib/brut/front_end/components/inputs/textarea.rb
|
505
480
|
- lib/brut/front_end/components/locale_detection.rb
|
506
481
|
- lib/brut/front_end/components/page_identifier.rb
|
507
|
-
- lib/brut/front_end/components/
|
482
|
+
- lib/brut/front_end/components/time_tag.rb
|
508
483
|
- lib/brut/front_end/components/traceparent.rb
|
509
484
|
- lib/brut/front_end/download.rb
|
510
485
|
- lib/brut/front_end/flash.rb
|
@@ -527,6 +502,8 @@ files:
|
|
527
502
|
- lib/brut/front_end/handling_results.rb
|
528
503
|
- lib/brut/front_end/http_method.rb
|
529
504
|
- lib/brut/front_end/http_status.rb
|
505
|
+
- lib/brut/front_end/inline_svg_locator.rb
|
506
|
+
- lib/brut/front_end/layout.rb
|
530
507
|
- lib/brut/front_end/layouts/_internal.html.erb
|
531
508
|
- lib/brut/front_end/middleware.rb
|
532
509
|
- lib/brut/front_end/middlewares/annotate_brut_owned_paths.rb
|
@@ -545,15 +522,9 @@ files:
|
|
545
522
|
- lib/brut/front_end/route_hooks/setup_request_context.rb
|
546
523
|
- lib/brut/front_end/routing.rb
|
547
524
|
- lib/brut/front_end/session.rb
|
548
|
-
- lib/brut/front_end/template.rb
|
549
|
-
- lib/brut/front_end/templates/block_filter.rb
|
550
|
-
- lib/brut/front_end/templates/erb_engine.rb
|
551
|
-
- lib/brut/front_end/templates/erb_parser.rb
|
552
|
-
- lib/brut/front_end/templates/escapable_filter.rb
|
553
|
-
- lib/brut/front_end/templates/html_safe_string.rb
|
554
|
-
- lib/brut/front_end/templates/locator.rb
|
555
525
|
- lib/brut/i18n.rb
|
556
526
|
- lib/brut/i18n/base_methods.rb
|
527
|
+
- lib/brut/i18n/for_back_end.rb
|
557
528
|
- lib/brut/i18n/for_cli.rb
|
558
529
|
- lib/brut/i18n/for_html.rb
|
559
530
|
- lib/brut/i18n/http_accept_language.rb
|
@@ -565,6 +536,7 @@ files:
|
|
565
536
|
- lib/brut/spec_support.rb
|
566
537
|
- lib/brut/spec_support/clock_support.rb
|
567
538
|
- lib/brut/spec_support/component_support.rb
|
539
|
+
- lib/brut/spec_support/e2e_support.rb
|
568
540
|
- lib/brut/spec_support/e2e_test_server.rb
|
569
541
|
- lib/brut/spec_support/enhanced_node.rb
|
570
542
|
- lib/brut/spec_support/flash_support.rb
|
@@ -1,47 +0,0 @@
|
|
1
|
-
require "temple"
|
2
|
-
|
3
|
-
# Holds code related to rendering ERB templates
|
4
|
-
module Brut::FrontEnd::Templates
|
5
|
-
autoload(:HTMLSafeString,"brut/front_end/templates/html_safe_string")
|
6
|
-
autoload(:ERBParser,"brut/front_end/templates/erb_parser")
|
7
|
-
autoload(:EscapableFilter,"brut/front_end/templates/escapable_filter")
|
8
|
-
autoload(:BlockFilter,"brut/front_end/templates/block_filter")
|
9
|
-
autoload(:ERBEngine,"brut/front_end/templates/erb_engine")
|
10
|
-
autoload(:Locator,"brut/front_end/templates/locator")
|
11
|
-
end
|
12
|
-
|
13
|
-
# Handles rendering HTML templates written in ERB. This is a light wrapper around `Tilt`.
|
14
|
-
# This also configured a few customizations to allow a Rails-like rendering of ERB:
|
15
|
-
#
|
16
|
-
# * HTML escaping by default
|
17
|
-
# * Helpers that return {Brut::FrontEnd::Templates::HTMLSafeString}s won't be escaped
|
18
|
-
#
|
19
|
-
# @see https://github.com/rtomayko/tilt
|
20
|
-
class Brut::FrontEnd::Template
|
21
|
-
|
22
|
-
# @!visibility private
|
23
|
-
# This sets up global state somewhere, even though we aren't using `TempleTemplate`
|
24
|
-
# anywhere.
|
25
|
-
TempleTemplate = Temple::Templates::Tilt(Brut::FrontEnd::Templates::ERBEngine,
|
26
|
-
register_as: "html.erb")
|
27
|
-
|
28
|
-
attr_reader :template_file_path
|
29
|
-
|
30
|
-
# Wraps a string that is deemed safe to insert into
|
31
|
-
# HTML without escaping it. This allows stuff like
|
32
|
-
# <%= component(SomeComponent) %> to work without
|
33
|
-
# having to remember to <%== all the time.
|
34
|
-
def initialize(template_file_path)
|
35
|
-
@template_file_path = template_file_path
|
36
|
-
@tilt_template = Tilt.new(@template_file_path)
|
37
|
-
end
|
38
|
-
|
39
|
-
def render_template(...)
|
40
|
-
@tilt_template.render(...)
|
41
|
-
end
|
42
|
-
|
43
|
-
# Convienience method to escape HTML in the canonical way.
|
44
|
-
def self.escape_html(string)
|
45
|
-
Brut::FrontEnd::Templates::EscapableFilter.escape_html(string)
|
46
|
-
end
|
47
|
-
end
|
@@ -1,61 +0,0 @@
|
|
1
|
-
# Allows rendering blocks in ERB the way Rails' helpers like `form_with` do.
|
2
|
-
# This is a slightly modified copy of Hanami's `Filters::Block`.
|
3
|
-
#
|
4
|
-
# @see https://github.com/hanami/view/blob/main/lib/hanami/view/erb/filters/block.rb
|
5
|
-
class Brut::FrontEnd::Templates::BlockFilter < Temple::Filter
|
6
|
-
END_LINE_RE = /\bend\b/
|
7
|
-
|
8
|
-
# @!visibility private
|
9
|
-
def on_erb_block(escape, code, content)
|
10
|
-
tmp = unique_name
|
11
|
-
|
12
|
-
# Remove the last `end` :code sexp, since this is technically "outside" the block
|
13
|
-
# contents, which we want to capture separately below. This `end` is added back after
|
14
|
-
# capturing the content below.
|
15
|
-
case content.last
|
16
|
-
in [:code, c] if c =~ END_LINE_RE
|
17
|
-
content.pop
|
18
|
-
end
|
19
|
-
|
20
|
-
[:multi,
|
21
|
-
# Capture the result of the code in a variable. We can't do `[:dynamic, code]` because
|
22
|
-
# it's probably not a complete expression (which is a requirement for Temple).
|
23
|
-
# DBC: an example is that 'code' might be "form_for do" which is not an expression.
|
24
|
-
# Because we later put an "end" in, the result will be
|
25
|
-
#
|
26
|
-
# some_var = helper do
|
27
|
-
# end
|
28
|
-
#
|
29
|
-
# Which IS valid Ruby.
|
30
|
-
[:code, "#{tmp} = #{code}"],
|
31
|
-
# Capture the content of a block in a separate buffer. This means that `yield` will
|
32
|
-
# not output the content to the current buffer, but rather return the output.
|
33
|
-
[:capture, unique_name, compile(content)],
|
34
|
-
[:code, "end"],
|
35
|
-
# Output the content, without escaping it.
|
36
|
-
# Hanami has this ↴
|
37
|
-
# [:escape, escape, [:dynamic, tmp]]
|
38
|
-
[:escape, escape, [:dynamic, Brut::FrontEnd::Templates.name + "::HTMLSafeString.new(#{tmp})"]]
|
39
|
-
]
|
40
|
-
|
41
|
-
# Details explaining the change:
|
42
|
-
#
|
43
|
-
# The sexps for template are quite convoluted and highly dynamic, so it is hard
|
44
|
-
# to understand exactly what effect they will have. Basically, what this [:multi thing is
|
45
|
-
# doing is to capture the result of the block in a variable:
|
46
|
-
#
|
47
|
-
# some_var = form_for(args) do
|
48
|
-
#
|
49
|
-
# It then captures the inside of the block in a new variable:
|
50
|
-
#
|
51
|
-
# some_other_var = «whatever was inside that `do`»
|
52
|
-
#
|
53
|
-
# And follows it with an end.
|
54
|
-
#
|
55
|
-
# The first variable—some_var—now holds the return value of the helper, form_for in this case. To
|
56
|
-
# output this content to the actual view, it must be dereferenced, thus [ :dynamic, "some_var" ].
|
57
|
-
#
|
58
|
-
# We are going to treat the return value of the block helper as HTML safe. Thus, we'll wrap it
|
59
|
-
# with HTMLSafeString.new(…).
|
60
|
-
end
|
61
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
# A temple "engine" that can be used to parse ERB and generate HTML
|
2
|
-
# in just the way we need.
|
3
|
-
class Brut::FrontEnd::Templates::ERBEngine < Temple::Engine
|
4
|
-
# Parse the ERB into sexps
|
5
|
-
use Brut::FrontEnd::Templates::ERBParser
|
6
|
-
|
7
|
-
# Handle block syntax used in a <%=
|
8
|
-
use Brut::FrontEnd::Templates::BlockFilter
|
9
|
-
|
10
|
-
# Trim whitespace like ERB does
|
11
|
-
use Temple::ERB::Trimming
|
12
|
-
|
13
|
-
# Escape strings only if they are not HTMLSafeString
|
14
|
-
use Brut::FrontEnd::Templates::EscapableFilter
|
15
|
-
# This filter actually runs the Ruby code
|
16
|
-
use Temple::Filters::StaticAnalyzer
|
17
|
-
# Flattens nested :multi expressions which I'm not sure is needed, but
|
18
|
-
# have cargo-culted from hanami
|
19
|
-
use Temple::Filters::MultiFlattener
|
20
|
-
# merges sequential :static, which again, not sure is needed, but
|
21
|
-
# have cargo-culted from hanami
|
22
|
-
use Temple::Filters::StaticMerger
|
23
|
-
|
24
|
-
# This generates everything into a string
|
25
|
-
use Temple::Generators::ArrayBuffer
|
26
|
-
end
|