github.com/nycdavid/zeus@v0.0.0-20201208104106-9ba439429e03/rubygem/lib/zeus/rails.rb (about)

     1  def find_rails_path(root_path)
     2    paths = %w(spec/dummy test/dummy .)
     3    paths.find { |path| File.exists?(File.expand_path(path, root_path)) }
     4  end
     5  
     6  ROOT_PATH = File.expand_path(Dir.pwd)
     7  RAILS_PATH = find_rails_path(ROOT_PATH)
     8  ENV_PATH  = File.expand_path('config/environment',  RAILS_PATH)
     9  BOOT_PATH = File.expand_path('config/boot',  RAILS_PATH)
    10  APP_PATH  = File.expand_path('config/application',  RAILS_PATH) unless defined? APP_PATH
    11  
    12  require 'zeus'
    13  
    14  def gem_is_bundled?(gem)
    15    gemfile_lock_contents = File.read(ROOT_PATH + "/Gemfile.lock")
    16    gemfile_lock_contents.scan(/^\s*#{gem} \(([^=~><]+?)\)/).flatten.first
    17  end
    18  
    19  if version = gem_is_bundled?('method_source')
    20    gem 'method_source', version
    21  end
    22  
    23  require 'zeus/m'
    24  
    25  module Zeus
    26    class Rails < Plan
    27      def after_fork
    28        reconnect_activerecord
    29        restart_girl_friday
    30        reconnect_redis
    31      end
    32  
    33      def _monkeypatch_rake
    34        if version = gem_is_bundled?('rake')
    35          gem 'rake', version
    36        end
    37        require 'rake/testtask'
    38        Rake::TestTask.class_eval {
    39  
    40          # Create the tasks defined by this task lib.
    41          def define
    42            desc "Run tests" + (@name==:test ? "" : " for #{@name}")
    43            task @name do
    44              # ruby "#{ruby_opts_string} #{run_code} #{file_list_string} #{option_list}"
    45              rails_env = ENV['RAILS_ENV']
    46              rubyopt = ENV['RUBYOPT']
    47              ENV['RAILS_ENV'] = nil
    48              ENV['RUBYOPT'] = nil # bundler sets this to require bundler :|
    49              puts "zeus test #{file_list_string}"
    50              ret = system "zeus test #{file_list_string}"
    51              ENV['RAILS_ENV'] = rails_env
    52              ENV['RUBYOPT'] = rubyopt
    53              ret
    54            end
    55            self
    56          end
    57  
    58          alias_method :_original_define, :define
    59  
    60          def self.inherited(klass)
    61            return unless klass.name == "TestTaskWithoutDescription"
    62            klass.class_eval {
    63              def self.method_added(sym)
    64                class_eval do
    65                  if !@rails_hack_reversed
    66                    @rails_hack_reversed = true
    67                    alias_method :define, :_original_define
    68                    def desc(*)
    69                    end
    70                  end
    71                end
    72              end
    73            }
    74          end
    75        }
    76      end
    77  
    78      def boot
    79        _monkeypatch_rake
    80        $LOAD_PATH.unshift "./lib"
    81  
    82        require BOOT_PATH
    83        # config/application.rb normally requires 'rails/all'.
    84        # Some 'alternative' ORMs such as Mongoid give instructions to switch this require
    85        # out for a list of railties, not including ActiveRecord.
    86        # We grep config/application.rb for all requires of rails/all or railties, and require them.
    87        rails_components = File.read(APP_PATH + ".rb").
    88          scan(/^\s*require\s*['"](.*railtie.*|rails\/all)['"]/).flatten
    89  
    90        rails_components = ["rails/all"] if rails_components == []
    91        rails_components.each do |component|
    92          require component
    93        end
    94      end
    95  
    96      def default_bundle
    97        Bundler.require(:default)
    98        Zeus::LoadTracking.add_feature('./Gemfile.lock')
    99      end
   100  
   101      def development_environment
   102        Bundler.require(:development)
   103        ::Rails.env = ENV['RAILS_ENV'] = "development"
   104        require APP_PATH
   105        ::Rails.application.require_environment!
   106      end
   107  
   108      def prerake
   109        require 'rake'
   110      end
   111  
   112      def rake
   113        Rake.application.run
   114      end
   115  
   116      def generate
   117        load_rails_generators
   118  
   119        if rails_5_1_or_higher?
   120          run_rails_5_1_or_higher_command('generate')
   121        else
   122          require 'rails/commands/generate'
   123        end
   124  
   125      end
   126  
   127      def destroy
   128        load_rails_generators
   129        if rails_5_1_or_higher?
   130          run_rails_5_1_or_higher_command('destroy')
   131        else
   132          require 'rails/commands/destroy'
   133        end
   134      end
   135  
   136      def runner
   137        if rails_5_1_or_higher?
   138          run_rails_5_1_or_higher_command('runner')
   139        else
   140          require 'rails/commands/runner'
   141        end
   142      end
   143  
   144      def console
   145        if rails_5_1_or_higher?
   146          run_rails_5_1_or_higher_command('console')
   147        else
   148          require 'rails/commands/console'
   149          if defined?(Pry)
   150            # Adding Rails Console helpers to Pry.
   151            if (3..4).include?(::Rails::VERSION::MAJOR)
   152              require 'rails/console/app'
   153              require 'rails/console/helpers'
   154              TOPLEVEL_BINDING.eval('self').extend ::Rails::ConsoleMethods
   155            end
   156  
   157            Pry.start
   158          else
   159            ::Rails::Console.start(::Rails.application)
   160          end
   161        end
   162      end
   163  
   164      def dbconsole
   165        if rails_5_1_or_higher?
   166          run_rails_5_1_or_higher_command('dbconsole')
   167        else
   168          require 'rails/commands/dbconsole'
   169  
   170          meth = ::Rails::DBConsole.method(:start)
   171  
   172          # `Rails::DBConsole.start` has been changed to load faster in
   173          # https://github.com/rails/rails/commit/346bb018499cde6699fcce6c68dd7e9be45c75e1
   174          #
   175          # This will work with both versions.
   176          if meth.arity.zero?
   177            ::Rails::DBConsole.start
   178          else
   179            ::Rails::DBConsole.start(::Rails.application)
   180          end
   181        end
   182      end
   183  
   184      def server
   185        if rails_5_1_or_higher?
   186          run_rails_5_1_or_higher_command('server')
   187        else
   188          require 'rails/commands/server'
   189          server = ::Rails::Server.new
   190          Dir.chdir(::Rails.application.root)
   191          server.start
   192        end
   193      end
   194  
   195      def test_environment
   196        Bundler.require(:test)
   197  
   198        ::Rails.env = ENV['RAILS_ENV'] = 'test'
   199        require APP_PATH
   200  
   201        $rails_rake_task = 'yup' # lie to skip eager loading
   202        ::Rails.application.require_environment!
   203        $rails_rake_task = nil
   204  
   205        $LOAD_PATH.unshift ".", "./lib", "./test", "./spec"
   206      end
   207  
   208      def test_helper
   209        if ENV['RAILS_TEST_HELPER']
   210          require ENV['RAILS_TEST_HELPER']
   211        else
   212          if File.exists?(ROOT_PATH + "/spec/rails_helper.rb")
   213            # RSpec >= 3.0+
   214            require 'rails_helper'
   215          elsif File.exists?(ROOT_PATH + "/spec/spec_helper.rb")
   216            # RSpec < 3.0
   217            require 'spec_helper'
   218          elsif File.exists?(ROOT_PATH + "/test/minitest_helper.rb")
   219            require 'minitest_helper'
   220          else
   221            require 'test_helper'
   222          end
   223        end
   224      end
   225  
   226      def test(argv=ARGV)
   227        # if there are two test frameworks and one of them is RSpec,
   228        # then "zeus test/rspec/testrb" without arguments runs the
   229        # RSpec suite by default.
   230        if using_rspec?(argv)
   231          ARGV.replace(argv)
   232          # if no directory is given, run the default spec directory
   233          argv << "spec" if argv.empty?
   234          if RSpec::Core::Runner.respond_to?(:invoke)
   235            RSpec::Core::Runner.invoke
   236          else
   237            RSpec::Core::Runner.run(argv)
   238          end
   239        else
   240          require 'minitest/autorun' if using_minitest?
   241          # Minitest and old Test::Unit
   242          Zeus::M.run(argv)
   243        end
   244      end
   245  
   246      private
   247  
   248      def using_rspec?(argv)
   249        defined?(RSpec) && (argv.empty? || spec_file?(argv))
   250      end
   251  
   252      def using_minitest?
   253        defined?(:MiniTest) || defined?(:Minitest)
   254      end
   255  
   256      SPEC_DIR_REGEXP = %r"(^|/)spec"
   257      SPEC_FILE_REGEXP = /.+_spec\.rb$/
   258  
   259      def spec_file?(argv)
   260        argv.any? do |arg|
   261          arg.match(Regexp.union(SPEC_DIR_REGEXP, SPEC_FILE_REGEXP))
   262        end
   263      end
   264  
   265      def restart_girl_friday
   266        return unless defined?(GirlFriday::WorkQueue)
   267        # The Actor is run in a thread, and threads don't persist post-fork.
   268        # We just need to restart each one in the newly-forked process.
   269        ObjectSpace.each_object(GirlFriday::WorkQueue) do |obj|
   270          obj.send(:start)
   271        end
   272      end
   273  
   274      def reconnect_activerecord
   275        return unless defined?(ActiveRecord::Base)
   276        begin
   277          ActiveRecord::Base.clear_all_connections!
   278          ActiveRecord::Base.establish_connection
   279          if ActiveRecord::Base.respond_to?(:shared_connection)
   280            ActiveRecord::Base.shared_connection = ActiveRecord::Base.retrieve_connection
   281          end
   282        rescue ActiveRecord::AdapterNotSpecified
   283        end
   284      end
   285  
   286      def reconnect_redis
   287        return unless defined?(Redis::Client)
   288        ObjectSpace.each_object(Redis::Client) do |client|
   289          client.connect rescue nil
   290        end
   291      end
   292  
   293      def load_rails_generators
   294        require 'rails/generators'
   295        ::Rails.application.load_generators
   296      rescue LoadError # Rails 3.0 doesn't require this block to be run, but 3.2+ does
   297      end
   298  
   299      def run_rails_5_1_or_higher_command(command)
   300        require 'rails/command'
   301        ::Rails::Command.invoke(command, ARGV)
   302      end
   303  
   304      def rails_5_1_or_higher?
   305        (::Rails::VERSION::MAJOR == 5 && ::Rails::VERSION::MINOR >= 1) ||
   306          ::Rails::VERSION::MAJOR > 5
   307      end
   308    end
   309  end
   310  
   311  Zeus.plan ||= Zeus::Rails.new