bridgetown-foundation 2.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
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: []