diff --git a/lib/tapioca/dsl/compilers/active_support_environment_inquirer.rb b/lib/tapioca/dsl/compilers/active_support_environment_inquirer.rb new file mode 100644 index 000000000..d184e7006 --- /dev/null +++ b/lib/tapioca/dsl/compilers/active_support_environment_inquirer.rb @@ -0,0 +1,64 @@ +# typed: strict +# frozen_string_literal: true + +return unless defined?(ActiveSupport::EnvironmentInquirer) + +module Tapioca + module Dsl + module Compilers + # `Tapioca::Dsl::Compilers::ActiveSupportEnvironmentInquirer` decorates an RBI file for non-default environment + # files in the `config/environments` directory. + # + # For example, in a Rails application with the following files: + # + # - config/environments/development.rb + # - config/environments/demo.rb + # - config/environments/production.rb + # - config/environments/staging.rb + # - config/environments/test.rb + # + # this compiler will produce an RBI file with the following content: + # ~~~rbi + # # typed: true + # + # class ActiveSupport::EnvironmentInquirer + # sig { returns(T::Boolean) } + # def demo?; end + # + # sig { returns(T::Boolean) } + # def staging?; end + # end + # ~~~ + #: [ConstantType = singleton(::ActiveSupport::EnvironmentInquirer)] + class ActiveSupportEnvironmentInquirer < Compiler + extend T::Sig + + # @override + #: -> void + def decorate + envs = Rails.root.glob("config/environments/*.rb").map { |f| f.basename(".rb").to_s }.sort + envs -= ::ActiveSupport::EnvironmentInquirer::DEFAULT_ENVIRONMENTS + return if envs.none? + + root.create_path(::ActiveSupport::EnvironmentInquirer) do |mod| + envs.each do |env| + mod.create_method("#{env}?", return_type: "T::Boolean") + end + end + end + + class << self + extend T::Sig + + # @override + #: -> T::Enumerable[T::Module[top]] + def gather_constants + return [] unless defined?(Rails.application) && Rails.application + + [::ActiveSupport::EnvironmentInquirer] + end + end + end + end + end +end diff --git a/manual/compiler_activesupportenvironmentinquirer.md b/manual/compiler_activesupportenvironmentinquirer.md new file mode 100644 index 000000000..ab1382a36 --- /dev/null +++ b/manual/compiler_activesupportenvironmentinquirer.md @@ -0,0 +1,25 @@ +## ActiveSupportEnvironmentInquirer + +`Tapioca::Dsl::Compilers::ActiveSupportEnvironmentInquirer` decorates an RBI file for non-default environment +files in the `config/environments` directory. + +For example, in a Rails application with the following files: + +- config/environments/development.rb +- config/environments/demo.rb +- config/environments/production.rb +- config/environments/staging.rb +- config/environments/test.rb + +this compiler will produce an RBI file with the following content: +~~~rbi +# typed: true + +class ActiveSupport::EnvironmentInquirer + sig { returns(T::Boolean) } + def demo?; end + + sig { returns(T::Boolean) } + def staging?; end +end +~~~ diff --git a/manual/compilers.md b/manual/compilers.md index 2f2960b1b..74f68c0c5 100644 --- a/manual/compilers.md +++ b/manual/compilers.md @@ -25,6 +25,7 @@ In the following section you will find all available DSL compilers: * [ActiveStorage](compiler_activestorage.md) * [ActiveSupportConcern](compiler_activesupportconcern.md) * [ActiveSupportCurrentAttributes](compiler_activesupportcurrentattributes.md) +* [ActiveSupportEnvironmentInquirer](compiler_activesupportenvironmentinquirer.md) * [ActiveSupportTimeExt](compiler_activesupporttimeext.md) * [Config](compiler_config.md) * [FrozenRecord](compiler_frozenrecord.md) diff --git a/spec/rails_spec_helper.rb b/spec/rails_spec_helper.rb index 82bdb0fa4..992b2d329 100644 --- a/spec/rails_spec_helper.rb +++ b/spec/rails_spec_helper.rb @@ -6,6 +6,15 @@ module RailsSpecHelper extend Tapioca::Helpers::Test::Content class << self + #: (String path) -> void + def define_fake_rails_app(path) + base_folder = Pathname.new(path) + config_class = Struct.new(:root) + config = config_class.new(base_folder) + app_class = Struct.new(:config) + Rails.application = app_class.new(config) + end + def load_active_storage add_ruby_file("application.rb", <<~RUBY) ENV["DATABASE_URL"] = "sqlite3::memory:" diff --git a/spec/tapioca/cli/dsl_spec.rb b/spec/tapioca/cli/dsl_spec.rb index a019ec30f..909a437a1 100644 --- a/spec/tapioca/cli/dsl_spec.rb +++ b/spec/tapioca/cli/dsl_spec.rb @@ -2858,6 +2858,7 @@ class PostCompiler < Tapioca::Dsl::Compiler Tapioca::Dsl::Compilers::ActiveRecordStore enabled Tapioca::Dsl::Compilers::ActiveSupportConcern enabled Tapioca::Dsl::Compilers::ActiveSupportCurrentAttributes enabled + Tapioca::Dsl::Compilers::ActiveSupportEnvironmentInquirer enabled Tapioca::Dsl::Compilers::MixedInClassAttributes enabled Tapioca::Dsl::Compilers::SidekiqWorker enabled Tapioca::Dsl::Compilers::SmartProperties enabled @@ -2893,6 +2894,7 @@ class PostCompiler < Tapioca::Dsl::Compiler Tapioca::Dsl::Compilers::ActiveRecordStore enabled Tapioca::Dsl::Compilers::ActiveSupportConcern enabled Tapioca::Dsl::Compilers::ActiveSupportCurrentAttributes enabled + Tapioca::Dsl::Compilers::ActiveSupportEnvironmentInquirer enabled Tapioca::Dsl::Compilers::MixedInClassAttributes enabled Tapioca::Dsl::Compilers::SidekiqWorker enabled Tapioca::Dsl::Compilers::SmartProperties disabled @@ -2928,6 +2930,7 @@ class PostCompiler < Tapioca::Dsl::Compiler Tapioca::Dsl::Compilers::ActiveRecordStore disabled Tapioca::Dsl::Compilers::ActiveSupportConcern disabled Tapioca::Dsl::Compilers::ActiveSupportCurrentAttributes disabled + Tapioca::Dsl::Compilers::ActiveSupportEnvironmentInquirer disabled Tapioca::Dsl::Compilers::MixedInClassAttributes disabled Tapioca::Dsl::Compilers::SidekiqWorker disabled Tapioca::Dsl::Compilers::SmartProperties enabled diff --git a/spec/tapioca/dsl/compilers/active_record_fixtures_spec.rb b/spec/tapioca/dsl/compilers/active_record_fixtures_spec.rb index 1fff0fea6..af311d073 100644 --- a/spec/tapioca/dsl/compilers/active_record_fixtures_spec.rb +++ b/spec/tapioca/dsl/compilers/active_record_fixtures_spec.rb @@ -32,7 +32,7 @@ class User before do require "rails" - define_fake_rails_app + Tapioca::RailsSpecHelper.define_fake_rails_app(tmp_path("lib")) end it "gathers only the ActiveSupport::TestCase base class" do @@ -231,18 +231,6 @@ def posts(fixture_name = nil, *other_fixtures); end end end end - - private - - #: -> void - def define_fake_rails_app - base_folder = Pathname.new(tmp_path("lib")) - - config_class = Struct.new(:root) - config = config_class.new(base_folder) - app_class = Struct.new(:config) - Rails.application = app_class.new(config) - end end end end diff --git a/spec/tapioca/dsl/compilers/active_support_environment_inquirer_spec.rb b/spec/tapioca/dsl/compilers/active_support_environment_inquirer_spec.rb new file mode 100644 index 000000000..73521ab4f --- /dev/null +++ b/spec/tapioca/dsl/compilers/active_support_environment_inquirer_spec.rb @@ -0,0 +1,74 @@ +# typed: strict +# frozen_string_literal: true + +require "spec_helper" + +module Tapioca + module Dsl + module Compilers + class ActiveSupportEnvironmentInquirerSpec < ::DslSpec + describe "Tapioca::Dsl::Compilers::ActiveSupportEnvironmentInquirer" do + describe "without a Rails app" do + it "gathers nothing if not in a Rails application" do + add_default_environment_files + + assert_empty(gathered_constants) + end + end + + describe "with a Rails app" do + before do + require "rails" + Tapioca::RailsSpecHelper.define_fake_rails_app(tmp_path("lib")) + add_default_environment_files + end + + describe "gather_constants" do + it "gathers only `ActiveSupport::EnvironmentInquirer` as a constant" do + assert_equal(["ActiveSupport::EnvironmentInquirer"], gathered_constants) + end + end + + describe "decorate" do + it "generates nothing if there are only default environments" do + expected = <<~RBI + # typed: strong + RBI + + assert_equal(expected, rbi_for("ActiveSupport::EnvironmentInquirer")) + end + + it "generates boolean predicate methods for non-default environments" do + add_content_file("config/environments/staging.rb", "") + add_content_file("config/environments/demo.rb", "") + + expected = <<~RBI + # typed: strong + + class ActiveSupport::EnvironmentInquirer + sig { returns(T::Boolean) } + def demo?; end + + sig { returns(T::Boolean) } + def staging?; end + end + RBI + + assert_equal(expected, rbi_for("ActiveSupport::EnvironmentInquirer")) + end + end + end + end + + private + + #: -> void + def add_default_environment_files + add_content_file("config/environments/development.rb", "") + add_content_file("config/environments/test.rb", "") + add_content_file("config/environments/production.rb", "") + end + end + end + end +end