Module: PryRails

Defined in:
lib/virb/pry_commands.rb

Constant Summary collapse

Commands =
Pry::CommandSet.new do
  create_command "show-routes", "Print out all defined routes in match order, with names." do
    group "Rails"

    def options(opt)
      opt.banner unindent <<-USAGE
        Usage: show-routes [-G]

        show-routes displays the current Rails app's routes.
      USAGE

      opt.on :G, "grep", "Filter output by regular expression", :argument => true
    end

    def process
      Rails.application.reload_routes!
      all_routes = Rails.application.routes.routes

      all_routes = begin
        begin
          # rails 4
          require 'action_dispatch/routing/inspector'
          inspector = ActionDispatch::Routing::RoutesInspector.new
        rescue LoadError => e
          # rails 3.2
          require 'rails/application/route_inspector'
          inspector = Rails::Application::RouteInspector.new
        end
        inspector.format(all_routes)
      rescue LoadError => e
        # rails 3.0 and 3.1. cribbed from
        # https://github.com/rails/rails/blob/3-1-stable/railties/lib/rails/tasks/routes.rake
        routes = all_routes.collect do |route|

          reqs = route.requirements.dup
          reqs[:to] = route.app unless route.app.class.name.to_s =~ /^ActionDispatch::Routing/
            reqs = reqs.empty? ? "" : reqs.inspect

          {:name => route.name.to_s, :verb => route.verb.to_s, :path => route.path, :reqs => reqs}
        end

        # Skip the route if it's internal info route
        routes.reject! { |r| r[:path] =~ %r{/rails/info/properties|^/assets} }

        name_width = routes.map{ |r| r[:name].length }.max
        verb_width = routes.map{ |r| r[:verb].length }.max
        path_width = routes.map{ |r| r[:path].length }.max

        routes.map do |r|
          "#{r[:name].rjust(name_width)} #{r[:verb].ljust(verb_width)} #{r[:path].ljust(path_width)} #{r[:reqs]}"
        end
      end

      output.puts all_routes.grep(Regexp.new(opts[:G] || ".")).join "\n"
    end
  end

  create_command "show-models", "Print out all defined models, with attribrutes." do
    group "Rails"

    def options(opt)
      opt.banner unindent <<-USAGE
        Usage: show-models

        show-models displays the current Rails app's models.
      USAGE

      opt.on :G, "grep", "Color output red by regular expression", :argument => true
    end

    def process
      Rails.application.eager_load!

      if defined?(ActiveRecord::Base)
        models = ActiveRecord::Base.descendants.map do |mod|
          model_string = mod.to_s + "\n"
          if mod.table_exists?
            model_string << mod.columns.map { |col| "  #{col.name}: #{col.type.to_s}" }.join("\n")
          else
            model_string << "  Table doesn't exist"
          end
          mod.reflections.each do |model,ref|
            model_string << "\n  #{ref.macro.to_s} #{model}"
            model_string << " through #{ref.options[:through]}" unless ref.options[:through].nil?
          end
          model_string
        end.join("\n")
      elsif defined?(Mongoid::Document)
        models = get_files.map do |path|
          mod = extract_class_name(path)
          model_string = "\033[1;34m#{mod.to_s}\033[0m\n"
          begin
            if mod.constantize.included_modules.include?(Mongoid::Document)
              model_string << mod.constantize.fields.values.sort_by(&:name).map { |col|
                "  #{col.name}: \033[1;33m#{col.options[:type].to_s.downcase}\033[0m"
              }.join("\n")
              mod.constantize.relations.each do |model,ref|
                model_string << "\n  #{kind_of_relation(ref.relation.to_s)} \033[1;34m#{model}\033[0m"
                model_string << ", autosave" if ref.options[:autosave]
                model_string << ", autobuild" if ref.options[:autobuild]
                model_string << ", validate" if ref.options[:validate]
                model_string << ", dependent-#{ref.options[:dependent]}" if ref.options[:dependent]
              end
            else
              model_string << "  Collection doesn't exist"
            end
            model_string

          rescue Exception
            STDERR.puts "Warning: exception #{$!} raised while trying to load model class #{path}"
          end
        end.join("\n")
      end

      models.gsub!(Regexp.new(opts[:G] || ".", Regexp::IGNORECASE)) { |s| text.red(s) } unless opts[:G].nil?
      output.puts models
    end

    def get_files(prefix ='')
      Dir.glob(prefix << "app/models/**/*.rb")
    end

    def extract_class_name(filename)
      filename.split('/')[2..-1].collect { |i| i.camelize }.join('::').chomp(".rb")
    end

    def kind_of_relation(string)
      case string.gsub('Mongoid::Relations::', '')
      when 'Referenced::Many' then 'has_many'
      when 'Referenced::One' then 'has_one'
      when 'Referenced::In' then 'belongs_to'
      when 'Embedded::Many' then 'embeds_many'
      when 'Embedded::One' then 'embeds_one'
      when 'Embedded::In' then 'embedded_in'
      end
    end
  end

  create_command "show-middleware" do
    group "Rails"

    def options(opt)
      opt.banner unindent <<-USAGE
        Usage: show-middleware [-G]

        show-middleware shows the Rails app's middleware.

        If this pry REPL is attached to a Rails server, the entire middleware
        stack is displayed.  Otherwise, only the middleware Rails knows about is
        printed.
      USAGE

      opt.on :G, "grep", "Filter output by regular expression", :argument => true
    end

    def process
      # assumes there is only one Rack::Server instance
      server = nil
      ObjectSpace.each_object(Rack::Server) do |object|
        server = object
      end

      middlewares = []

      if server
        stack = server.instance_variable_get("@wrapped_app")
        middlewares << stack.class.to_s

        while stack.instance_variable_defined?("@app") do
          stack = stack.instance_variable_get("@app")
          # Rails 3.0 uses the Application class rather than the application
          # instance itself, so we grab the instance.
          stack = Rails.application  if stack == Rails.application.class
          middlewares << stack.class.to_s  if stack != Rails.application
        end
      else
        middleware_names = Rails.application.middleware.map do |middleware|
          # After Rails 3.0, the middleware are wrapped in a special class
          # that responds to #name.
          if middleware.respond_to?(:name)
            middleware.name
          else
            middleware.inspect
          end
        end
        middlewares.concat middleware_names
      end
      middlewares << Rails.application.class.to_s
      print_middleware middlewares.grep(Regexp.new(opts[:G] || "."))
    end

    def print_middleware(middlewares)
      middlewares.each do |middleware|
        string = if middleware == Rails.application.class.to_s
          "run #{middleware}.routes"
        else
          "use #{middleware}"
        end
        output.puts string
      end
    end
  end
end