Skip to content

Commit

Permalink
Add more functionality to Foundation
Browse files Browse the repository at this point in the history
- translation safety
- module nesting
- also clean up outdated subclass tracking for route class files
  • Loading branch information
jaredcwhite committed Apr 16, 2024
1 parent bef4af8 commit adc2c88
Show file tree
Hide file tree
Showing 11 changed files with 116 additions and 28 deletions.
9 changes: 8 additions & 1 deletion bridgetown-core/lib/bridgetown-core/helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,17 @@ def translate(key, **options) # rubocop:disable Metrics/CyclomaticComplexity, Me
key = "#{view_path.tr("/", ".")}#{key}" if view_path.present?
end

ActiveSupport::HtmlSafeTranslation.translate(key, **options)
return I18n.translate(key, **options) unless %r{(?:_|\b)html\z}.match?(key)

translate_with_html(key, **options)
end
alias_method :t, :translate

def translate_with_html(key, **options)
escaper = ->(input) { input.to_s.encode(xml: :attr).gsub(%r{\A"|"\Z}, "") }
Bridgetown::Foundation::SafeTranslations.translate(key, escaper, **options)
end

# Delegates to <tt>I18n.localize</tt> with no additional functionality.
#
# @return [String] the localized string
Expand Down
2 changes: 0 additions & 2 deletions bridgetown-core/lib/bridgetown-core/rack/boot.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ def self.autoload_server_folder( # rubocop:todo Metrics

loader.reload
loader.eager_load
Bridgetown::Rack::Routes.reload_subclasses
rescue SyntaxError => e
Bridgetown::Errors.print_build_error(e)
end.start
Expand All @@ -78,7 +77,6 @@ def self.autoload_server_folder( # rubocop:todo Metrics
next unless load_path == server_folder

loader.eager_load
Bridgetown::Rack::Routes.reload_subclasses
end

loaders_manager.setup_loaders([server_folder])
Expand Down
26 changes: 1 addition & 25 deletions bridgetown-core/lib/bridgetown-core/rack/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,6 @@ def print_routes
end
# rubocop:enable Bridgetown/NoPutsAllowed, Metrics/MethodLength

# @return [Hash<String, Class(Routes)>]
attr_accessor :tracked_subclasses

# @return [Proc]
attr_accessor :router_block

Expand All @@ -59,30 +56,9 @@ def <=>(other)
"#{priorities[priority]}#{self}" <=> "#{priorities[other.priority]}#{other}"
end

# @param base [Class(Routes)]
def inherited(base)
Bridgetown::Rack::Routes.track_subclass base
super
end

# @param klass [Class(Routes)]
def track_subclass(klass)
Bridgetown::Rack::Routes.tracked_subclasses ||= {}
Bridgetown::Rack::Routes.tracked_subclasses[klass.name] = klass
end

# @return [Array<Class(Routes)>]
def sorted_subclasses
Bridgetown::Rack::Routes.tracked_subclasses&.values&.sort
end

# @return [void]
def reload_subclasses
Bridgetown::Rack::Routes.tracked_subclasses&.each_key do |klassname|
Kernel.const_get(klassname)
rescue NameError
Bridgetown::Rack::Routes.tracked_subclasses.delete klassname
end
Bridgetown::Rack::Routes.descendants.sort
end

# Add a router block via the current Routes class
Expand Down
1 change: 1 addition & 0 deletions bridgetown-core/test/source/src/_locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ en:
foo: foo
bar: bar
foo_html: <strong>foo</strong>
dangerous_html: <button>Click %{me}</button>
contacts:
bar:
foo: foo
5 changes: 5 additions & 0 deletions bridgetown-core/test/test_ruby_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ def setup
assert @helpers.translate("about.foo_html").html_safe?
end

should "return escaped interpolated values within html safe translation" do
assert_equal "<button>Click &lt;span&gt;Me&lt;/span&gt;</button>",
@helpers.translate("about.dangerous_html", me: "<span>Me</span>")
end

should "not return html safe string when key does not end with _html" do
refute @helpers.translate("about.foo").html_safe?
end
Expand Down
1 change: 1 addition & 0 deletions bridgetown-foundation/.rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ inherit_from: ../.rubocop.yml
AllCops:
Exclude:
- "*.gemspec"
- "script/console"
32 changes: 32 additions & 0 deletions bridgetown-foundation/lib/bridgetown/foundation/core_ext/module.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# frozen_string_literal: true

module Bridgetown::Foundation
module CoreExt
module Module
module Nested
def nested_within?(other)
other.nested_parents.within?(nested_parents[1..])
end

def nested_parents
return [] unless name

nesting_segments = name.split("::")[...-1]
nesting_segments.map.each_with_index do |_nesting_name, index|
Kernel.const_get(nesting_segments[..-(index + 1)].join("::"))
end
end

def nested_parent
nested_parents.first
end

def nested_name
name&.split("::")&.last
end
end

::Module.include Nested
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# frozen_string_literal: true

module Bridgetown
module Foundation
# NOTE: this is tested by `test/test_ruby_helpers.rb` in bridgetown-core
#
# This is loosely based on the HtmlSafeTranslation module from ActiveSupport, but you can
# actually use it for any kind of safety use case in a translation setting because its
# decoupled from any specific escaping or safety mechanisms.
module SafeTranslations
def self.translate(key, escaper, safety_method = :html_safe, **options)
safe_options = escape_translation_options(options, escaper)

i18n_error = false

exception_handler = ->(*args) do
i18n_error = true
I18n.exception_handler.(*args)
end

I18n.translate(key, **safe_options, exception_handler:).then do |translation|
i18n_error ? translation : safe_translation(translation, safety_method)
end
end

def self.escape_translation_options(options, escaper)
@reserved_i18n_keys ||= I18n::RESERVED_KEYS.to_set

options.to_h do |name, value|
unless @reserved_i18n_keys.include?(name) || (name == :count && value.is_a?(Numeric))
next [name, escaper.(value)]
end

[name, value]
end
end

def self.safe_translation(translation, safety_method)
@safe_value ||= -> { _1.respond_to?(safety_method) ? _1.send(safety_method) : _1 }

return translation.map { @safe_value.(_1) } if translation.respond_to?(:map)

@safe_value.(translation)
end
end
end
end
12 changes: 12 additions & 0 deletions bridgetown-foundation/script/console
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/env ruby

require "bundler"
Bundler.setup

require "bridgetown-foundation"

module Bridgetown
module Foundation
binding.irb
end
end
1 change: 1 addition & 0 deletions bridgetown-website/Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
gem "bridgetown", path: "../bridgetown"
gem "bridgetown-builder", path: "../bridgetown-builder"
gem "bridgetown-core", path: "../bridgetown-core"
gem "bridgetown-foundation", path: "../bridgetown-foundation"
gem "bridgetown-paginate", path: "../bridgetown-paginate"

gem "puma", "< 7"
Expand Down
8 changes: 8 additions & 0 deletions bridgetown-website/Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ PATH
activesupport (>= 6.0, < 8.0)
addressable (~> 2.4)
amazing_print (~> 1.2)
bridgetown-foundation (= 1.3.4)
colorator (~> 1.0)
csv (~> 3.2)
erubi (~> 1.9)
Expand All @@ -34,6 +35,12 @@ PATH
tilt (~> 2.0)
zeitwerk (~> 2.5)

PATH
remote: ../bridgetown-foundation
specs:
bridgetown-foundation (1.3.4)
zeitwerk (~> 2.5)

PATH
remote: ../bridgetown-paginate
specs:
Expand Down Expand Up @@ -147,6 +154,7 @@ DEPENDENCIES
bridgetown-builder!
bridgetown-core!
bridgetown-feed (~> 3)
bridgetown-foundation!
bridgetown-paginate!
bridgetown-quick-search (~> 2.0)
bridgetown-seo-tag (~> 6.0)
Expand Down

0 comments on commit adc2c88

Please sign in to comment.