bridgetown-foundation 2.0.0.beta1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 00a2692340742728b14ce61beb79b3181409376be73f56c21a60db4e3d28c3ee
4
+ data.tar.gz: fc2ae32d07c3f218d98ed6409abab7431e6927078005c6dc4dbd158d054ce4c5
5
+ SHA512:
6
+ metadata.gz: 82722f2c1d63bbc05f2d8de93290999316162c0de9f55d91c24fbe831ee67a7b0335c1e3c91846038518b7d944e413c6b1be795fcead56e53815b068c2c2de72
7
+ data.tar.gz: de9adb2dc49ed52b36fe7f61e7f10f29e30b8b12fced50cd8e2cc678b563f11001c79d038fead8a786898963c8be45352b85207a14aa65be21423abeecc756f6
data/.rubocop.yml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ inherit_from: ../.rubocop.yml
3
+
4
+ AllCops:
5
+ Exclude:
6
+ - "*.gemspec"
7
+ - "script/console"
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+
5
+ task spec: :test
6
+ require "rake/testtask"
7
+ Rake::TestTask.new(:test) do |test|
8
+ test.libs << "lib" << "test"
9
+ test.pattern = "test/**/test_*.rb"
10
+ test.verbose = true
11
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "benchmark"
4
+
5
+ class TestInclusion
6
+ module StringAdder
7
+ def included_add(num)
8
+ to_i + num
9
+ end
10
+ end
11
+
12
+ String.include StringAdder
13
+
14
+ def self.perform
15
+ raise "crash!" unless "10".included_add(20) == 30
16
+ end
17
+ end
18
+
19
+ module StringAdderRefinement
20
+ refine String do
21
+ def refined_add(num)
22
+ to_i + num
23
+ end
24
+ end
25
+ end
26
+
27
+ class TestRefinement
28
+ using StringAdderRefinement
29
+
30
+ def self.perform
31
+ raise "crash!" unless "10".refined_add(20) == 30
32
+ end
33
+ end
34
+
35
+ raise "Unconfirmed!" unless "".respond_to?(:included_add)
36
+ raise "Unconfirmed!" if "".respond_to?(:refined_add)
37
+
38
+ n = 1_000_000
39
+ Benchmark.bmbm(12) do |x|
40
+ x.report("inclusion:") do
41
+ n.times do
42
+ TestInclusion.perform
43
+ end
44
+ end
45
+ x.report("refinements:") do
46
+ n.times do
47
+ TestRefinement.perform
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/bridgetown/foundation/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "bridgetown-foundation"
7
+ spec.version = Bridgetown::Foundation::VERSION
8
+ spec.author = "Bridgetown Team"
9
+ spec.email = "maintainers@bridgetownrb.com"
10
+ spec.summary = "Ruby language extensions and other utilities useful for the Bridgetown ecosystem"
11
+ spec.homepage = "https://github.com/bridgetownrb/bridgetown/tree/main/bridgetown-foundation"
12
+ spec.license = "MIT"
13
+ spec.required_ruby_version = ">= 3.1"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r!^(test|script)/!) }
16
+ spec.require_paths = ["lib"]
17
+
18
+ spec.metadata = {
19
+ "source_code_uri" => "https://github.com/bridgetownrb/bridgetown",
20
+ "bug_tracker_uri" => "https://github.com/bridgetownrb/bridgetown/issues",
21
+ "changelog_uri" => "https://github.com/bridgetownrb/bridgetown/releases",
22
+ "homepage_uri" => spec.homepage,
23
+ "rubygems_mfa_required" => "true",
24
+ }
25
+
26
+ spec.add_dependency("hash_with_dot_access", "~> 2.0")
27
+ spec.add_dependency("inclusive", "~> 1.0")
28
+ spec.add_dependency("zeitwerk", "~> 2.5")
29
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown::Foundation
4
+ module CoreExt
5
+ module Class
6
+ module Descendants
7
+ def descendants
8
+ direct_children = subclasses.select do |klass|
9
+ next true if klass.name.nil? # anonymous class
10
+
11
+ # We do this to weed out old classes pre-Zeitwerk reload
12
+ klass == Kernel.const_get(klass.name)
13
+ rescue NameError
14
+ nil
15
+ end
16
+
17
+ (direct_children + direct_children.map(&:descendants)).flatten
18
+ end
19
+ end
20
+
21
+ ::Class.include Descendants
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown::Foundation
4
+ module CoreExt
5
+ module String
6
+ module Colorize
7
+ class << self
8
+ extend Inclusive::Class
9
+
10
+ # @return [Bridgetown::Foundation::Packages::Ansi]
11
+ public_packages def ansi = [Bridgetown::Foundation::Packages::Ansi]
12
+
13
+ def included(klass)
14
+ ansi.tap do |a|
15
+ a.colors.each_key do |color|
16
+ klass.define_method(color) { |*args| a.public_send(color, self, *args) }
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ # Reset output colors back to a regular string output
23
+ def reset_ansi
24
+ Colorize.ansi.reset(self)
25
+ end
26
+ end
27
+
28
+ module StartsWithAndEndsWith
29
+ def self.included(klass)
30
+ klass.alias_method :starts_with?, :start_with?
31
+ klass.alias_method :ends_with?, :end_with?
32
+ end
33
+ end
34
+
35
+ ::String.include Colorize, StartsWithAndEndsWith
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown::Foundation
4
+ module Packages
5
+ module Ansi
6
+ extend self
7
+
8
+ ESCAPE = format("%c", 27)
9
+ MATCH = %r!#{ESCAPE}\[(?:\d+)(?:;\d+)*(j|k|m|s|u|A|B|G)|\e\(B\e\[m!ix
10
+ COLORS = {
11
+ bold: 1,
12
+ black: 30,
13
+ red: 31,
14
+ green: 32,
15
+ yellow: 33,
16
+ blue: 34,
17
+ magenta: 35,
18
+ cyan: 36,
19
+ white: 37,
20
+ }.freeze
21
+
22
+ def colors = COLORS
23
+
24
+ # Strip ANSI from the current string. It also strips cursor stuff,
25
+ # well some of it, and it also strips some other stuff that a lot of
26
+ # the other ANSI strippers don't.
27
+ def strip(str)
28
+ str.gsub MATCH, ""
29
+ end
30
+
31
+ # Does the string include ANSI color codes?
32
+ def has?(str)
33
+ !!(str =~ MATCH)
34
+ end
35
+
36
+ # Reset the color back to the default color so that you do not leak any
37
+ # colors when you move onto the next line. This is probably normally
38
+ # used as part of a wrapper so that we don't leak colors.
39
+ def reset(str = "")
40
+ @ansi_reset ||= format("%c[0m", 27)
41
+ "#{@ansi_reset}#{str}"
42
+ end
43
+
44
+ # SEE: `self::COLORS` for a list of methods. They are mostly
45
+ # standard base colors supported by pretty much any xterm-color, we do
46
+ # not need more than the base colors so we do not include them.
47
+ COLORS.each do |color, num|
48
+ define_method color do |str|
49
+ "#{format("%c", 27)}[#{num}m#{str}#{reset}"
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown::Foundation
4
+ module Packages
5
+ module PidTracker
6
+ def create_pid_dir
7
+ FileUtils.mkdir_p pids_dir
8
+ end
9
+
10
+ def add_pid(pid, file:)
11
+ File.write pidfile_for(file), "#{pid}\n", mode: "a+"
12
+ end
13
+
14
+ def read_pidfile(file)
15
+ File.readlines pidfile_for(file), chomp: true
16
+ end
17
+
18
+ def remove_pidfile(file)
19
+ File.delete pidfile_for(file)
20
+ end
21
+
22
+ private
23
+
24
+ def root_dir
25
+ Dir.pwd
26
+ end
27
+
28
+ def pids_dir
29
+ File.join(root_dir, "tmp", "pids")
30
+ end
31
+
32
+ def pidfile_for(file)
33
+ File.join(pids_dir, "#{file}.pid")
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown::Foundation
4
+ module Packages
5
+ # NOTE: this is tested by `test/test_ruby_helpers.rb` in bridgetown-core
6
+ #
7
+ # This is loosely based on the HtmlSafeTranslation module from ActiveSupport, but you can
8
+ # actually use it for any kind of safety use case in a translation setting because its
9
+ # decoupled from any specific escaping or safety mechanisms.
10
+ module SafeTranslations
11
+ extend Inclusive::Public
12
+
13
+ def translate(key, escaper, safety_method = :html_safe, **options)
14
+ safe_options = escape_translation_options(options, escaper)
15
+
16
+ i18n_error = false
17
+
18
+ exception_handler = ->(*args) do
19
+ i18n_error = true
20
+ I18n.exception_handler.(*args)
21
+ end
22
+
23
+ I18n.translate(key, **safe_options, exception_handler:).then do |translation|
24
+ i18n_error ? translation : safe_translation(translation, safety_method)
25
+ end
26
+ end
27
+
28
+ public_function :translate
29
+
30
+ def escape_translation_options(options, escaper)
31
+ @reserved_i18n_keys ||= I18n::RESERVED_KEYS.to_set
32
+
33
+ options.to_h do |name, value|
34
+ unless @reserved_i18n_keys.include?(name) || (name == :count && value.is_a?(Numeric))
35
+ next [name, escaper.(value)]
36
+ end
37
+
38
+ [name, value]
39
+ end
40
+ end
41
+
42
+ def safe_translation(translation, safety_method)
43
+ @safe_value ||= -> { _1.respond_to?(safety_method) ? _1.send(safety_method) : _1 }
44
+
45
+ return translation.map { @safe_value.(_1) } if translation.respond_to?(:map)
46
+
47
+ @safe_value.(translation)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown::Foundation
4
+ class QuestionableString < ::String
5
+ def method_missing(method_name, *args)
6
+ value = method_name.to_s
7
+ if value.end_with?("?")
8
+ value.chop!
9
+ return self == value
10
+ end
11
+
12
+ super
13
+ end
14
+
15
+ def respond_to_missing?(method_name, include_private = false)
16
+ method_name.end_with?("?") || super
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown::Foundation
4
+ module RefineExt
5
+ # This is a very simplistic algorithm…it essentially just works on the Array/Hash values level,
6
+ # which for our purposes is fine.
7
+ module DeepDuplicatable
8
+ refine ::Hash do
9
+ def deep_dup
10
+ hash = dup
11
+ each do |key, value|
12
+ hash.delete(key)
13
+ if key.is_a?(::String) || key.is_a?(::Symbol)
14
+ hash[key] = value.dup
15
+ else
16
+ hash[key.dup] = if value.is_a?(Array) || value.is_a?(Hash)
17
+ value.deep_dup
18
+ else
19
+ value.dup
20
+ end
21
+ end
22
+ end
23
+ hash
24
+ end
25
+ end
26
+
27
+ refine ::Array do
28
+ def deep_dep
29
+ map do |item|
30
+ next item.dup unless item.is_a?(Array) || item.is_a?(Hash)
31
+
32
+ item.deep_dup
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ module Bridgetown
41
+ module Refinements
42
+ include Foundation::RefineExt::DeepDuplicatable
43
+ end
44
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown::Foundation
4
+ module RefineExt
5
+ module Module
6
+ using RefineExt::Object
7
+
8
+ refine ::Module do
9
+ def nested_within?(other)
10
+ return false if self == other
11
+
12
+ other.nested_parents.within?(nested_parents) #[1..])
13
+ end
14
+
15
+ def nested_parents
16
+ return [] unless name
17
+
18
+ nesting_segments = name.split("::")[...-1]
19
+ nesting_segments.map.each_with_index do |_nesting_name, index|
20
+ Kernel.const_get(nesting_segments[..-(index + 1)].join("::"))
21
+ end
22
+ end
23
+
24
+ def nested_parent
25
+ nested_parents.first
26
+ end
27
+
28
+ def nested_name
29
+ name&.split("::")&.last
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ module Bridgetown
37
+ module Refinements
38
+ include Foundation::RefineExt::Module
39
+ end
40
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown::Foundation
4
+ module RefineExt
5
+ module Object
6
+ refine ::Object do
7
+ # This method lets you check if the receiver is "within" the other object. In most cases,
8
+ # this check is accomplished via the `include?` method…aka, `10.within? [5, 10]` would
9
+ # return `true` as `[5, 10].include? 10` is true. And String/String comparison are
10
+ # case-insensivitve.
11
+ #
12
+ # However, for certain comparison types: Module/Class, Hash, and Set, the lesser-than (`<`)
13
+ # operator is used instead. This is so you can check `BigDecimal.within? Numeric`,
14
+ # `{easy_as: 123}.within?({indeed: "it's true", easy_as: 123})`, and if a Set is a
15
+ # `proper_subset?` of another Set.
16
+ #
17
+ # For Array/Array comparisons, a difference is checked, so `[1,2].within? [3,2,1]` is true,
18
+ # but `[1,2].within? [2,3]` is false.
19
+ #
20
+ # Also for Range, the `cover?` method is used instead of `include?`.
21
+ #
22
+ # @param other [Object] for determining if receiver lies within this value
23
+ # @return [Boolean]
24
+ def within?(other) # rubocop:disable Metrics
25
+ # rubocop:disable Style/IfUnlessModifier
26
+ if is_a?(Module) && other.is_a?(Module)
27
+ return self < other
28
+ end
29
+
30
+ if (is_a?(Hash) && other.is_a?(Hash)) || (is_a?(Set) && other.is_a?(Set))
31
+ return self < other
32
+ end
33
+
34
+ if is_a?(Array) && other.is_a?(Array)
35
+ return false if empty?
36
+
37
+ return difference(other).empty?
38
+ end
39
+
40
+ if other.is_a?(Range)
41
+ return other.cover?(self) == true
42
+ end
43
+
44
+ if is_a?(::String) && other.is_a?(::String)
45
+ return other.downcase.include?(downcase)
46
+ end
47
+
48
+ other&.include?(self) == true
49
+ # rubocop:enable Style/IfUnlessModifier
50
+ rescue NoMethodError
51
+ false
52
+ end
53
+
54
+ # NOTE: if you _really_ need to preserve Active Support's `in?` functionality, you can just
55
+ # require "active_support/core_ext/object/inclusion"
56
+ def in?(...) = Bridgetown::Foundation.deprecation_warning(
57
+ self, :in?, :within?, 2024, 12
58
+ ).then { within?(...) }
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ module Bridgetown
65
+ module Refinements
66
+ include Foundation::RefineExt::Object
67
+ end
68
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown::Foundation
4
+ module RefineExt
5
+ module String
6
+ refine ::String do
7
+ def indent!(indent_by, *args)
8
+ if args.length.positive?
9
+ Kernel.warn "multiple arguments aren't supported by `indent!' in Bridgetown", uplevel: 1
10
+ end
11
+
12
+ gsub! %r!^(?\!$)!, " " * indent_by
13
+ end
14
+
15
+ def indent(indent_by, *args)
16
+ if args.length.positive?
17
+ Kernel.warn "multiple arguments aren't supported by `indent' in Bridgetown", uplevel: 1
18
+ end
19
+
20
+ dup.indent!(indent_by)
21
+ end
22
+
23
+ def questionable = Bridgetown::Foundation::QuestionableString.new(self)
24
+
25
+ def inquiry = Bridgetown::Foundation.deprecation_warning(
26
+ self, :inquiry, :questionable, 2024, 12
27
+ ).then { questionable }
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ module Bridgetown
34
+ module Refinements
35
+ include Foundation::RefineExt::String
36
+ end
37
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Foundation
5
+ # TODO: should we define versions here now and pull it within Core?
6
+ VERSION = "2.0.0.beta1"
7
+ end
8
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bridgetown/foundation/version"
4
+ require "hash_with_dot_access"
5
+ require "inclusive"
6
+ require "zeitwerk"
7
+ require "delegate"
8
+
9
+ module Bridgetown::Foundation
10
+ # This is loosly based on the `deprecate` method in `Gem::Deprecate`
11
+ #
12
+ # @param target [Object]
13
+ # @param name [Symbol] e.g. `:howdy`
14
+ # @param repl [Symbol] e.g. `:hello`
15
+ # @param year [Integer] e.g. `2025`
16
+ # @param month [Integer] e.g. `1` for January
17
+ def self.deprecation_warning(target, name, repl, year, month) # rubocop:disable Metrics/ParameterLists
18
+ klass = target.is_a?(Module)
19
+ target = klass ? "#{self}." : "#{self.class}#"
20
+ msg = [
21
+ "NOTE: #{target}#{name} is deprecated",
22
+ repl == :none ? " with no replacement" : "; use #{repl} instead",
23
+ format(". It will be removed on or after %4d-%02d.", year, month), # rubocop:disable Style/FormatStringToken
24
+ "\n#{target}#{name} called from #{Gem.location_of_caller.join(":")}",
25
+ ]
26
+ warn "#{msg.join}."
27
+ end
28
+ end
29
+
30
+ # You can add `using Bridgetown::Refinements` to any portion of your Ruby code to load in all
31
+ # of the refinements available in Foundation. Or you can add a using statement for a particular
32
+ # refinement which lives inside `Bridgetown::Foundation::RefineExt`.
33
+ module Bridgetown::Refinements
34
+ include HashWithDotAccess::Refinements
35
+ end
36
+
37
+ Zeitwerk.with_loader do |l|
38
+ l.push_dir "#{__dir__}/bridgetown/foundation", namespace: Bridgetown::Foundation
39
+ l.ignore "#{__dir__}/bridgetown/foundation/version.rb"
40
+ l.setup
41
+ l.eager_load
42
+ end
43
+
44
+ module Bridgetown
45
+ # Any method call sent will be passed along to the wrapped object with refinements activated
46
+ class WrappedObjectWithRefinements < SimpleDelegator
47
+ using Bridgetown::Refinements
48
+
49
+ # rubocop:disable Style/MissingRespondToMissing
50
+ def method_missing(method, ...) = __getobj__.send(method, ...)
51
+ # rubocop:enable Style/MissingRespondToMissing
52
+ end
53
+
54
+ # Call this method to wrap any object(s) in order to use Foundation's refinements
55
+ #
56
+ # @param *obj [Object]
57
+ # @return [WrappedObjectWithRefinements]
58
+ def self.refine(*obj)
59
+ if obj.length == 1
60
+ WrappedObjectWithRefinements.new(obj[0])
61
+ else
62
+ obj.map { WrappedObjectWithRefinements.new _1 }
63
+ end
64
+ end
65
+
66
+ def self.add_refinement(mod, &)
67
+ Bridgetown::Refinements.include(mod)
68
+ Bridgetown::WrappedObjectWithRefinements.class_eval(&)
69
+ end
70
+ end
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bridgetown-foundation
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.0.beta1
5
+ platform: ruby
6
+ authors:
7
+ - Bridgetown Team
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-08-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: hash_with_dot_access
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: inclusive
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: zeitwerk
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.5'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.5'
55
+ description:
56
+ email: maintainers@bridgetownrb.com
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - ".rubocop.yml"
62
+ - Rakefile
63
+ - benchmark/refinements.rb
64
+ - bridgetown-foundation.gemspec
65
+ - lib/bridgetown-foundation.rb
66
+ - lib/bridgetown/foundation/core_ext/class.rb
67
+ - lib/bridgetown/foundation/core_ext/string.rb
68
+ - lib/bridgetown/foundation/packages/ansi.rb
69
+ - lib/bridgetown/foundation/packages/pid_tracker.rb
70
+ - lib/bridgetown/foundation/packages/safe_translations.rb
71
+ - lib/bridgetown/foundation/questionable_string.rb
72
+ - lib/bridgetown/foundation/refine_ext/deep_duplicatable.rb
73
+ - lib/bridgetown/foundation/refine_ext/module.rb
74
+ - lib/bridgetown/foundation/refine_ext/object.rb
75
+ - lib/bridgetown/foundation/refine_ext/string.rb
76
+ - lib/bridgetown/foundation/version.rb
77
+ homepage: https://github.com/bridgetownrb/bridgetown/tree/main/bridgetown-foundation
78
+ licenses:
79
+ - MIT
80
+ metadata:
81
+ source_code_uri: https://github.com/bridgetownrb/bridgetown
82
+ bug_tracker_uri: https://github.com/bridgetownrb/bridgetown/issues
83
+ changelog_uri: https://github.com/bridgetownrb/bridgetown/releases
84
+ homepage_uri: https://github.com/bridgetownrb/bridgetown/tree/main/bridgetown-foundation
85
+ rubygems_mfa_required: 'true'
86
+ post_install_message:
87
+ rdoc_options: []
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '3.1'
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">"
98
+ - !ruby/object:Gem::Version
99
+ version: 1.3.1
100
+ requirements: []
101
+ rubygems_version: 3.3.26
102
+ signing_key:
103
+ specification_version: 4
104
+ summary: Ruby language extensions and other utilities useful for the Bridgetown ecosystem
105
+ test_files: []