pax_global_header00006660000000000000000000000064136445422330014520gustar00rootroot0000000000000052 comment=9d9ae4a0b0bd487bb41170c834b3fa417e161f23 pry-0.13.1/000077500000000000000000000000001364454223300124145ustar00rootroot00000000000000pry-0.13.1/.circleci/000077500000000000000000000000001364454223300142475ustar00rootroot00000000000000pry-0.13.1/.circleci/config.yml000066400000000000000000000115231364454223300162410ustar00rootroot00000000000000version: 2 references: repo_restore_cache: &repo_restore_cache restore_cache: keys: - repo-{{ .Environment.CIRCLE_SHA1 }} repo_save_cache: &repo_save_cache save_cache: key: repo-{{ .Environment.CIRCLE_SHA1 }}-{{ epoch }} paths: - ~/pry bundle_install: &bundle_install run: name: Install Bundler dependencies command: bundle install --path ~/pry/vendor/bundle --jobs 15 unit: &unit run: name: Run unit tests command: bundle exec rake environment: # Make sure TERM is set so Pry can indent correctly inside tests. TERM: screen-256color install_alpine_nano: &install_alpine_nano run: name: Install Nano text editor on Alpine Linux command: apk add nano install_ubuntu_nano: &install_ubuntu_nano run: name: Install Nano text editor on Ubuntu command: sudo apt-get install nano jobs: rubocop_lint: docker: - image: circleci/ruby:2.6 working_directory: ~/pry steps: - checkout - <<: *repo_save_cache - <<: *bundle_install - run: name: Run RuboCop linting command: bundle exec rubocop --parallel yard_lint: docker: - image: circleci/ruby:2.6 working_directory: ~/pry steps: - checkout - <<: *repo_save_cache - <<: *bundle_install - run: name: Run YARD linting command: bundle exec yardoc --fail-on-warning --no-progress "ruby-1.9": docker: - image: kyrylo/ruby-1.9.3p551 working_directory: /home/circleci/pry steps: - <<: *repo_restore_cache - <<: *bundle_install - <<: *install_alpine_nano - <<: *unit "ruby-2.0": docker: - image: kyrylo/ruby-2.0.0p648 working_directory: /home/circleci/pry steps: - <<: *repo_restore_cache - <<: *bundle_install - <<: *install_alpine_nano - <<: *unit "ruby-2.1": docker: - image: circleci/ruby:2.1 working_directory: ~/pry steps: - <<: *repo_restore_cache - <<: *bundle_install - <<: *install_ubuntu_nano - <<: *unit "ruby-2.2": docker: - image: circleci/ruby:2.2 working_directory: ~/pry steps: - <<: *repo_restore_cache - <<: *bundle_install - <<: *install_ubuntu_nano - <<: *unit "ruby-2.3": docker: - image: circleci/ruby:2.3 working_directory: ~/pry steps: - <<: *repo_restore_cache - <<: *bundle_install - <<: *install_ubuntu_nano - <<: *unit "ruby-2.4": docker: - image: circleci/ruby:2.4 working_directory: ~/pry steps: - <<: *repo_restore_cache - <<: *bundle_install - <<: *install_ubuntu_nano - <<: *unit "ruby-2.5": docker: - image: circleci/ruby:2.5 working_directory: ~/pry steps: - <<: *repo_restore_cache - <<: *bundle_install - <<: *install_ubuntu_nano - <<: *unit "ruby-2.6": docker: - image: circleci/ruby:2.6 working_directory: ~/pry steps: - <<: *repo_restore_cache - <<: *bundle_install - <<: *install_ubuntu_nano - <<: *unit "ruby-2.7": docker: - image: circleci/ruby:2.7.0 working_directory: ~/pry steps: - <<: *repo_restore_cache - <<: *bundle_install - <<: *install_ubuntu_nano - <<: *unit "jruby-9.1-jdk": docker: - image: circleci/jruby:9.1-jdk working_directory: ~/pry steps: - <<: *repo_restore_cache - <<: *bundle_install - <<: *install_ubuntu_nano - <<: *unit "jruby-9.2-jdk": docker: - image: circleci/jruby:9.2-jdk working_directory: ~/pry steps: - <<: *repo_restore_cache - <<: *bundle_install - <<: *install_ubuntu_nano - <<: *unit workflows: version: 2 build: jobs: - rubocop_lint - yard_lint - "ruby-1.9": requires: - rubocop_lint - yard_lint - "ruby-2.0": requires: - rubocop_lint - yard_lint - "ruby-2.1": requires: - rubocop_lint - yard_lint - "ruby-2.2": requires: - rubocop_lint - yard_lint - "ruby-2.3": requires: - rubocop_lint - yard_lint - "ruby-2.4": requires: - rubocop_lint - yard_lint - "ruby-2.5": requires: - rubocop_lint - yard_lint - "ruby-2.6": requires: - rubocop_lint - yard_lint - "ruby-2.7": requires: - rubocop_lint - yard_lint - "jruby-9.1-jdk": requires: - rubocop_lint - yard_lint - "jruby-9.2-jdk": requires: - rubocop_lint - yard_lint pry-0.13.1/.circleci/ruby-1.9.3p551/000077500000000000000000000000001364454223300163115ustar00rootroot00000000000000pry-0.13.1/.circleci/ruby-1.9.3p551/Dockerfile000066400000000000000000000043231364454223300203050ustar00rootroot00000000000000FROM alpine RUN mkdir -p /usr/local/etc \ && { \ echo 'install: --no-document'; \ echo 'update: --no-document'; \ } >> /usr/local/etc/gemrc ENV RUBY_MAJOR 1.9 ENV RUBY_VERSION 1.9.3-p551 ENV RUBYGEMS_VERSION 1.8.23.2 ENV BUNDLER_VERSION 1.16.6 RUN set -ex \ && apk add --no-cache --virtual .ruby-builddeps \ autoconf \ bison \ bzip2 \ bzip2-dev \ ca-certificates \ coreutils \ curl \ gcc \ gdbm-dev \ glib-dev \ libc-dev \ libffi-dev \ libxml2-dev \ libxslt-dev \ linux-headers \ make \ ncurses-dev \ openssl-dev \ procps \ readline-dev \ ruby \ yaml-dev \ zlib-dev \ && curl -fSL -o ruby.tar.gz "http://cache.ruby-lang.org/pub/ruby/$RUBY_MAJOR/ruby-$RUBY_VERSION.tar.gz" \ && mkdir -p /usr/src \ && tar -xzf ruby.tar.gz -C /usr/src \ && rm ruby.tar.gz \ && cd /usr/src/ruby-$RUBY_VERSION \ && { echo '#define ENABLE_PATH_CHECK 0'; echo; cat file.c; } > file.c.new && mv file.c.new file.c \ && { echo '#include '; echo; cat io.c; } > io.c.new && mv io.c.new io.c \ && autoconf \ && ac_cv_func_isnan=yes ac_cv_func_isinf=yes ./configure --disable-install-doc \ && make \ && make install \ && runDeps="$( \ scanelf --needed --nobanner --recursive /usr/local \ | awk '{ gsub(/,/, "\nso:", $2); print "so:" $2 }' \ | sort -u \ | xargs -r apk info --installed \ | sort -u \ )" \ && apk add --virtual .ruby-rundeps $runDeps \ bzip2 \ ca-certificates \ curl \ libffi-dev \ openssl-dev \ yaml-dev \ procps \ zlib-dev \ && apk del .ruby-builddeps \ && gem update --system $RUBYGEMS_VERSION \ && rm -r /usr/src/ruby-$RUBY_VERSION RUN apk add --no-cache git nano build-base RUN gem update --system 2.7.9 RUN gem install bundler --version "$BUNDLER_VERSION" --force ENV GEM_HOME /usr/local/bundle ENV BUNDLE_PATH="$GEM_HOME" BUNDLE_BIN="$GEM_HOME/bin" BUNDLE_SILENCE_ROOT_WARNING=1 BUNDLE_APP_CONFIG="$GEM_HOME" ENV PATH $BUNDLE_BIN:$PATH RUN mkdir -p "$GEM_HOME" "$BUNDLE_BIN" && chmod 777 "$GEM_HOME" "$BUNDLE_BIN" CMD [ "irb" ] pry-0.13.1/.circleci/ruby-2.0.0p648/000077500000000000000000000000001364454223300163055ustar00rootroot00000000000000pry-0.13.1/.circleci/ruby-2.0.0p648/Dockerfile000066400000000000000000000043231364454223300203010ustar00rootroot00000000000000FROM alpine RUN mkdir -p /usr/local/etc \ && { \ echo 'install: --no-document'; \ echo 'update: --no-document'; \ } >> /usr/local/etc/gemrc ENV RUBY_MAJOR 2.0 ENV RUBY_VERSION 2.0.0-p648 ENV RUBYGEMS_VERSION 1.8.23.2 ENV BUNDLER_VERSION 1.16.6 RUN set -ex \ && apk add --no-cache --virtual .ruby-builddeps \ autoconf \ bison \ bzip2 \ bzip2-dev \ ca-certificates \ coreutils \ curl \ gcc \ gdbm-dev \ glib-dev \ libc-dev \ libffi-dev \ libxml2-dev \ libxslt-dev \ linux-headers \ make \ ncurses-dev \ openssl-dev \ procps \ readline-dev \ ruby \ yaml-dev \ zlib-dev \ && curl -fSL -o ruby.tar.gz "http://cache.ruby-lang.org/pub/ruby/$RUBY_MAJOR/ruby-$RUBY_VERSION.tar.gz" \ && mkdir -p /usr/src \ && tar -xzf ruby.tar.gz -C /usr/src \ && rm ruby.tar.gz \ && cd /usr/src/ruby-$RUBY_VERSION \ && { echo '#define ENABLE_PATH_CHECK 0'; echo; cat file.c; } > file.c.new && mv file.c.new file.c \ && { echo '#include '; echo; cat io.c; } > io.c.new && mv io.c.new io.c \ && autoconf \ && ac_cv_func_isnan=yes ac_cv_func_isinf=yes ./configure --disable-install-doc \ && make \ && make install \ && runDeps="$( \ scanelf --needed --nobanner --recursive /usr/local \ | awk '{ gsub(/,/, "\nso:", $2); print "so:" $2 }' \ | sort -u \ | xargs -r apk info --installed \ | sort -u \ )" \ && apk add --virtual .ruby-rundeps $runDeps \ bzip2 \ ca-certificates \ curl \ libffi-dev \ openssl-dev \ yaml-dev \ procps \ zlib-dev \ && apk del .ruby-builddeps \ && gem update --system $RUBYGEMS_VERSION \ && rm -r /usr/src/ruby-$RUBY_VERSION RUN apk add --no-cache git nano build-base RUN gem update --system 2.7.9 RUN gem install bundler --version "$BUNDLER_VERSION" --force ENV GEM_HOME /usr/local/bundle ENV BUNDLE_PATH="$GEM_HOME" BUNDLE_BIN="$GEM_HOME/bin" BUNDLE_SILENCE_ROOT_WARNING=1 BUNDLE_APP_CONFIG="$GEM_HOME" ENV PATH $BUNDLE_BIN:$PATH RUN mkdir -p "$GEM_HOME" "$BUNDLE_BIN" && chmod 777 "$GEM_HOME" "$BUNDLE_BIN" CMD [ "irb" ] pry-0.13.1/.gitignore000066400000000000000000000002051364454223300144010ustar00rootroot00000000000000Makefile *.so *.o *.def *.rbc *.bundle doc/ pkg/ coverage/ .yardoc/ /tags vendor *.gem .rvmrc Gemfile.lock *.swp **/*~ .idea/ .ackrc pry-0.13.1/.rspec000066400000000000000000000000361364454223300135300ustar00rootroot00000000000000--color --require spec_helper pry-0.13.1/.rubocop.yml000066400000000000000000000026551364454223300146760ustar00rootroot00000000000000inherit_from: .rubocop_todo.yml Style/NumericPredicate: Enabled: false Layout/IndentHeredoc: Enabled: false Naming/MethodName: Exclude: - 'spec/code_object_spec.rb' - 'lib/pry/method.rb' - 'lib/pry/wrapped_module.rb' - 'lib/pry/code.rb' Metrics/ModuleLength: Exclude: - 'lib/pry/config/behavior.rb' Layout/CommentIndentation: Exclude: - 'spec/fixtures/example_nesting.rb' Layout/MultilineMethodCallIndentation: EnforcedStyle: indented Style/ClassAndModuleChildren: Exclude: - 'spec/fixtures/example_nesting.rb' # TODO: delete exclusions when we drop Ruby 1.9.3 support. Style/ExpandPathArguments: Exclude: - 'lib/pry/commands.rb' - 'pry.gemspec' Style/Semicolon: Exclude: - 'spec/method_spec.rb' Style/SingleLineMethods: Exclude: - 'spec/method_spec.rb' - 'spec/fixtures/example_nesting.rb' Style/StringLiterals: Enabled: false # TODO: delete this rule when we drop Ruby 1.9.3 support. Style/SymbolArray: EnforcedStyle: brackets Metrics/LineLength: Max: 90 Bundler/OrderedGems: Enabled: false Style/DoubleNegation: Enabled: false Style/Documentation: Enabled: false Style/CommentedKeyword: Exclude: - 'spec/fixtures/example_nesting.rb' Gemspec/RequiredRubyVersion: Enabled: false Style/ModuleFunction: Enabled: false Style/FrozenStringLiteralComment: Enabled: true EnforcedStyle: always Exclude: - 'spec/fixtures/example_nesting.rb' pry-0.13.1/.rubocop_todo.yml000066400000000000000000000021151364454223300157120ustar00rootroot00000000000000# This configuration was generated by # `rubocop --auto-gen-config` # on 2019-03-21 00:51:00 +0200 using RuboCop version 0.66.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. # Offense count: 88 Metrics/AbcSize: Max: 48 # Offense count: 191 # Configuration parameters: CountComments, ExcludedMethods. # ExcludedMethods: refine Metrics/BlockLength: Max: 794 # Offense count: 1 # Configuration parameters: CountBlocks. Metrics/BlockNesting: Max: 4 # Offense count: 19 # Configuration parameters: CountComments. Metrics/ClassLength: Max: 317 # Offense count: 32 Metrics/CyclomaticComplexity: Max: 32 # Offense count: 114 # Configuration parameters: CountComments, ExcludedMethods. Metrics/MethodLength: Max: 101 # Offense count: 1 # Configuration parameters: CountKeywordArgs. Metrics/ParameterLists: Max: 6 # Offense count: 29 Metrics/PerceivedComplexity: Max: 25 pry-0.13.1/.yardopts000066400000000000000000000000571364454223300142640ustar00rootroot00000000000000--markup markdown --private --hide-void-return pry-0.13.1/CHANGELOG.md000066400000000000000000001474471364454223300142460ustar00rootroot00000000000000### master ### [v0.13.1][v0.13.1] (April 12, 2020) #### Bug fixes * Fixed bug where on invalid input only the last syntax error is displayed (instead of all of them) ([#2117](https://github.com/pry/pry/pull/2117)) * Fixed `Pry::Config` raising `NoMethodError` on undefined option instead of returning `nil` (usually invoked via `Pry.config.foo_option` calls) ([#2126](https://github.com/pry/pry/pull/2126)) * Fixed `help` command not displaying regexp aliases properly ([#2120](https://github.com/pry/pry/pull/2120)) * Fixed `pry-backtrace` not working ([#2122](https://github.com/pry/pry/pull/2122)) ### [v0.13.0][v0.13.0] (March 21, 2020) #### Features * Added metadata to the gem (such as changelog URI, source code URI & bug tracker URI), so it shows on https://rubygems.org/gems/pry ([#1869](https://github.com/pry/pry/pull/1869)) * Added ability to forward ARGV to a Pry session via `--` (or `-`) when launching Pry from shell ([#1902](https://github.com/pry/pry/commit/5cd65d3c0eb053f6edcdf571eea5d0cd990626ed)) * Added `Pry::Config::LazyValue` & `Pry::Config::MemoizedValue`, which allow storing callable procs in the config ([#2024](https://github.com/pry/pry/pull/2024)) * Added the `rc_file` config option that tells Pry the path to `pryrc` ([#2027](https://github.com/pry/pry/pull/2027)) * Added the `--code` flag to the `wtf` command, which shows code for each backtrace frame ([#2037](https://github.com/pry/pry/pull/2037)) * Added the ability to paste method call chains with leading dots ([#2060](https://github.com/pry/pry/pull/2060)) #### API changes * `Pry::Prompt` is a class now and it can be instantiated to create new prompts on the fly that are not registered with `Pry::Prompt#add`. Learn more about its API in the docs ([#1877](https://github.com/pry/pry/pull/1877)) #### Deprecations * Deprecated `Pry.config.exception_whitelist` in favor of `Pry.config.unrescued_exceptions` ([#1874](https://github.com/pry/pry/pull/1874)) * Deprecated `Pry.config.prompt = Pry::Prompt[:simple][:value]` in favor of `Pry.config.prompt = Pry::Prompt[:simple]` when setting Pry prompt via `pryrc`. `Pry::Prompt[:simple]` would return an instance of `Pry::Prompt` instead of `Hash` ([#1877](https://github.com/pry/pry/pull/1877)) * Deprecated setting prompt via an array of two procs: ([#1877](https://github.com/pry/pry/pull/1877)) ```ruby # Deprecated, emits a warning. Pry.config.prompt = [proc {}, proc {}] ``` This will be removed in the next release. * Deprecated the `show-doc` command. The `show-source -d` is the new recommended way of reading docs ([#1934](https://github.com/pry/pry/pull/1934)) * Deprecated `Pry::Command#_pry_`. Use `Pry::Command#pry_instance` instead ([#1989](https://github.com/pry/pry/pull/1989)) #### Breaking changes * Deleted deprecated `Pry::Platform` ([#1863](https://github.com/pry/pry/pull/1863)) * Deleted deprecated `Pry#{input/output}_array` ([#1884](https://github.com/pry/pry/pull/1864)) * Deleted deprecated `Pry::Prompt::MAP` ([#1866](https://github.com/pry/pry/pull/1866)) * Deleted deprecated methods of `Pry::Helpers::BaseHelpers` such as `mac_osx?`, `linux?`, `windows?`, `windows_ansi?`, `jruby?`, `jruby_19?`, `mri?`, `mri_19?`, `mri_2?` ([#1867](https://github.com/pry/pry/pull/1867)) * Deleted deprecated `Pry::Command#text` ([#1865](https://github.com/pry/pry/pull/1865)) * Deleted deprecated `Pry::Method#all_from_common` ([#1868](https://github.com/pry/pry/pull/1868)) * Deleted `install-command` ([#1979](https://github.com/pry/pry/pull/1979)) * Deleted `Pry::Helpers::BaseHelpers#command_dependencies_met?` ([#1979](https://github.com/pry/pry/pull/1979)) * Deleted commands: `gem-cd`, `gem-install`, `gem-list`, `gem-open`, `gem-readme`, `gem-search`, `gem-stats` ([#1981](https://github.com/pry/pry/pull/1981)) * Deleted deprecated commands: `edit-method` and `show-command` ([#2001](https://github.com/pry/pry/pull/2001)) * Deleted `Pry::Command#disabled_commands` ([#2001](https://github.com/pry/pry/pull/2001)) * Deleted `Pry::BlockCommand#opts` (use `#context` instead) ([#2003](https://github.com/pry/pry/pull/2003)) * Deleted `Pry.lazy` (use `Pry::Config::LazyValue` instead) ([#2024](https://github.com/pry/pry/pull/2024)) #### Bug fixes * Fixed bug where using `Pry.config.prompt_name` can return a `Pry::Config::Lazy` instead of expected instance of `String` ([#1890](https://github.com/pry/pry/commit/c8f23b3464d596c08922dc923c64bb57488e6227)) * Fixed `LoadError` being raised when using auto completions and Bundler ([#1896](https://github.com/pry/pry/commit/85850f47e074fe01f93e5cb7d561e7c2de7aede9)) * Fixed bug where `Pry.input_ring` doesn't append duplicate elements ([#1898](https://github.com/pry/pry/pull/1898)) * Fixed Ruby 2.6 warning about `Binding#source_location` ([#1904](https://github.com/pry/pry/pull/1904)) * Fixed wrong `winsize` when custom `output` is passed to Pry ([#2045](https://github.com/pry/pry/pull/2045)) * Fixed `XDG_CONFIG_HOME` & `XDG_DATA_HOME` precedence. When these env variables are set, Pry no longer uses traditional files like `~/.pryrc` & `~/.pry_history`. Instead, the env variable paths are loaded first ([#2056](https://github.com/pry/pry/pull/2056)) * Fixed the `$SAFE will become a normal global variable in Ruby 3.0` warning on Ruby 2.7 ([#2107](https://github.com/pry/pry/pull/2107)) * Fixed bug when `whereami -c` cannot show beginning of the class, which is on the same line as another expression ([#2098](https://github.com/pry/pry/pull/2098)) * Fixed bug when `Object#owner` is defined, which results into somewhat broken method introspection ([#2113](https://github.com/pry/pry/pull/2113)) * Fixed bug when indentation leaves parts of input after pressing enter when Readline is enabled with mode indicators for vi mode. This was supposed to be fixed in v0.12.2 but it regressed ([#2114](https://github.com/pry/pry/pull/2114)) ### [v0.12.2][v0.12.2] (November 12, 2018) #### Bug fixes * Restore removed deprecations, which were removed by accident due to a bad rebase. ### [v0.12.1][v0.12.1] (November 12, 2018) #### Bug fixes * Stopped creating a new hash each time `Pry::Prompt#[]` is invoked ([#1855](https://github.com/pry/pry/pull/1855)) * Fixed `less` pager not working when it's available ([#1861](https://github.com/pry/pry/pull/1861)) ### [v0.12.0][v0.12.0] (November 5, 2018) #### Major changes * Dropped support for Rubinius ([#1785](https://github.com/pry/pry/pull/1785)) #### Features * Added a new command, `clear-screen`, that clears the content of the screen Pry is running in regardless of platform (Windows or UNIX-like) ([#1723](https://github.com/pry/pry/pull/1723)) * Added a new command, `gem-stat`, that prints gem statistics such as gem dependencies and downloads ([#1707](https://github.com/pry/pry/pull/1707)) * Added support for nested exceptions for the `wtf` command ([#1791](https://github.com/pry/pry/pull/1791)) * Added support for dynamic prompt names ([#1833](https://github.com/pry/pry/pull/1833)) ```rb # pryrc Pry.config.prompt_name = Pry.lazy { rand(100) } # Session [1] 80(main)> [2] 87(main)> [3] 30(main)> ``` * Added support for XDG Base Directory Specification ([#1609](https://github.com/pry/pry/pull/1609), [#1844](https://github.com/pry/pry/pull/1844), ([#1848](https://github.com/pry/pry/pull/1848))) * Removed the `simple-prompt`. Use `change-prompt simple` instead. The `list-prompt` command was removed and embedded as `change-prompt --list` ([#1849](https://github.com/pry/pry/pull/1849)) #### API changes * The following methods started accepting the new optional `config` parameter ([#1809](https://github.com/pry/pry/pull/1809)): * `Pry::Helpers.tablify(things, line_length, config = Pry.config)` * `Pry::Helpers.tablify_or_one_line(heading, things, config = Pry.config)` * `Pry::Helpers.tablify_to_screen_width(things, options, config = Pry.config)` * `Pry::Helpers::Table.new(items, args, config = Pry.config)` You are expected to pass a session-local `_pry_.config` instead of the global one. * Added new method `Pry::Config.assign`, for creating a Config non-recursively ([#1725](https://github.com/pry/pry/issues/1725)) * Added `Pry.lazy`, which is a helper method for values that need to be calculated dynamically. Currently, only `config.prompt_name` supports it ([#1833](https://github.com/pry/pry/pull/1833)) * `Pry::Prompt` responds to `.[]`, `.all` & `.add` now. The `Pry::Prompt.add` method must be used for implementing custom prompts. See the API in the documentation for the class ([#1846](https://github.com/pry/pry/pull/1846)) #### Breaking changes * Deleted the `Pry::Helpers::Text.bright_default` alias for `Pry::Helpers::Text.bold` ([#1795](https://github.com/pry/pry/pull/1795)) * `Pry::Helpers.tablify_to_screen_width(things, options, config = Pry.config)` requires `options` or `nil` in place of them. * `Pry::Helpers::Table.new(items, args, config = Pry.config)` requires `args` or `nil` in place of them. * Completely revamped `Pry::HistoryArray` ([#1818](https://github.com/pry/pry/pull/1818)). * It's been renamed to `Pry::Ring` ([#1817](https://github.com/pry/pry/pull/1817)) * The implementation has changed and as result, the following methods were removed: * `Pry::Ring#length` (use `Pry::Ring#count` instead) * `#empty?`, `#each`, `#inspect`, `#pop!`, `#to_h` * To access old Enumerable methods convert the ring to Array with `#to_a` * Fixed indexing for elements (e.g. `_pry_.input_ring[0]` always return some element and not `nil`) * Renamed `Pry.config.prompt_safe_objects` to `Pry.config.prompt_safe_contexts` * Removed deprecated `Pry::CommandSet#before_command` & `Pry::CommandSet#after_command` ([#1838](https://github.com/pry/pry/pull/1838)) #### Deprecations * Deprecated `_pry_.input_array` & `_pry_.output_array` in favour of `_pry_.input_ring` & `_pry_.output_ring` respectively ([#1814](https://github.com/pry/pry/pull/1814)) * Deprecated `Pry::Command#text`. Please use `#black`, `#white`, etc. directly instead (as you would with helper functions from `BaseHelpers` and `CommandHelpers`) ([#1701](https://github.com/pry/pry/pull/1701)) * Deprecated `_pry_.input_array` & `_pry_.output_array` in favour of `_pry_.input_ring` and `_pry_.output_ring` respectively ([#1817](https://github.com/pry/pry/pull/1817)) * Deprecated `Pry::Platform`. Use `Pry::Helpers::Platform` instead. Note that `Pry::Helpers::BaseHelpers` still includes the `Platform` methods but emits a warning. You must switch to `Pry::Helpers::Platform` in your code ([#1838](https://github.com/pry/pry/pull/1838), ([#1845](https://github.com/pry/pry/pull/1845))) * Deprecated `Pry::Prompt::MAP`. You should use `Pry::Prompt.all` instead to access the same map ([#1846](https://github.com/pry/pry/pull/1846)) #### Bug fixes * Fixed a bug where `cd Hash.new` reported `self` as an instance of Pry::Config in the prompt ([#1725](https://github.com/pry/pry/pull/1725)) * Silenced the `Could not find files for the given pattern(s)` error message coming from `where` on Windows, when `less` or another pager is not installed ([#1767](https://github.com/pry/pry/pull/1767)) * Fixed possible double loading of Pry plugins' `cli.rb` on Ruby (>= 2.4) due to [the `realpath` changes while invoking `require`](https://bugs.ruby-lang.org/issues/10222) ([#1762](https://github.com/pry/pry/pull/1762), [#1774](https://github.com/pry/pry/pull/1762)) * Fixed `NoMethodError` on code objects that have a comment but no source when invoking `show-source` ([#1779](https://github.com/pry/pry/pull/1779)) * Fixed `negative argument (ArgumentError)` upon pasting code with tabs, which used to confuse automatic indentation ([#1771](https://github.com/pry/pry/pull/1771)) * Fixed Pry not being able to load history on Ruby 2.4.4+ when it contains the null character ([#1789](https://github.com/pry/pry/pull/1789)) * Fixed Pry raising errors on `cd`'ing into some objects that redefine `method_missing` and `respond_to?` ([#1811](https://github.com/pry/pry/pull/1811)) * Fixed bug when indentation leaves parts of input after pressing enter when Readline is enabled with mode indicators for vi mode ([#1813](https://github.com/pry/pry/pull/1813), [#1820](https://github.com/pry/pry/pull/1820), [#1825](https://github.com/pry/pry/pull/1825)) * Fixed `edit` not writing to history ([#1749](https://github.com/pry/pry/issues/1749)) #### Other changes * Deprecated the `Data` constant to match Ruby 2.5 in the `ls` command ([#1731](https://github.com/pry/pry/pull/1731)) ### 0.11.3 #### Features * Add Pry::Testable, an improved modular replacement for PryTestHelpers. **breaking change**. See pull request [#1679](https://github.com/pry/pry/pull/1679). * Add a new category module: "Pry::Platform". Loosely related to #1668 below. See pull request [#1670](https://github.com/pry/pry/pull/1670) * Add `mac_osx?` and `linux?` utility functions to Pry::Helpers::BaseHelpers. See pull request [#1668](https://github.com/pry/pry/pull/1668). * Add utility functions for drawing colorised text on a colorised background. See pull request [#1673](https://github.com/pry/pry/pull/1673). #### Bug fixes * Fix a case of infinite recursion in `Pry::Method::WeirdMethodLocator#find_method_in_superclass` that users of the [Hanami](http://hanamirb.org/) web framework experienced and reported since 2015. See pull request [#1689](https://github.com/pry/pry/pull/1689). * Fix a bug where Method objects were not returned for setters inherited from a default (Pry::Config::Default). Eg, this is no longer an error: pry(main)> d = Pry::Config.from_hash({}, Pry::Config::Default.new) pry(main)> d.method(:exception_whitelist=) # Error See pull request [#1688](https://github.com/pry/pry/pull/1688). * Do not capture unused Proc objects in Text helper methods `no_color` and `no_paging`, for performance reasons. Improve the documentation of both methods. See pull request [#1691](https://github.com/pry/pry/pull/1691). * Fix `String#pp` output color. See pull request [#1674](https://github.com/pry/pry/pull/1674). ### 0.11.0 * Add alias 'whereami[?!]+' for 'whereami' command. ([#1597](https://github.com/pry/pry/pull/1597)) * Improve Ruby 2.4 support ([#1611](https://github.com/pry/pry/pull/1611)): * Deprecated constants are hidden from `ls` output by default, use the `-d` switch to see them. * Fix warnings that originate in Pry while using the repl. * Improve completion speed in large applications. ([#1588](https://github.com/pry/pry/pull/1588)) * Pry::ColorPrinter.pp: add `newline` argument and pass it on to PP. ([#1603](https://github.com/pry/pry/pull/1603)) * Use `less` or system pager pager on MS Windows if it is available. ([#1512](https://github.com/pry/pry/pull/1512)) * Add `Pry.configure` as an alternative to the current way of changing configuration options in `.pryrc` files. ([#1502](https://github.com/pry/pry/pull/1502)) * Add `Pry::Config::Behavior#eager_load!` to add a possible workaround for issues like ([#1501](https://github.com/pry/pry/issues/1501)) * Remove Slop as a runtime dependency by vendoring v3.4 as Pry::Slop. People can depend on Slop v4 and Pry at the same time without running into version conflicts. ([#1497](https://github.com/pry/pry/issues/1497)) * Fix auto-indentation of code that uses a single-line rescue ([#1450](https://github.com/pry/pry/issues/1450)) * Remove "Pry::Config#refresh", please use "Pry::Config#clear" instead. * Defining a method called "ls" no longer breaks the "ls" command ([#1407](https://github.com/pry/pry/issues/1407)) * Don't raise when directory permissions don't allow file expansion ([#1432](https://github.com/pry/pry/issues/1432)) * Syntax highlight <tt> tags in documentation output. * Add support for BasicObject subclasses who implement their own #inspect (#1341) * Fix 'include RSpec::Matchers' at the top-level (#1277) * Add 'gem-readme' command, prints the README file bundled with a rubygem * Add 'gem-search' command, searches for a gem with the rubygems.org HTTP API * Fixed bug in the `cat` command where it was impossible to use line numbers with files ([#1349](https://github.com/pry/pry/issues/1349)) * Fixed uncaught Errno::EOPNOTSUPP exception when $stdout is a socket ([#1352](https://github.com/pry/pry/issues/1352)) * Display a warning when you cd'ed inside a C object and executed 'show-source' without arguments ([#691](https://github.com/pry/pry/issues/691)) * Make the stagger_output method more reliable by reusing possibly available Pry instance ([#1364](https://github.com/pry/pry/pull/1364)) * Make the 'gem-install' message less confusing by removing backticks ([#1350](https://github.com/pry/pry/pull/1350)) * Fixed error when Pry was trying to load incompatible versions of plugins ([#1312](https://github.com/pry/pry/issues/1312)) * Fixed bug when `hist --clear` led to ArgumentError ([#1340](https://github.com/pry/pry/pull/1340)) * Fixed the "uninitialized constant Pry::ObjectPath::StringScanner" exception during autocomplete ([#1330](https://github.com/pry/pry/issues/1330)) * Secured usage of colours with special characters (RL_PROMPT_START_IGNORE and RL_PROMPT_END_IGNORE) in Pry::Helpers::Text ([#493](https://github.com/pry/pry/issues/493#issuecomment-39232771)) * Fixed regression with `pry -e` when it messes the terminal ([#1387](https://github.com/pry/pry/issues/1387)) * Fixed regression with space prefixes of expressions ([#1369](https://github.com/pry/pry/issues/1369)) * Introduced the new way to define hooks for commands (with `Pry.hooks.add_hook("{before,after}_commandName")`). The old way is deprecated, but still supported (with `Pry.commands.{before,after}_command`) ([#651](https://github.com/pry/pry/issues/651)) * Removed old API's using `Pry::Hooks.from_hash` altogether * Removed hints on Foreman support (see [this](https://github.com/ddollar/foreman/pull/536)) * Fixed support for the tee command ([#1334](https://github.com/pry/pry/issues/1334)) * Implemented support for CDPATH for ShellCommand ([#1433](https://github.com/pry/pry/issues/1433), [#1434](https://github.com/pry/pry/issues/1434)) * `Pry::CLI.parse_options` does not start Pry anymore ([#1393](https://github.com/pry/pry/pull/1393)) * The gem uses CPU-less platforms for Windows now ([#1410](https://github.com/pry/pry/pull/1410)) * Add `Pry::Config::Memoization` to make it easier to implement your own `Pry::Config::Default` class.([#1503](https://github.com/pry/pry/pull/1503/)) * Lazy load the config defaults for `Pry.config.history` and `Pry.config.gist`. ### 0.10.1 * Fix bugs with jruby * Move to rspec for testing (from bacon) * Clean up ruby warnings ### 0.10.0 #### Features * Added a `watch` command that lets you see how values change over time. * Added an experimental `Pry.auto_resize!` method * Makes Pry notice that your window has resized and tell Readline about it * Fixes various bugs with command history after a window resize * Off by default, but can be called from your `.pryrc` if you're brave * `play` now has an `-e`/`--expression` flag * Evaluates until the end of the first valid expression * History gets appended to `~/.pry_history` after every input, not just at quit * Return values render with more accurate syntax highlighting * Return values start rendering immediately and stream into the pager * User can override `.pryrc` location by setting `$PRYRC` env var (#893) * User can whitelist objects whose inspect output should appear in prompt (#885) * See `Pry.config.prompt_safe_objects` * `whereami` is now aliased to `@` * Added arguments to `whereami`: * `-m` shows the surrounding method * `-c` shows the surrounding class * `-f` shows the entire file * Lazy load configuration values (Pry.config). (#1096) * Defer requiring `readline` until Pry is started for the first time. (#1117) * Add option to disable input completer through `_pry_.config.completer = nil` * Add `list-prompts` command. (#1175) * Lists the available prompts available for use. * Add `change-prompt` command. (#1175) * Switches the current prompt, by name. * Add `list-inspectors` command. (#1176) * Lists the inspectors available to print Ruby return values. * Add `change-inspector` command. (#1176) * Switches the current inspector, by name. * Add `show-source -e`. (#1185) * Evaluate the given Ruby expression and show the source of its return value. * Add `Pry.config.windows_console_warning`(#1218) * Windows JRuby users who don't want warnings about ansicon can set `Pry.config.windows_console_warning = false`. * Add arguments to `play` command. * `-p` prints the code before playing it. * `-e` allows you to play expressions from your session. * Add `cd -` to switch to the previous binding. * Allow prying into frozen objects. #### Dependency changes * Remove dependency on `ffi` gem on JRuby ([#1158](https://github.com/pry/pry/issues/1158)) * Remove optional dependency on Bond ([#1166](https://github.com/pry/pry/issues/1166)) * Bond support has been extracted to the `pry-bond` plugin * Remove dependency on `openstruct` ([#1096](https://github.com/pry/pry/issues/1096)) * Drop support for Ruby 1.8.7 (0.9.12.x will continue to be available) * Add support for Ruby 2.1 * Require Coderay `~> 1.1.0` * Remove deprecated hooks API ([#1209](https://github.com/pry/pry/pull/1209)) * Add 64-bit windows support. #### Bug fixes, etc. * The `gem-install` command can require gems like `net-ssh` thanks to better logic for guessing what path to require. (#1188) * `toggle-color` command toggles the local `_pry_.color` setting instead of the global `Pry.color`. * Update `Pry::CLIPPED_PRINT` to include a hex representation of object ID when printing a return value. (#1162) * Wrap exceptions in a proxy instead of adding singleton methods. (#1145) * `Pry#last_exception=` now supports exception objects that have been frozen. * `binding.pry` inside `.pryrc` file now works, with some limitations (@richo / #1118) * Add support for BasicObjects to `ls` (#984) * Allow `ls -c ` (#891) * Fix indentation not working if the `mathn` stdlib was loaded (#872) * Fix `hist`'s `--exclude-pry` switch (#874) * Fix `gem-install` on JRuby (#870) * Fix source lookup for instrumented classes (#923) * Improved thread safety when multiple instances are running (#944) * Make `edit` ignore `-n`/`--no-reload` flag and `disable_auto_reload` config in cases where the user was editing a tempfile * Make `gem-cd` use the most recent gem, not the oldest * Make `install-command` honor `.gemrc` switches (#666) * Make `hist` with no parameters show just the current session's history (#205) * `hist --all` shows older history * Make `-s`/`--super` flag of `show-source`/`show-doc` work when method name is being inferred from context (#877) * Rename `--installed-plugins` flag to `--plugins` * Strip ANSI codes from prompt before measuring length for indentation (#493) * Fix bug in `edit` regarding recognition of file names without suffix. * Reduced download size by removing tests etc. from distributed gem. #### Dev-facing changes * `CommandSet#commands`, sometimes referenced through `Pry.commands.commands`, renamed to `CommandSet#to_hash`. It returns a duplicate of the internal hash a CommandSet uses. * `CommandSet#keys` is now an alias of `CommandSet#list_commands`. * All commands should now reference configuration values via `_pry_.config` (local) and not `Pry.config` (global). (#1096) * This change improves support for concurrent environments and context-specific Pry sessions. `_pry_.config` inherits default values from `Pry.config` but can override them locally. * `rake pry` now accepts switches prefixed with `_` (e.g., `rake pry _v`) * Pagers now act like `IO`s and accept streaming output * See `_pry_.pager.page` and `_pry_.pager.open`. * The `Pry` class has been broken up into two smaller classes. * `Pry` represents non-UI-specific session state, including the eval string * `Pry::REPL` controls the user-facing interface * This should make it easier to drive Pry from alternative interfaces * `Pry.start` now has a `:driver` option that defaults to `Pry::REPL` * This involved a lot of refactoring and may break plugins that depend on the old layout * Add `ColorPrinter` subclass of `PP` for colorized object inspection * Add `[]` and `[]=` methods to `CommandSet`, which find and replace commands * Example: `Pry.commands["help"] = MyHelpCommand` * The completion API has been refactored (see fdb703a8de4ef3) * `Pry.config.input_stack` (and the input stack concept in general) no longer exists * There's a new `Pry::Terminal` class that implements a number of different methods of determining the terminal's dimensions * Add `ReplTester` class for high-level simulation of Pry sessions in tests * Add `Pry.main`. Returns the special instance of Object referenced by self of `TOPLEVEL_BINDING`: "main". * Changed second argument of `Pry.view_clip()` from Fixnum to Hash to support returning a string with or without a hex representation of object ID. (#1162) * The `output` and `pager` objects will now strip color-codes, so commands should always print in color. * Commands now have a `state` hash that is persistent across invocations of the command in the same pry session. ### 0.9.12.6 (2014/01/28) * Don't fail if Bond is not installed (#1106) ### 0.9.12.5 (2014/01/27) * Fix early readline errors by deferring require of readline (#1081, #1095) ### 0.9.12.4 (2013/11/23) * Fix issue with Coderay colors being black, even when on a black background (#1016) ### 0.9.12.3 (2013/09/11) * Bump Coderay dependency (#987) * Fix consecutive newlines in heredocs being collapsed (#962) * Fix pager not working in JRuby > 1.7.5 (#992) ### 0.9.12.2 (2013/05/10) * Make `reload-code` with no args reload "current" file (#920) ### 0.9.12.1 (2013/04/21) * Add workaround for JRuby crashing bug (#890) * Related to http://jira.codehaus.org/browse/JRUBY-7114 ### 0.9.12 (2013/02/12) #### Features * `pry --gem` (see 19bfc13aa) * `show-source` now works on commands created with `create_command` * `whereami` now has `-m` (method), `-c` (class), and `-f` (file) options * `show-source` now falls back to superclass (and displays warning) if it can't find class code * `show-source`/`show-doc` now indicate when `-a` option is available #### Bug fixes, etc. * Fix commands breaking due to Slop looking at `ARGV` instead of command parameters (#828) * Fix pager breaking in some situations (#845) * Fix broken rendering of some docs (#795) * Silence warnings during failed tab-completion attempts * Fix broken prompt when prompt is colored (#822 / #823) * Added `reload-method` as alias for `reload-code` (for backwards compatibility) * Reopen `Readline.output` if it is not a tty (see 1538bc0990) ### 0.9.11.4 (2013/01/20) * Fix pager not rendering color codes in some circumstances * Add `Pry.last_internal_error`, useful for devs debugging commands ### 0.9.11.3 (2013/01/17) * Fix `Pry.run_command` * Improve `ls` output * Add `:requires_gem => "jist"` to `gist` command (so dependencies can be installed via `install-command`) * Improve help for `edit` command ### 0.9.11.2 (2013/01/16) * Fix minor bug in `gist` on Windows: rescue `Jist::ClipboardError` rather than letting the scary error spill out to users and potentially having them think the gist didn't post. ### 0.9.11.1 (2013/01/16) * Fix minor bug in `gist` command where I neglected to remove a call to a non-existent method (`no_arg`) which was called when `gist` is invoked with no parameters ### 0.9.11 (2013/01/16) #### Dependency changes * Upgrade `slop` to `~> 3.4` * New optional dependency: `bond` * You'll need to perform `gem install bond` * It improves autocompletion if you use Readline * Does not work for libedit (More info: https://github.com/pry/pry/wiki/FAQ#wiki-readline) * Big thanks to cldwalker #### Features * Basic Ruby 2.0 support (#738) * JRuby 1.7.0+ support (#732) * New `reload-code` command * Reload code for methods, classes, commands, objects and so on * Examples: `reload-code MyClass`, `reload-code my_method`, `reload-code my_obj` * Bond tab completion (see "Dependency changes") * Consolidate "show" commands into `show-source` * `show-source` can now extract source for: * Classes * Methods * Procs * Pry commands * Arbitrary objects (it shows the source for the class of the object) * As a result, `show-command` is now removed * `gist`, `play`, and `save-file` now infer object type without requiring flags * Examples: `play MyClass`, `play my_file.rb`, `play my_method` * Consolidate editing commands into `edit` * `edit` can now edit: * Files * Methods * Classes * Pry commands * As a result, `edit-method` is now removed * Examples: `edit MyClass`, `edit my_file.rb`, `edit my_method` * `amend-line` and `play` now properly indent code added to input buffer * Support for multiple require switches (`pry -rubygems -r./a.rb`) (#674) * Support for multiple exec switches (`pry -e ':one' -e ':two'`) * Ability to customize the name displayed in the prompt (#695) * `--patch` switch for `edit --ex` command (#716) * Respect the `$PAGER` environment variable (#736) * `disable-pry` command (#497) * Two new hooks, `before_eval` and `after_eval` * Tab completion for `Array#` in `show-source` and `show-doc` * `gem-install` immediately requires gems * `-l` switch for `ls` command (displays local variables) * `gem-open` command * `fix-indent` command * Subcommands API * Public test API for plugin writers (see d1489a) * Tabular `ls` output * `--no-line-numbers` switch for `whereami` command * `--lines` switch for `play` command #### Bug fixes, etc. * Use single escape instead of double in `find-method` (#652) * Fix blank string delimiters (#657) * Fix unwanted `binding_impl_method` local in scratch bindings (#622) * Fix `edit-method -p` changing constant lookup (#645) * Fix `.pryrc` loading twice when invoked from `$HOME` directory (#682) * Fix Pry not remembering initial `pwd` (#675) * Fix multiline object coloring (#717) * Fix `show-method` not supporting `String::new` notation (#719) * Fix `whereami` command not showing correct line numbers (#754) * Fix buggy Cucumber AST output (#751) * Fix `while/until do` loops indentation (#787) * Fix `--no-plugins` switch (#526) * Ensure all errors go to the error handler (#774) * Fix `.pryrc` loading with wrong `__FILE__` * Fix pager not working if `less` is not available * Fix `^D` in nested REPL * Many small improvements to error message clarity and documentation formatting ### 0.9.10 (2012/07/04) #### Dependency changes * Upgrade `slop` to version 3 (#561) * Switch from `gist` gem to `jist` (#590) * Upgrade `method_source` to 0.8 #### Features * Add `--hist`, `-o` and `-k` flags to `gist` command (#572) * Support `show-source`/`show-doc` on methods defined in `class_eval` (#584) * Support `show-source`/`show-doc` on gem methods defined in C (#585) * Add `--disable-plugin` and `--select-plugin` options (#596) * Allow `cd -` to switch between bindings (#597) * Add `Pry.config.should_load_local_rc` to turn off `./.pryrc` (#612) * Allow running a file of Pry input with `pry ` * Support colours in `ri` command * Add `before_eval` hook * The prompt proc now gets a lot more data when its arity is 1 #### Bug fixes, etc. * Removed the `req` command (#554) * Fix rendering bugs when starting Pry (#567) * Fix `Array#pretty_print` on Jruby (#568) * Fix `edit` on Windows (#575) * Fix `find-method` in the presence of badly behaved objects (#576) * Fix `whereami` in ERb files on Rails (#580) * Raise fewer exceptions while tab completing (#632) * Don't immediately quit Pry when an error happens in Readline (#605) * Support for `ansicon` to give JRuby Windows users colour (#606) * Massive speed improvements to `show-source` for modules (#613) * Improve `whereami` command when not in a `binding.pry` (#620) * Support embedded documents (`=begin` ... `=end`) (#622) * Support editing files with spaces in the name (#627) * Renamed `__binding_impl__` to `__pry__` * Support for absolute paths in `$EDITOR` * Fix `cat` command on files with unknown extensions * Many, many internal refactorings and tidyings ### 0.9.9.6 (2012/05/09) * Fix `ZeroDivisionError` in `correct_indentation` (#558) ### 0.9.9.5 (2012/05/09) * Fix `ZeroDivisionError` in `correct_indentation` (#558) * Fix double highlighting in RDoc (#562) * Automatically create configuration for plugins (#548) ### 0.9.9.4 (2012/04/26) * Fix `NoMethodError: undefined method `winsize' for #>` (#549) * Fixes for JRuby * Fix syntax error on `exit` (550) * Heredoc content no longer auto-indented ### 0.9.9.3 (2012/04/19) * Fix `show-doc` failing on some core classes, like `Bignum` ### 0.9.9.2 (2012/04/18) * Make `correct_indentation`'s auto-colorization respect `Pry.color` ### 0.9.9.1 (2012/04/18) * Clear up confusion in `show-source`/`show-doc` docs * `-a` switch applies to classes as well as modules ### 0.9.9 (2012/04/18) #### New features * Lines of input are syntax highlighted upon Enter keypress * `show-source` command can now show class/module source code * Use `-a` to see all monkeypatches * Hard dependency on `ruby18_source_location` gem in MRI 1.8 * `show-doc` command can now show class/module docs * Use `-a` to see docs for all monkeypatches * Hard dependency on `ruby18_source_location` gem in MRI 1.8 * New `find-method` command * Performs a recursive search in a namespace for the existence of methods * Can find methods whose names match a regex or methods which contain provided code * This command is like a ruby-aware `grep`, very cool (thanks swarley) * [`pry-coolline`](https://github.com/pry/pry-coolline) now works properly * `alias_command` method now much more powerful * Example: `alias_command "lM", "ls -M"` * `whereami` is now more intelligent * Automatically shows entire source code of current method if current context is a method (thanks robgleeson) * New `raise-up` command * Allows you to raise an exception that will bubble out of pry (ending the session) and escape into enclosing program #### Bug fixes, etc. * Fixed crash when paging under Windows * Lines ending with `\` are incomplete (kudos to fowl) * `edit-method -n` no longer blocks (thanks misfo) * Show instance methods of modules by default in `ls` * Docs for REPL-defined methods can now be displayed using `show-doc` * Autoload `ruby18_source_location` on MRI 1.8, when available * See https://github.com/conradirwin/ruby18_source_location * Tab completion should work on first line now (historic bug fixed) * `:quiet => true` option added to `Pry.start`, turns off `whereami` * Another easter egg added * Show unloaded constants in yellow for `ls` * Improved documentation for `Pry.config` options * Improved auto-indentation * JRuby: heuristics used to clean up `ls` output * Fewer internal methods polluting output ### 0.9.8.4 (2012/6/3) * ~/.pry_history wasn't being created (if it did not exist)! FIXED * `hist --save` saved colors! FIXED * added Pry#add_sticky_local API for adding sticky locals to individual pry instances ### 0.9.8.3 (2012/3/2) * various tweaks to improve rbx support * commands now support optional block arguments * much improved help command * updated method_source dependency * added wtf command * jruby should now work in windows (though without color) ### 0.9.8.2 (2012/2/9) * fixed bugs related to --super * upgraded slop dependency * added edit -c (edit current line) * edit now respects Pry.config.disable_autoreload option ### 0.9.8.1 (2012/1/30) * fixed broken --no-plugins option * Ensure ARGV is not mutated during option parsing. * Use a more rbx-friendly test for unicodeness * Use rbx-{18,19}mode as indicated http://about.travis-ci.org/docs/user/languages/ruby/ * Don't explode in gem-list [Fixes #453, #454] * Check for command-name collision on assignment [Fixes #450] ### 0.9.8 (2012/1/25) MAJOR NEW FEATURES - upgraded command api, https://github.com/pry/pry/wiki/Custom-commands - added a system of hooks for customizing pry behaviour - changed syntax checking to use eval() for improved accuracy - added save-file command - added gist command (removed gist-method, new gist command is more general) complete CHANGELOG: * CommandError's no longer cause the current input to be discarded * Better syntax highlighting for rbx code code * added cat --in to show pry input history * prefixed temporary file names with 'pry' * show-doc now supports -l and -b options (line numbers) * play now supports -i and -d options * moved UserCommandAPI command-set to pry-developer_tools plugin * added :when_started event for hooks, called in Pry.start * added a man page * added rename method to Pry::CommandSet (commands can be renamed) * added CommandSet#{before_command,after_command} for enhancing builtin commands * added checking for namespace collisions with pry commands, set Pry.config.collision_warning * work around namespace collisions by ensuring lines starting with a space are executed as * ruby.work around namespace collisions by pressuring lines starting with a space are executed as ruby * added handlers for Ctrl+C (SIGINT) on jruby, these are now caught as in other ruby versions * removed dependency on ruby_parser * prevented colours leaking across the pry prompt * fixed edge cases in Pry::Method, for methods with crazy names and methods that have been 'undef'd * refactored history handling code for clarity and correctness * added Pry::WrappedModule as a counterpart to Pry::Method * made a trailing , cause pry to wait for further input * removed gist-method command, added gist command * added pry-backtrace command to show history of current session * fixed whereami within 'super' methods * replaced inline version guards by Pry::Helpers::BaseHelpers.{rbx?,jruby?,windows?} etc. * removed the CommandProcessor, its functionality is part of the new Command class * changed cd .. at the top level so it doesn't quit pry. * changed edit-command to no-longer need a command set argument * fixed empty lines so that they don't replace _ by nil * fixed SyntaxErrors at the REPL level so they don't replace _ex_. ### 0.9.7.4 (2011/11/5) * ls -M now works in modules (bugfix) * added exception message for bad cd object/path * no longer die when encounter exceptions in .pryrc * baked in CoolLine support * Pry.config.input in .pryrc now respected ### 0.9.7.3 (2011/10/28) * really fixed indentation for 'super if' and friends * Fixed indentation for tmux * added Pry.config.correct_indent option (to toggle whether indentation * corrected optional param behaviour for method signatures: e.g Signature meth(param1=?, param2=?) ### 0.9.7.2 (2011/10/27) * fixed indentation for 'super if' and 'ensure', 'next if', etc * refactored Pry#run_command so it can accept an eval_string parameter (so amend-line and so on can work with it) * changed ^D so it no longer resets indent level automatically ### 0.9.7.1 (2011/10/26) * fixed gem dependency issues ### 0.9.7 (2011/10/25) MAJOR NEW FEATURES: - upgraded ls command to have a more intuitive interface - added automatic indentation (thanks YorickPeterse!) - added Pry::Method wrapper class to encapsulate method-related functionality complete CHANGELOG: * fixed syntax highlighting for object literals * fixed ActiveSupport method-naming conflict with "in?" * added --super option to edit-method, show-method, and friends - making it easier to operate on superclass methods * officially added edit --in to open previous expressions in an editor * whereami now works for REPL-defined code * started using JRuby parser for input validation in JRuby (thanks pangloss!) * fixed bug where ~/.pryrc could be loaded more than once (thanks kelseyjudson!) * added parse_options! helper to pull option parsing out of commands * Pry now respects the terminal's input encoding * moved some requires out of the startup process for improved speed * added input_array info to DEFAULT_PROMPT, e.g [1] pry(main)> * added --no-history option to pry binary (prevent history being LOADED, history will still be saved) ### 0.9.6.2 (2011/9/27) * downgrading to CodeRay 0.9.8 due to problems with 1.0 and rails (autoloading problem) see #280 on pry and #6 on CodeRay * also added (as a minor feature) cirwin's implementation of edit --in * added early break/exit for objectpath errors (the 'cd 34/@hello/bad_path/23') ### 0.9.6 (2011/9/19) * restored previous behavior of command-line switches (allowing "-rfilename") * removed -p option (--play) from edit command * `edit` with no arguments now edits the current or most recent expression * `edit` auto-reloads .rb files (need to specify -n to suppress) * added -p option (--patch) to edit-method command, which allows monkeypatching methods without touching the original file * edit-method can now edit REPL-defined methods * cat --ex now works on exceptions in REPL-defined code * play -m now uses eval_string.replace() * play -m --open uses show-input to show play'd code * added "unindent" helper to make adding help to commands easier * local ./.pryrc now loaded after ~/.pryrc if it exists * cat --ex N and edit --ex N now can navigate through backtrace, where cat --ex (with no args) moves through successive levels of the backtrace automatically with state stored on the exception object itself * new option Pry.config.exception_window_size determines window size for cat --ex * input_stack now implemented - pushing objects onto a pry instance's input_stack causes the instance to read from those objects in turn as it encounters EOF on the previous object. On finishing the input_stack the input object for the pry instance is set back to Pry.config.input, if this fails, pry breaks out of the REPL (throw(:breakout)) with an error message * Pry.config.system() defines how pry runs system commands * now injecting target_self method into command scope * play now performs 'show-input' always unless eval_string contains a valid expression (i.e it's about to be eval'd) * play and hist --replay now push the current input object onto the input_stack before redirecting input to a StringIO (works much better with pry-remote now) ### 0.9.5 (2011/9/8) MAJOR NEW FEATURES: - JRuby support, including show-method/edit-method and editor integration on both 1.8 and 1.9 versions - extended cd syntax: cd ../@x/y - play command now works much better with _in_ array (this is a very powerful feature, esp with Pry::NAV_PROMPT) - history saving/loading is now lightning fast - 'edit' (entered by itself) now opens current lines in input buffer in an editor, and evals on exit - 'edit' command is also, in general more intelligent - ls output no longer in array format, and colors can be configured, e.g: Pry.config.ls.ivar_color = :bright_blue - new switch-to command for moving around the binding stack without exiting out of sessions - more sophisticated prompts, Pry::NAV_PROMPT to ease deep spelunking of code - major bug fix for windows systems - much better support for huge objects, should no longer hang pry (see #245) - cat --ex and edit --ex now work better complete CHANGELOG: * tempfile should end in .rb (for edit -t) * ls output should not be in array format * fix history saving (should not save all of Readline::HISTORY, but only what changed) * prevent blank lines going to Readline::HISTORY (thanks cirwin!) * ensure that cat --ex emulates the `whereami` format - includes line numbers and formatted the same, etc * fixed bug #200 ( https://github.com/pry/pry/issues/200 )- string interpolation bug (thanks to ryanf) * show-doc and stat now display method visibility (update WIKI) * got rid of warnings caused by stricter ruby 1.9.3 rules * remove interpolation of command names and fix interpolation error message (update WIKI) (thanks ryanf!) * 'nested sessions' now use binding stacks (so each instance manages its own collection of bindings without spawning other instances) * 'cd ..' just pops a binding off the binding_stack with special behaviour when only one binding in stack - it breaks out of the repl loop * added switch-to command (like jump-to but doesn't unwind the stack) * show-method and show-doc now accept multiple method names * control_d hook added (Pry.config.control_d_handler) * behaviour of ^d is now to break out of current expr if in multi-line expr, or break out of current context if nested, or break out of pry repl loop if at top-level * can no longer interpolate command name itself e.g #{x}-#{y} where x = "show" and y = "doc" * ^C no longer captured * got rid of Pry.active_instance, Pry.last_exception and friends. * also special locals now shared among bindings in a pry instance (i.e _ex_ (and friends) re-injected into new binding entered with 'cd') * renamed inp and out to _in_ and _out_ (to avoid collisions with actual locals in debugging scope) * added third parameter to prompts, the pry instance itself (_pry) see https://github.com/pry/pry/issues/233 for why it's important * cd behaviour when no args performs the same as `cd /` * commands with keep_retval can now return nil (to suppress output now return 'void' instead) * Pry::CommandProcessor::Result introduced * Pry.view_clip() modified to be more robust and properly display Class#name * edit command when invoked with no args now works like edit -t * when edit is invoked (with no args or with -t) inside a multi-line expression input buffer, it dumps that buffer into a temp file and takes you to it * got rid of Pry#null_input? since all that was needed was eval_string.empty? * cd command now supports complex syntax: cd ../@y/y/../z * JRuby is no longer a 2nd class citizen, almost full JRuby support, passing 100% tests * added Pry::NAV_PROMPT (great new navigation prompt, per robgleeson) and Pry::SIMPLE_PRINT for simple (IRB-style) print output (just using inspect) * _pry_ now passed as 3rd parameter to :before_session hook * ls colors now configurable via Pry.config.ls.local_var_color = :bright_red etc * ls separator configurable via, e.g Pry.config.ls.separator = " " * Pry.view_clip() now only calls inspect on a few immediates, otherwise uses the #<> syntax, which has been truncated further to exclude teh mem address, again related to #245 ### 0.9.3 (2011/7/27) * cat --ex (cats 5 lines above and below line in file where exception was raised) * edit --ex (edits line in file where exception was raised) * edit -t (opens a temporary file and evals it in current context when closed) * `pry -r` requires now happen after plugin loading (so as not to interfere with * new Pry.config.disable_auto_reload option, for turning off auto reloading by edit-method and related (thanks ryanf) * add better error messages for `cd` command * fixed exotic object regression - BasicObject.new etc now return "=> unknown" * added reload-method command (reloads the associated file of a method) * converted: import => import-set, version => pry-version, install => install-command * Pry.config.command_prefix support (thanks ryanf!) * fixed indentation for simple-prompt * hist command now excludes last line of input (the command invocation itself) * hist now has `history` alias * missing plugins no longer raise exception, just print a warning to $stderr * fixed jedit editor support ### 0.9.2 (2011/6/21) * fixed string interpolation bug (caused valid ruby code not to execute, sorry!) * fixed `ls` command, so it can properly display members of Object and classes, and BasicObject, etc * added a few git related commands to experimental command set, blame and diff ### 0.9.0 (2011/6/17) * plugin system * regex commands * show-method works on methods defined in REPL * new command system/API * rubinius core support * more backports to ruby 1.8 * inp/out special locals * _ex_ backtrace navigation object (_ex_.line, _ex_.file) * readline history saving/loading * prompt stack * more hooks * amend-line * play * show-input * edit * much more comprehensive test suite * support for new and old rubygems API * changed -s behaviour of ls (now excludes Object methods) * removed eval-file, lls, lcd, and a few other commands ### 0.7.6.1 (2011/3/26) * added slightly better support for YARD * now @param and @return tags are colored green and markdown `code` is syntax highlighted using coderay ### 0.7.6 (2011/3/26) * `whereami` command now accepts parameter AROUND, to display AROUND lines on eitherside of invocation line. * made it so `whereami` is invoked even if no method exists in current context (i.e in rspec tests) * added rubinius support for `whereami` invocation in HOOKS by checking for __unknown__.rb rather than just
### 0.7.0 (2011/3/15) * add pry-doc support with syntax highlighting for docs * add 'mj' option to ls (restrict to singleton methods) * add _ex_ local to hold last exception raised in an exception ### 0.6.8 (2011/3/6) * add whereami command, a la the `ir_b` gem * make whereami run at the start of every session * make .pryrc be loaded by run-time pry sessions ### 0.6.7 (2011/3/4) * color support * --simple-prompt for pry commandline * -I mode for pry commandline * --color mode for pry commandline * clean up requires (put them all in one place) * simple-prompt command and toggle-color command. ### 0.6.3 (2011/2/28) * Using MethodSource 0.3.4 so 1.8 show-method support provided * `Set` class added to list of classes that are inspected ### 0.6.1 (2011/2/26) * !@ command alias for exit_all * `cd /` for breaking out to pry top level (jump-to 0) * made `-e` option work in a more effective way for `pry` command line invocation * exit and exit-all commands now accept a parameter, this parameter becomes the return value of repl() * `command` method from CommandBase now accepts a :keep_retval arg that determines if command value is returned to pry session or just `nil` (`nil` was old behaviour) * tests for new :keep_retval and exit-all/exit behaviour; :keep_retval will remain undocumented. ### 0.5.8 (2011/2/22) * Added -c (context) option to show-doc, show-methods and eval-file * Fixed up ordering issue of -c and -r parameters to command line pry ### 0.5.7 (2011/2/21) * Added pry executable, auto-loads .pryrc in user's home directory, if it exists. ### 0.5.5 (2011/2/19) * Added Pry.run_command * More useful error messages * Easter eggs (game and cohen-poem) ### 0.5.0 (2011/2/17) * Use clipped version of Pry.view() for large objects * Exit Pry session on ^d * Use Shellwords for breaking up parameters to pry commands * Use OptionParser to parse options for default pry commands * Add version command * Refactor 'status' command: add current method info * Add meth_name_from_binding utility lambda to commands.rb * Add -M, -m, -v(erbose), -a(ll), -s(uper), -l(ocals), -i(ivars), -k(klass vars), etc options to ls * add -M(instance method) options to show-method and show-doc * add --help option to most commands * Get rid of ls_method and ls_imethods (subsumed by more powerful ls) * Get rid of show_idoc and show_imethod * Add special eval-file command that evals target file in current context ### 0.4.5 (2011/1/27) * fixed show_method (though fragile as it references __binding_impl__ directly, making a name change to that method difficult ### 0.4.4 (2011/1/27) * oops, added examples/ directory ### 0.4.3 (2011/1/26) * added alias_command and desc methods to Pry::CommandBase * changed behaviour of ls_methods and ls_imethods to return sorted lists of methods ### 0.4.1 (2011/1/23) * made it so a 'def meth;end' in an object Pry session defines singleton methods, not methods on the class (except in the case of immediates) * reorganized documentation, moving customization to a separate wiki file * storing wiki in a nested git repo, as github wiki pages have their own repo * added more tests for new method definition behaviour ### 0.4.0 (2011/1/21) * added command API * added many new commands, i.e ls_methods and friends * modified other commands * now accepts greater customization, can modify: input, output, hooks, prompt, print object * added tab completion (even completes commands) * added extensive tests * added examples * many more changes ### 0.1.3 (2010/12/9) * Got rid of rubygems dependency, refactored some code. ### 0.1.2 (2010/12/8) * now rescuing SyntaxError as well as Racc::Parser error in valid_expression? ### 0.1.0 (2010/12/8) * release! [v0.12.0]: https://github.com/pry/pry/releases/tag/v0.12.0 [v0.12.1]: https://github.com/pry/pry/releases/tag/v0.12.1 [v0.12.2]: https://github.com/pry/pry/releases/tag/v0.12.2 [v0.13.0]: https://github.com/pry/pry/releases/tag/v0.13.0 [v0.13.1]: https://github.com/pry/pry/releases/tag/v0.13.1 pry-0.13.1/Dockerfile000066400000000000000000000006161364454223300144110ustar00rootroot00000000000000FROM ubuntu:latest RUN apt-get update -y RUN apt-get install -y wget make git RUN wget --no-check-certificate -O ruby-install-0.4.3.tar.gz https://github.com/postmodern/ruby-install/archive/v0.4.3.tar.gz RUN tar -xzvf ruby-install-0.4.3.tar.gz RUN cd ruby-install-0.4.3 && make install RUN ruby-install ruby 1.9.3 RUN ruby-install ruby 2.1.1 RUN ruby-install ruby 2.1.2 ADD . /pry WORKDIR /pry pry-0.13.1/Gemfile000066400000000000000000000006751364454223300137170ustar00rootroot00000000000000# frozen_string_literal: true source 'https://rubygems.org' gemspec gem 'rake' gem 'yard' gem 'rspec', '~> 3.8.0' # TODO: unlock version when the bug is fixed: # https://github.com/rspec/rspec-expectations/issues/1113 gem 'rspec-expectations', '= 3.8.2' gem 'simplecov', '~> 0.16', require: false # Rubocop supports only >=2.2.0 if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.2.0') gem 'rubocop', '= 0.66.0', require: false end pry-0.13.1/LICENSE000066400000000000000000000021211364454223300134150ustar00rootroot00000000000000License ------- (The MIT License) Copyright (c) 2018 John Mair (banisterfiend) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. pry-0.13.1/README.md000066400000000000000000000335041364454223300137000ustar00rootroot00000000000000Pry === [![Circle Build Status](https://circleci.com/gh/pry/pry.svg?style=shield)](https://circleci.com/gh/pry/pry) [![Code Climate](https://codeclimate.com/github/pry/pry.svg)](https://codeclimate.com/github/pry/pry) [![Gem Version](https://badge.fury.io/rb/pry.svg)](https://badge.fury.io/rb/pry) [![Documentation Status](https://inch-ci.org/github/pry/pry.svg?branch=master)](https://inch-ci.org/github/pry/pry) [![Downloads](https://img.shields.io/gem/dt/pry.svg?style=flat)](https://rubygems.org/gems/pry) ![Pry logo](https://www.dropbox.com/s/zp8o63kquby2rln/pry_logo_350.png?raw=1) © John Mair ([banisterfiend](https://twitter.com/banisterfiend)) 2018
(Creator) © Kyrylo Silin ([kyrylosilin](https://twitter.com/kyrylosilin)) 2018
(Maintainer) **Alumni:** * Conrad Irwin * Ryan Fitzgerald * Robert Gleeson **Links:** * https://pryrepl.org/ * [YARD API documentation](https://www.rubydoc.info/gems/pry) * [Wiki](https://github.com/pry/pry/wiki) Table of Contents ================= * [Introduction](#introduction) * [Key features](#key-features) * [Installation](#installation) * [Overview](#overview) * [Commands](#commands) * [Navigating around state](#navigating-around-state) * [Runtime invocation](#runtime-invocation) * [Command Shell Integration](#command-shell-integration) * [Code Browsing](#code-browsing) * [Documentation Browsing](#documentation-browsing) * [Gist integration](#gist-integration) * [Edit methods](#edit-methods) * [Live Help System](#live-help-system) * [Use Pry as your Rails Console](#use-pry-as-your-rails-console) * [Syntax Highlighting](#syntax-highlighting) * [Supported Rubies](#supported-rubies) * [Contact](#contact) * [License](#license) * [Contributors](#contributors) Introduction ------------ Pry is a runtime developer console and IRB alternative with powerful introspection capabilities. Pry aims to be more than an IRB replacement. It is an attempt to bring REPL driven programming to the Ruby language. Key features ------------ * Source code browsing (including core C source with the pry-doc gem) * Documentation browsing * Live help system * Open methods in editors (`edit Class#method`) * Syntax highlighting * Command shell integration (start editors, run git, and rake from within Pry) * Gist integration * Navigation around state (`cd`, `ls` and friends) * Runtime invocation (use Pry as a developer console or debugger) * Exotic object support (BasicObject instances, IClasses, ...) * A powerful and flexible command system * Ability to view and replay history * Many convenience commands inspired by IPython, Smalltalk and other advanced REPLs * A wide-range number of [plugins](https://github.com/pry/pry/wiki/Available-plugins) that provide remote sessions, full debugging functionality, and more. Installation ------------ ### Bundler ```ruby gem 'pry', '~> 0.12.2' ``` ### Manual ```sh gem install pry ``` Overview -------- Pry is fairly flexible and allows significant user [customization](https://github.com/pry/pry/wiki/Customization-and-configuration). It is trivial to read from any object that has a `readline` method and write to any object that has a `puts` method. Many other aspects of Pry are also configurable, making it a good choice for implementing custom shells. Pry comes with an executable so it can be invoked at the command line. Just enter `pry` to start. A `pryrc` file in `$XDG_CONFIG_HOME/pry/` or the user's home directory will be loaded if it exists. Type `pry --help` at the command line for more information. ### Commands Nearly every piece of functionality in a Pry session is implemented as a command. Commands are not methods and must start at the beginning of a line, with no whitespace in between. Commands support a flexible syntax and allow 'options' in the same way as shell commands, for example the following Pry command will show a list of all private instance methods (in scope) that begin with 'pa' ```ruby pry(YARD::Parser::SourceParser):5> ls -Mp --grep ^pa YARD::Parser::SourceParser#methods: parse parser_class parser_type parser_type= parser_type_for_filename ``` ### Navigating around state Pry allows us to pop in and out of different scopes (objects) using the `cd` command. This enables us to explore the run-time view of a program or library. To view which variables and methods are available within a particular scope we use the versatile [ls command.](https://gist.github.com/c0fc686ef923c8b87715) Here we will begin Pry at top-level, then Pry on a class and then on an instance variable inside that class: ```ruby pry(main)> class Hello pry(main)* @x = 20 pry(main)* end => 20 pry(main)> cd Hello pry(Hello):1> ls -i instance variables: @x pry(Hello):1> cd @x pry(20):2> self + 10 => 30 pry(20):2> cd .. pry(Hello):1> cd .. pry(main)> cd .. ``` The number after the `:` in the pry prompt indicates the nesting level. To display more information about nesting, use the `nesting` command. E.g ```ruby pry("friend"):3> nesting Nesting status: 0. main (Pry top level) 1. Hello 2. 100 3. "friend" => nil ``` We can then jump back to any of the previous nesting levels by using the `jump-to` command: ```ruby pry("friend"):3> jump-to 1 => 100 pry(Hello):1> ``` ### Runtime invocation Pry can be invoked in the middle of a running program. It opens a Pry session at the point it's called and makes all program state at that point available. It can be invoked on any object using the `my_object.pry` syntax or on the current binding (or any binding) using `binding.pry`. The Pry session will then begin within the scope of the object (or binding). When the session ends the program continues with any modifications you made to it. This functionality can be used for such things as: debugging, implementing developer consoles and applying hot patches. code: ```ruby # test.rb require 'pry' class A def hello() puts "hello world!" end end a = A.new # start a REPL session binding.pry # program resumes here (after pry session) puts "program resumes here." ``` Pry session: ```ruby pry(main)> a.hello hello world! => nil pry(main)> def a.goodbye pry(main)* puts "goodbye cruel world!" pry(main)* end => nil pry(main)> a.goodbye goodbye cruel world! => nil pry(main)> exit program resumes here. ``` ### Command Shell Integration A line of input that begins with a '.' will be forwarded to the command shell. This enables us to navigate the file system, spawn editors, and run git and rake directly from within Pry. Further, we can use the `shell-mode` command to incorporate the present working directory into the Pry prompt and bring in (limited at this stage, sorry) file name completion. We can also interpolate Ruby code directly into the shell by using the normal `#{}` string interpolation syntax. In the code below we're going to switch to `shell-mode` and edit the `pryrc` file. We'll then cat its contents and reload the file. ```ruby pry(main)> shell-mode pry main:/home/john/ruby/projects/pry $ .cd ~ pry main:/home/john $ .emacsclient .pryrc pry main:/home/john $ .cat .pryrc def hello_world puts "hello world!" end pry main:/home/john $ load ".pryrc" => true pry main:/home/john $ hello_world hello world! ``` We can also interpolate Ruby code into the shell. In the example below we use the shell command `cat` on a random file from the current directory and count the number of lines in that file with `wc`: ```ruby pry main:/home/john $ .cat #{Dir['*.*'].sample} | wc -l 44 ``` ### Code Browsing You can browse method source code with the `show-source` command. Nearly all Ruby methods (and some C methods, with the pry-doc gem) can have their source viewed. Code that is longer than a page is sent through a pager (such as less), and all code is properly syntax highlighted (even C code). The `show-source` command accepts two syntaxes, the typical ri `Class#method` syntax and also simply the name of a method that's in scope. You can optionally pass the `-l` option to `show-source` to include line numbers in the output. In the following example we will enter the `Pry` class, list the instance methods beginning with 're' and display the source code for the `rep` method: ```ruby pry(main)> cd Pry pry(Pry):1> ls -M --grep re Pry#methods: re readline refresh rep repl repl_epilogue repl_prologue retrieve_line pry(Pry):1> show-source rep -l From: /home/john/ruby/projects/pry/lib/pry/pry_instance.rb:143 Number of lines: 6 143: def rep(target=TOPLEVEL_BINDING) 144: target = Pry.binding_for(target) 145: result = re(target) 146: 147: show_result(result) if should_print? 148: end ``` Note that we can also view C methods (from Ruby Core) using the `pry-doc` plugin; we also show off the alternate syntax for `show-source`: ```ruby pry(main)> show-source Array#select From: array.c in Ruby Core (C Method): Number of lines: 15 static VALUE rb_ary_select(VALUE ary) { VALUE result; long i; RETURN_ENUMERATOR(ary, 0, 0); result = rb_ary_new2(RARRAY_LEN(ary)); for (i = 0; i < RARRAY_LEN(ary); i++) { if (RTEST(rb_yield(RARRAY_PTR(ary)[i]))) { rb_ary_push(result, rb_ary_elt(ary, i)); } } return result; } ``` ### Documentation Browsing One use-case for Pry is to explore a program at run-time by `cd`-ing in and out of objects and viewing and invoking methods. In the course of exploring it may be useful to read the documentation for a specific method that you come across. Like `show-source` the `show-doc` command supports two syntaxes - the normal `ri` syntax as well as accepting the name of any method that is currently in scope. The Pry documentation system does not rely on pre-generated `rdoc` or `ri`, instead it grabs the comments directly above the method on demand. This results in speedier documentation retrieval and allows the Pry system to retrieve documentation for methods that would not be picked up by `rdoc`. Pry also has a basic understanding of both the rdoc and yard formats and will attempt to syntax highlight the documentation appropriately. Nonetheless, the `ri` functionality is very good and has an advantage over Pry's system in that it allows documentation lookup for classes as well as methods. Pry therefore has good integration with `ri` through the `ri` command. The syntax for the command is exactly as it would be in command-line - so it is not necessary to quote strings. In our example we will enter the `Gem` class and view the documentation for the `try_activate` method: ```ruby pry(main)> cd Gem pry(Gem):1> show-doc try_activate From: /Users/john/.rvm/rubies/ruby-1.9.2-p180/lib/ruby/site_ruby/1.9.1/rubygems.rb:201 Number of lines: 3 Try to activate a gem containing path. Returns true if activation succeeded or wasn't needed because it was already activated. Returns false if it can't find the path in a gem. pry(Gem):1> ``` We can also use `ri` in the normal way: ```ruby pry(main) ri Array#each ----------------------------------------------------------- Array#each array.each {|item| block } -> array ------------------------------------------------------------------------ Calls _block_ once for each element in _self_, passing that element as a parameter. a = [ "a", "b", "c" ] a.each {|x| print x, " -- " } produces: a -- b -- c -- ``` ### Edit methods You can use `edit Class#method` or `edit my_method` (if the method is in scope) to open a method for editing directly in your favorite editor. Pry has knowledge of a few different editors and will attempt to open the file at the line the method is defined. You can set the editor to use by assigning to the `Pry.editor` accessor. `Pry.editor` will default to `$EDITOR` or failing that will use `nano` as the backup default. The file that is edited will be automatically reloaded after exiting the editor - reloading can be suppressed by passing the `--no-reload` option to `edit` In the example below we will set our default editor to "emacsclient" and open the `Pry#repl` method for editing: ```ruby pry(main)> Pry.editor = "emacsclient" pry(main)> edit Pry#repl ``` ### Live Help System Many other commands are available in Pry; to see the full list type `help` at the prompt. A short description of each command is provided with basic instructions for use; some commands have a more extensive help that can be accessed via typing `command_name --help`. A command will typically say in its description if the `--help` option is available. ### Use Pry as your Rails Console The recommended way to use Pry as your Rails console is to add [the `pry-rails` gem](https://github.com/rweng/pry-rails) to your Gemfile. This replaces the default console with Pry, in addition to loading the Rails console helpers and adding some useful Rails-specific commands. If you don't want to change your Gemfile, you can still run a Pry console in your app's environment using Pry's `-r` flag: ```sh pry -r ./config/environment ``` Also check out the [wiki](https://github.com/pry/pry/wiki/Setting-up-Rails-or-Heroku-to-use-Pry) for more information about integrating Pry with Rails. ### Syntax Highlighting Syntax highlighting is on by default in Pry. If you want to change the colors, check out the [pry-theme](https://github.com/kyrylo/pry-theme) gem. You can toggle the syntax highlighting on and off in a session by using the `toggle-color` command. Alternatively, you can turn it off permanently by putting the line `Pry.color = false` in your `pryrc` file. Supported Rubies ---------------- * CRuby >= 1.9.3 * JRuby >= 1.7 Contact ------- In case you have a problem, question or a bug report, feel free to: * ask a question on IRC (#pry on Freenode) * [file an issue](https://github.com/pry/pry/issues) * [tweet at us](https://twitter.com/pryruby) License ------- The project uses the MIT License. See LICENSE.md for details. Contributors ------------ Pry is primarily the work of [John Mair (banisterfiend)](https://github.com/banister), for full list of contributors see the [contributors graph](https://github.com/pry/pry/graphs/contributors). pry-0.13.1/Rakefile000066400000000000000000000051761364454223300140720ustar00rootroot00000000000000# frozen_string_literal: true require 'rake/clean' require 'rubygems/package_task' $LOAD_PATH.unshift 'lib' require 'pry/version' CLOBBER.include('**/*~', '**/*#*', '**/*.log') CLEAN.include( '**/*#*', '**/*#*.*', '**/*_flymake*.*', '**/*_flymake', '**/*.rbc', '**/.#*.*' ) require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) task default: :spec desc "Run pry (you can pass arguments using _ in place of -)" task :pry do ARGV.shift if ARGV.first == "pry" ARGV.map! do |arg| arg.sub(/^_*/) { |m| "-" * m.size } end load 'bin/pry' end desc "Show pry version." task :version do puts "Pry version: #{Pry::VERSION}" end desc "Profile pry's startup time" task :profile do require 'profile' require 'pry' Pry.start(TOPLEVEL_BINDING, input: StringIO.new('exit')) end def modify_base_gemspec # rubocop:disable Security/Eval eval(File.read('pry.gemspec')).tap { |s| yield s } # rubocop:enable Security/Eval end namespace :ruby do spec = modify_base_gemspec do |s| s.platform = Gem::Platform::RUBY end Gem::PackageTask.new(spec) do |pkg| pkg.need_zip = false pkg.need_tar = false end end namespace :jruby do spec = modify_base_gemspec do |s| s.add_dependency('spoon', '~> 0.0') s.platform = 'java' end Gem::PackageTask.new(spec) do |pkg| pkg.need_zip = false pkg.need_tar = false end end %w[mswin32 mingw32].each do |platform| namespace platform do spec = modify_base_gemspec do |s| s.add_dependency('win32console', '~> 1.3') s.platform = Gem::Platform.new ['universal', platform, nil] end Gem::PackageTask.new(spec) do |pkg| pkg.need_zip = false pkg.need_tar = false end end task gems: "#{platform}:gem" end desc "build all platform gems at once" task gems: [:clean, :rmgems, 'ruby:gem', 'jruby:gem'] desc "remove all platform gems" task rmgems: ['ruby:clobber_package'] task rm_gems: :rmgems task rm_pkgs: :rmgems desc "reinstall gem" task reinstall: :gems do begin sh "gem uninstall pry" rescue StandardError nil end sh "gem install #{File.dirname(__FILE__)}/pkg/pry-#{Pry::VERSION}.gem" end task install: :reinstall desc "build and push latest gems" task pushgems: :gems do chdir("#{File.dirname(__FILE__)}/pkg") do Dir["*.gem"].each do |gemfile| sh "gem push #{gemfile}" end end end namespace :docker do desc "build a docker container with multiple rubies" task :build do system "docker build -t pry/pry ." end desc "test pry on multiple ruby versions" task test: :build do system( "docker run -i -t -v /tmp/prytmp:/tmp/prytmp pry/pry ./multi_test_inside_docker.sh" ) end end pry-0.13.1/bin/000077500000000000000000000000001364454223300131645ustar00rootroot00000000000000pry-0.13.1/bin/pry000077500000000000000000000003331364454223300137230ustar00rootroot00000000000000#!/usr/bin/env ruby # frozen_string_literal: true # (C) John Mair (banisterfiend) # MIT license $0 = 'pry' require 'pry' # Process command line options and run Pry opts = Pry::CLI.parse_options Pry::CLI.start(opts) pry-0.13.1/lib/000077500000000000000000000000001364454223300131625ustar00rootroot00000000000000pry-0.13.1/lib/pry.rb000066400000000000000000000076471364454223300143370ustar00rootroot00000000000000# frozen_string_literal: true # (C) John Mair (banisterfiend) 2016 # MIT License require 'pry/version' require 'pry/last_exception' require 'pry/forwardable' require 'pry/helpers/base_helpers' require 'pry/helpers/documentation_helpers' require 'pry/helpers' require 'pry/basic_object' require 'pry/prompt' require 'pry/plugins' require 'pry/code_object' require 'pry/exceptions' require 'pry/hooks' require 'pry/input_completer' require 'pry/command' require 'pry/class_command' require 'pry/block_command' require 'pry/command_set' require 'pry/syntax_highlighter' require 'pry/editor' require 'pry/history' require 'pry/color_printer' require 'pry/exception_handler' require 'pry/system_command_handler' require 'pry/control_d_handler' require 'pry/command_state' require 'pry/warning' require 'pry/env' Pry::Commands = Pry::CommandSet.new unless defined?(Pry::Commands) require 'pry/commands/ls/jruby_hacks' require 'pry/commands/ls/methods_helper' require 'pry/commands/ls/interrogatable' require 'pry/commands/ls/grep' require 'pry/commands/ls/formatter' require 'pry/commands/ls/globals' require 'pry/commands/ls/constants' require 'pry/commands/ls/methods' require 'pry/commands/ls/self_methods' require 'pry/commands/ls/instance_vars' require 'pry/commands/ls/local_names' require 'pry/commands/ls/local_vars' require 'pry/commands/ls/interrogatable' require 'pry/commands/ls/ls_entity' require 'pry/commands/ls/methods_helper' require 'pry/commands/ls' require 'pry/config/attributable' require 'pry/config/value' require 'pry/config/memoized_value' require 'pry/config/lazy_value' require 'pry/config' require 'pry/pry_class' require 'pry/pry_instance' require 'pry/inspector' require 'pry/pager' require 'pry/indent' require 'pry/object_path' require 'pry/output' require 'pry/input_lock' require 'pry/repl' require 'pry/code' require 'pry/ring' require 'pry/method' require 'pry/wrapped_module' require 'pry/wrapped_module/candidate' require 'pry/slop' require 'pry/cli' require 'pry/core_extensions' require 'pry/repl_file_loader' require 'pry/code/loc' require 'pry/code/code_range' require 'pry/code/code_file' require 'pry/method/weird_method_locator' require 'pry/method/disowned' require 'pry/method/patcher' require 'pry/commands/amend_line' require 'pry/commands/bang' require 'pry/commands/bang_pry' require 'pry/commands/cat' require 'pry/commands/cat/abstract_formatter.rb' require 'pry/commands/cat/input_expression_formatter.rb' require 'pry/commands/cat/exception_formatter.rb' require 'pry/commands/cat/file_formatter.rb' require 'pry/commands/cd' require 'pry/commands/change_inspector' require 'pry/commands/change_prompt' require 'pry/commands/clear_screen' require 'pry/commands/code_collector' require 'pry/commands/disable_pry' require 'pry/commands/easter_eggs' require 'pry/commands/edit' require 'pry/commands/edit/exception_patcher' require 'pry/commands/edit/file_and_line_locator' require 'pry/commands/exit' require 'pry/commands/exit_all' require 'pry/commands/exit_program' require 'pry/commands/find_method' require 'pry/commands/fix_indent' require 'pry/commands/help' require 'pry/commands/hist' require 'pry/commands/import_set' require 'pry/commands/jump_to' require 'pry/commands/list_inspectors' require 'pry/commands/nesting' require 'pry/commands/play' require 'pry/commands/pry_backtrace' require 'pry/commands/pry_version' require 'pry/commands/raise_up' require 'pry/commands/reload_code' require 'pry/commands/reset' require 'pry/commands/ri' require 'pry/commands/save_file' require 'pry/commands/shell_command' require 'pry/commands/shell_mode' require 'pry/commands/show_info' require 'pry/commands/show_doc' require 'pry/commands/show_input' require 'pry/commands/show_source' require 'pry/commands/stat' require 'pry/commands/switch_to' require 'pry/commands/toggle_color' require 'pry/commands/watch_expression' require 'pry/commands/watch_expression/expression.rb' require 'pry/commands/whereami' require 'pry/commands/wtf' pry-0.13.1/lib/pry/000077500000000000000000000000001364454223300137745ustar00rootroot00000000000000pry-0.13.1/lib/pry/basic_object.rb000066400000000000000000000003551364454223300167330ustar00rootroot00000000000000# frozen_string_literal: true class Pry class BasicObject < BasicObject [:Kernel, :File, :Dir, :LoadError, :ENV, :Pry].each do |constant| const_set constant, ::Object.const_get(constant) end include Kernel end end pry-0.13.1/lib/pry/block_command.rb000066400000000000000000000012441364454223300171120ustar00rootroot00000000000000# frozen_string_literal: true class Pry # A super-class for Commands that are created with a single block. # # This class ensures that the block is called with the correct number of # arguments and the right context. # # Create subclasses using {Pry::CommandSet#command}. class BlockCommand < Command # Call the block that was registered with this command. # @param [Array] args The arguments passed # @return [Object] The return value of the block def call(*args) instance_exec(*normalize_method_args(block, args), &block) end def help "#{command_options[:listing].to_s.ljust(18)} #{description}" end end end pry-0.13.1/lib/pry/class_command.rb000066400000000000000000000131721364454223300171300ustar00rootroot00000000000000# frozen_string_literal: true class Pry # A super-class of Commands with structure. # # This class implements the bare-minimum functionality that a command should # have, namely a --help switch, and then delegates actual processing to its # subclasses. # # Create subclasses using {Pry::CommandSet#create_command}, and override the # `options(opt)` method to set up an instance of Pry::Slop, and the `process` # method to actually run the command. If necessary, you can also override # `setup` which will be called before `options`, for example to require any # gems your command needs to run, or to set up state. class ClassCommand < Command class << self # Ensure that subclasses inherit the options, description and # match from a ClassCommand super class. def inherited(klass) klass.match match klass.description description klass.command_options options end def source source_object.source end def doc new.help end def source_location source_object.source_location end def source_file source_object.source_file end alias file source_file def source_line source_object.source_line end alias line source_line private # The object used to extract the source for the command. # # This should be a `Pry::Method(block)` for a command made with `create_command` # and a `Pry::WrappedModule(self)` for a command that's a standard class. # @return [Pry::WrappedModule, Pry::Method] def source_object @source_object ||= if name =~ /^[A-Z]/ Pry::WrappedModule(self) else Pry::Method(block) end end end attr_accessor :opts attr_accessor :args # Set up `opts` and `args`, and then call `process`. # # This method will display help if necessary. # # @param [Array] args The arguments passed # @return [Object] The return value of `process` or VOID_VALUE def call(*args) setup self.opts = slop self.args = opts.parse!(args) if opts.present?(:help) output.puts slop.help void else process(*normalize_method_args(method(:process), args)) end end # Return the help generated by Pry::Slop for this command. def help slop.help end # Return an instance of Pry::Slop that can parse either subcommands or the # options that this command accepts. def slop Pry::Slop.new do |opt| opt.banner(unindent(self.class.banner)) subcommands(opt) options(opt) opt.on :h, :help, 'Show this message.' end end # Generate shell completions # @param [String] search The line typed so far # @return [Array] the words to complete def complete(search) slop.flat_map do |opt| [opt.long && "--#{opt.long} " || opt.short && "-#{opt.short}"] end.compact + super end # A method called just before `options(opt)` as part of `call`. # # This method can be used to set up any context your command needs to run, # for example requiring gems, or setting default values for options. # # @example # def setup # require 'gist' # @action = :method # end def setup; end # A method to setup Pry::Slop commands so it can parse the subcommands your # command expects. If you need to set up default values, use `setup` # instead. # # @example A minimal example # def subcommands(cmd) # cmd.command :download do |opt| # description 'Downloads a content from a server' # # opt.on :verbose, 'Use verbose output' # # run do |options, arguments| # ContentDownloader.download(options, arguments) # end # end # end # # @example Define the invokation block anywhere you want # def subcommands(cmd) # cmd.command :download do |opt| # description 'Downloads a content from a server' # # opt.on :verbose, 'Use verbose output' # end # end # # def process # # Perform calculations... # opts.fetch_command(:download).run do |options, arguments| # ContentDownloader.download(options, arguments) # end # # More calculations... # end def subcommands(cmd); end # A method to setup Pry::Slop so it can parse the options your command expects. # # @note Please don't do anything side-effecty in the main part of this # method, as it may be called by Pry at any time for introspection reasons. # If you need to set up default values, use `setup` instead. # # @example # def options(opt) # opt.banner "Gists methods or classes" # opt.on(:c, :class, "gist a class") do # @action = :class # end # end def options(opt); end # The actual body of your command should go here. # # The `opts` mehod can be called to get the options that Pry::Slop has passed, # and `args` gives the remaining, unparsed arguments. # # The return value of this method is discarded unless the command was # created with `:keep_retval => true`, in which case it is returned to the # repl. # # @example # def process # if opts.present?(:class) # gist_class # else # gist_method # end # end def process raise CommandError, "command '#{command_name}' not implemented" end end end pry-0.13.1/lib/pry/cli.rb000066400000000000000000000137771364454223300151070ustar00rootroot00000000000000# frozen_string_literal: true require 'stringio' class Pry # Manage the processing of command line options class CLI NoOptionsError = Class.new(StandardError) class << self # @return [Proc] The Proc defining the valid command line options. attr_accessor :options # @return [Array] The Procs that process the parsed options. Plugins can # utilize this facility in order to add and process their own Pry # options. attr_accessor :option_processors # @return [Array] The input array of strings to process # as CLI options. attr_accessor :input_args # Add another set of CLI options (a Pry::Slop block) def add_options(&block) if options old_options = options self.options = proc do instance_exec(&old_options) instance_exec(&block) end else self.options = block end self end # Bring in options defined in plugins def add_plugin_options Pry.plugins.values.each(&:load_cli_options) self end # Add a block responsible for processing parsed options. def add_option_processor(&block) self.option_processors ||= [] option_processors << block self end # Clear `options` and `option_processors` def reset self.options = nil self.option_processors = nil end def parse_options(args = ARGV) unless options raise NoOptionsError, "No command line options defined! Use Pry::CLI.add_options to " \ "add command line options." end @pass_argv = args.index { |cli_arg| %w[- --].include?(cli_arg) } if @pass_argv slop_args = args[0...@pass_argv] self.input_args = args.replace(args[@pass_argv + 1..-1]) else self.input_args = slop_args = args end begin opts = Pry::Slop.parse!( slop_args, help: true, multiple_switches: false, strict: true, &options ) rescue Pry::Slop::InvalidOptionError # Display help message on unknown switches and exit. puts Pry::Slop.new(&options) Kernel.exit end Pry.initial_session_setup Pry.final_session_setup # Option processors are optional. option_processors.each { |processor| processor.call(opts) } if option_processors opts end def start(opts) Kernel.exit if opts.help? # invoked via cli Pry.cli = true # create the actual context if opts[:context] Pry.initial_session_setup context = Pry.binding_for(eval(opts[:context])) # rubocop:disable Security/Eval Pry.final_session_setup else context = Pry.toplevel_binding end if !@pass_argv && Pry::CLI.input_args.any? && Pry::CLI.input_args != ["pry"] full_name = File.expand_path(Pry::CLI.input_args.first) Pry.load_file_through_repl(full_name) Kernel.exit end # Start the session (running any code passed with -e, if there is any) Pry.start(context, input: StringIO.new(Pry.config.exec_string)) end end reset end end # Bring in options defined by plugins Pry::Slop.new do on "no-plugins" do Pry.config.should_load_plugins = false end end.parse(ARGV.dup) Pry::CLI.add_plugin_options if Pry.config.should_load_plugins # The default Pry command line options (before plugin options are included) Pry::CLI.add_options do banner( "Usage: pry [OPTIONS]\n" \ "Start a Pry session.\n" \ "See http://pryrepl.org/ for more information.\n" \ "Copyright (c) 2016 John Mair (banisterfiend)" \ ) on( :e, :exec=, "A line of code to execute in context before the session starts" ) do |input| Pry.config.exec_string += "\n" unless Pry.config.exec_string.empty? Pry.config.exec_string += input end on "no-pager", "Disable pager for long output" do Pry.config.pager = false end on "no-history", "Disable history loading" do Pry.config.history.should_load = false end on "no-color", "Disable syntax highlighting for session" do Pry.config.color = false end on :f, "Suppress loading of pryrc" do Pry.config.should_load_rc = false Pry.config.should_load_local_rc = false end on :s, "select-plugin=", "Only load specified plugin (and no others)." do |plugin_name| Pry.config.should_load_plugins = false Pry.plugins[plugin_name].activate! end on :d, "disable-plugin=", "Disable a specific plugin." do |plugin_name| Pry.plugins[plugin_name].disable! end on "no-plugins", "Suppress loading of plugins." do Pry.config.should_load_plugins = false end on "plugins", "List installed plugins." do puts "Installed Plugins:" puts "--" Pry.locate_plugins.each do |plugin| puts plugin.name.to_s.ljust(18) << plugin.spec.summary end Kernel.exit end on "simple-prompt", "Enable simple prompt mode" do Pry.config.prompt = Pry::Prompt[:simple] end on "noprompt", "No prompt mode" do Pry.config.prompt = Pry::Prompt[:none] end on :r, :require=, "`require` a Ruby script at startup" do |file| Pry.config.requires << file end on(:I=, "Add a path to the $LOAD_PATH", as: Array, delimiter: ":") do |load_path| load_path.map! do |path| %r{\A\./} =~ path ? path : File.expand_path(path) end $LOAD_PATH.unshift(*load_path) end on "gem", "Shorthand for -I./lib -rgemname" do |_load_path| $LOAD_PATH.unshift("./lib") Dir["./lib/*.rb"].each do |file| Pry.config.requires << file end end on :v, :version, "Display the Pry version" do puts "Pry version #{Pry::VERSION} on Ruby #{RUBY_VERSION}" Kernel.exit end on :c, :context=, "Start the session in the specified context. Equivalent to " \ "`context.pry` in a session.", default: "Pry.toplevel_binding" end pry-0.13.1/lib/pry/code.rb000066400000000000000000000251751364454223300152450ustar00rootroot00000000000000# frozen_string_literal: true require 'method_source' class Pry class << self # Convert the given object into an instance of `Pry::Code`, if it isn't # already one. # # @param [Code, Method, UnboundMethod, Proc, Pry::Method, String, Array, # IO] obj def Code(obj) case obj when Code obj when ::Method, UnboundMethod, Proc, Pry::Method Code.from_method(obj) else Code.new(obj) end end end # `Pry::Code` is a class that encapsulates lines of source code and their # line numbers and formats them for terminal output. It can read from a file # or method definition or be instantiated with a `String` or an `Array`. # # In general, the formatting methods in `Code` return a new `Code` object # which will format the text as specified when `#to_s` is called. This allows # arbitrary chaining of formatting methods without mutating the original # object. class Code class << self include MethodSource::CodeHelpers # Instantiate a `Code` object containing code loaded from a file or # Pry's line buffer. # # @param [String] filename The name of a file, or "(pry)". # @param [Symbol] code_type The type of code the file contains. # @return [Code] def from_file(filename, code_type = nil) code_file = CodeFile.new(filename, code_type) new(code_file.code, 1, code_file.code_type) end # Instantiate a `Code` object containing code extracted from a # `::Method`, `UnboundMethod`, `Proc`, or `Pry::Method` object. # # @param [::Method, UnboundMethod, Proc, Pry::Method] meth The method # object. # @param [Integer, nil] start_line The line number to start on, or nil to # use the method's original line numbers. # @return [Code] def from_method(meth, start_line = nil) meth = Pry::Method(meth) start_line ||= meth.source_line || 1 new(meth.source, start_line, meth.source_type) end # Attempt to extract the source code for module (or class) `mod`. # # @param [Module, Class] mod The module (or class) of interest. # @param [Integer] candidate_rank The module candidate (by rank) # to use (see `Pry::WrappedModule::Candidate` for more information). # @param [Integer, nil] start_line The line number to start on, or nil to # use the method's original line numbers. # @return [Code] def from_module(mod, candidate_rank = 0, start_line = nil) candidate = Pry::WrappedModule(mod).candidate(candidate_rank) start_line ||= candidate.line new(candidate.source, start_line, :ruby) end end # @return [Symbol] The type of code stored in this wrapper. attr_accessor :code_type # Instantiate a `Code` object containing code from the given `Array`, # `String`, or `IO`. The first line will be line 1 unless specified # otherwise. If you need non-contiguous line numbers, you can create an # empty `Code` object and then use `#push` to insert the lines. # # @param [Array, String, IO] lines # @param [Integer?] start_line # @param [Symbol?] code_type def initialize(lines = [], start_line = 1, code_type = :ruby) lines = lines.lines if lines.is_a? String @lines = lines.each_with_index.map do |line, lineno| LOC.new(line, lineno + start_line.to_i) end @code_type = code_type @with_marker = @with_indentation = @with_line_numbers = nil end # Append the given line. +lineno+ is one more than the last existing # line, unless specified otherwise. # # @param [String] line # @return [void] def push(line) line_number = @lines.any? ? @lines.last.lineno + 1 : 1 @lines.push(LOC.new(line, line_number)) end alias << push # Filter the lines using the given block. # # @yield [LOC] # @return [Code] def select(&block) alter do @lines = @lines.select(&block) end end # Filter the lines using the given block. # # @yield [LOC] # @return [Code] def reject(&block) alter do @lines = @lines.reject(&block) end end # Remove all lines that aren't in the given range, expressed either as a # `Range` object or a first and last line number (inclusive). Negative # indices count from the end of the array of lines. # # @param [Range, Integer] start_line # @param [Integer?] end_line # @return [Code] def between(start_line, end_line = nil) return self unless start_line code_range = CodeRange.new(start_line, end_line) alter do @lines = @lines[code_range.indices_range(@lines)] || [] end end # Take `num_lines` from `start_line`, forward or backwards. # # @param [Integer] start_line # @param [Integer] num_lines # @return [Code] def take_lines(start_line, num_lines) start_idx = if start_line >= 0 @lines.index { |loc| loc.lineno >= start_line } || @lines.length else [@lines.length + start_line, 0].max end alter do @lines = @lines.slice(start_idx, num_lines) end end # Remove all lines except for the +lines+ up to and excluding +lineno+. # # @param [Integer] lineno # @param [Integer] lines # @return [Code] def before(lineno, lines = 1) return self unless lineno select do |loc| loc.lineno >= lineno - lines && loc.lineno < lineno end end # Remove all lines except for the +lines+ on either side of and including # +lineno+. # # @param [Integer] lineno # @param [Integer] lines # @return [Code] def around(lineno, lines = 1) return self unless lineno select do |loc| loc.lineno >= lineno - lines && loc.lineno <= lineno + lines end end # Remove all lines except for the +lines+ after and excluding +lineno+. # # @param [Integer] lineno # @param [Integer] lines # @return [Code] def after(lineno, lines = 1) return self unless lineno select do |loc| loc.lineno > lineno && loc.lineno <= lineno + lines end end # Remove all lines that don't match the given `pattern`. # # @param [Regexp] pattern # @return [Code] def grep(pattern) return self unless pattern pattern = Regexp.new(pattern) select do |loc| loc.line =~ pattern end end # Format output with line numbers next to it, unless `y_n` is falsy. # # @param [Boolean?] y_n # @return [Code] def with_line_numbers(y_n = true) alter do @with_line_numbers = y_n end end # Format output with a marker next to the given +lineno+, unless +lineno+ is # falsy. # # @param [Integer?] lineno # @return [Code] def with_marker(lineno = 1) alter do @with_marker = !!lineno @marker_lineno = lineno end end # Format output with the specified number of spaces in front of every line, # unless `spaces` is falsy. # # @param [Integer?] spaces # @return [Code] def with_indentation(spaces = 0) alter do @with_indentation = !!spaces @indentation_num = spaces end end # @return [Integer] the number of digits in the last line. def max_lineno_width !@lines.empty? ? @lines.last.lineno.to_s.length : 0 end # @return [String] a formatted representation (based on the configuration of # the object). def to_s print_to_output(''.dup, false) end # @return [String] a (possibly highlighted) copy of the source code. def highlighted print_to_output(''.dup, true) end # Writes a formatted representation (based on the configuration of the # object) to the given output, which must respond to `#<<`. def print_to_output(output, color = false) @lines.each do |loc| loc = loc.dup loc.colorize(@code_type) if color loc.add_line_number(max_lineno_width, color) if @with_line_numbers loc.add_marker(@marker_lineno) if @with_marker loc.indent(@indentation_num) if @with_indentation output << loc.line output << "\n" end output end # Get the comment that describes the expression on the given line number. # # @param [Integer] line_number (1-based) # @return [String] the code. def comment_describing(line_number) self.class.comment_describing(raw, line_number) end # Get the multiline expression that starts on the given line number. # # @param [Integer] line_number (1-based) # @return [String] the code. def expression_at(line_number, consume = 0) self.class.expression_at(raw, line_number, consume: consume) end # Get the (approximate) Module.nesting at the give line number. # # @param [Integer] line_number line number starting from 1 # @return [Array] a list of open modules. def nesting_at(line_number) Pry::Indent.nesting_at(raw, line_number) end # Return an unformatted String of the code. # # @return [String] def raw @lines.map(&:line).join("\n") << "\n" end # Return the number of lines stored. # # @return [Integer] def length @lines ? @lines.length : 0 end # Two `Code` objects are equal if they contain the same lines with the same # numbers. Otherwise, call `to_s` and `chomp` and compare as Strings. # # @param [Code, Object] other # @return [Boolean] def ==(other) if other.is_a?(Code) other_lines = other.instance_variable_get(:@lines) @lines.each_with_index.all? { |loc, i| loc == other_lines[i] } else to_s.chomp == other.to_s.chomp end end # Forward any missing methods to the output of `#to_s`. def method_missing(method_name, *args, &block) if (string = to_s).respond_to?(method_name) string.__send__(method_name, *args, &block) else super end end undef =~ # Check whether String responds to missing methods. def respond_to_missing?(method_name, include_private = false) ''.respond_to?(method_name, include_private) || super end if RUBY_VERSION.start_with?('1.9') # @todo This is needed for Ruby 1.9 support where `lines` return an # Enumerator. Newer Rubies return an Array def lines super.to_a end end protected # An abstraction of the `dup.instance_eval` pattern used throughout this # class. def alter(&block) dup.tap { |o| o.instance_eval(&block) } end end end pry-0.13.1/lib/pry/code/000077500000000000000000000000001364454223300147065ustar00rootroot00000000000000pry-0.13.1/lib/pry/code/code_file.rb000066400000000000000000000064531364454223300171540ustar00rootroot00000000000000# frozen_string_literal: true require 'method_source' class Pry class CodeFile DEFAULT_EXT = '.rb'.freeze # List of all supported languages. # @return [Hash] EXTENSIONS = { %w[.py] => :python, %w[.js] => :javascript, %w[.css] => :css, %w[.xml] => :xml, %w[.php] => :php, %w[.html] => :html, %w[.diff] => :diff, %w[.java] => :java, %w[.json] => :json, %w[.c .h] => :c, %w[.rhtml] => :rhtml, %w[.yaml .yml] => :yaml, %w[.cpp .hpp .cc .h .cxx] => :cpp, %w[.rb .ru .irbrc .gemspec .pryrc .rake] => :ruby }.freeze FILES = { %w[Gemfile Rakefile Guardfile Capfile] => :ruby }.freeze # Store the current working directory. This allows show-source etc. to work if # your process has changed directory since boot. [Issue #675] INITIAL_PWD = Dir.pwd # @return [Symbol] The type of code stored in this wrapper. attr_reader :code_type # @param [String] filename The name of a file with code to be detected # @param [Symbol] code_type The type of code the `filename` contains def initialize(filename, code_type = type_from_filename(filename)) @filename = filename @code_type = code_type end # @return [String] The code contained in the current `@filename`. def code if @filename == Pry.eval_path Pry.line_buffer.drop(1) elsif Pry::Method::Patcher.code_for(@filename) Pry::Method::Patcher.code_for(@filename) else path = abs_path @code_type = type_from_filename(path) File.read(path) end end private # @raise [MethodSource::SourceNotFoundError] if the `filename` is not # readable for some reason. # @return [String] absolute path for the given `filename`. def abs_path code_path.detect { |path| readable?(path) } || raise(MethodSource::SourceNotFoundError, "Cannot open #{@filename.inspect} for reading.") end # @param [String] path # @return [Boolean] if the path, with or without the default ext, # is a readable file then `true`, otherwise `false`. def readable?(path) File.readable?(path) && !File.directory?(path) || File.readable?(path << DEFAULT_EXT) end # @return [Array] All the paths that contain code that Pry can use for its # API's. Skips directories. def code_path [from_pwd, from_pry_init_pwd, *from_load_path] end # @param [String] filename # @param [Symbol] default (:unknown) the file type to assume if none could be # detected. # @return [Symbol, nil] The SyntaxHighlighter type of a file from its # extension, or `nil` if `:unknown`. def type_from_filename(filename, default = :unknown) _, @code_type = EXTENSIONS.find do |k, _| k.any? { |ext| ext == File.extname(filename) } end || FILES.find do |k, _| k.any? { |file_name| file_name == File.basename(filename) } end code_type || default end # @return [String] def from_pwd File.expand_path(@filename, Dir.pwd) end # @return [String] def from_pry_init_pwd File.expand_path(@filename, INITIAL_PWD) end # @return [String] def from_load_path $LOAD_PATH.map { |path| File.expand_path(@filename, path) } end end end pry-0.13.1/lib/pry/code/code_range.rb000066400000000000000000000034771364454223300173340ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Code # Represents a range of lines in a code listing. # # @api private class CodeRange # @param [Integer] start_line # @param [Integer?] end_line def initialize(start_line, end_line = nil) @start_line = start_line @end_line = end_line force_set_end_line end # @param [Array] lines # @return [Range] def indices_range(lines) Range.new(*indices(lines)) end private attr_reader :start_line attr_reader :end_line # If `end_line` is equal to `nil`, then calculate it from the first # parameter, `start_line`. Otherwise, leave it as it is. # @return [void] def force_set_end_line if start_line.is_a?(Range) set_end_line_from_range else @end_line ||= start_line end end # Finds indices of `start_line` and `end_line` in the given Array of # +lines+. # # @param [Array] lines # @return [Array] def indices(lines) [find_start_index(lines), find_end_index(lines)] end # @return [Integer] def find_start_index(lines) return start_line if start_line < 0 lines.index { |loc| loc.lineno >= start_line } || lines.length end # @return [Integer] def find_end_index(lines) return end_line if end_line < 0 (lines.index { |loc| loc.lineno > end_line } || 0) - 1 end # For example, if the range is 4..10, then `start_line` would be equal to # 4 and `end_line` to 10. # @return [void] def set_end_line_from_range @end_line = start_line.last @end_line -= 1 if start_line.exclude_end? @start_line = start_line.first end end end end pry-0.13.1/lib/pry/code/loc.rb000066400000000000000000000055141364454223300160150ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Code # Represents a line of code (which may, in fact, contain multiple lines if # the entirety was eval'd as a single unit following the `edit` command). # # A line of code is a tuple, which consists of a line and a line number. A # `LOC` object's state (namely, the line parameter) can be changed via # instance methods. `Pry::Code` heavily uses this class. # # @api private # @example # loc = LOC.new("def example\n :example\nend", 1) # puts loc.line # def example # :example # end # #=> nil # # loc.indent(3) # loc.line #=> " def example\n :example\nend" class LOC # @return [Array] attr_reader :tuple # @param [String] line The line of code. # @param [Integer] lineno The position of the +line+. def initialize(line, lineno) @tuple = [line.chomp, lineno.to_i] end # @return [Boolean] def ==(other) other.tuple == tuple end def dup self.class.new(line, lineno) end # @return [String] def line tuple.first end # @return [Integer] def lineno tuple.last end # Paints the `line` of code. # # @param [Symbol] code_type # @return [void] def colorize(code_type) tuple[0] = SyntaxHighlighter.highlight(line, code_type) end # Prepends the line number `lineno` to the `line`. # # @param [Integer] max_width # @return [void] def add_line_number(max_width = 0, color = false) padded = lineno.to_s.rjust(max_width) colorized_lineno = if color Pry::Helpers::BaseHelpers.colorize_code(padded) else padded end properly_padded_line = handle_multiline_entries_from_edit_command(line, max_width) tuple[0] = "#{colorized_lineno}: #{properly_padded_line}" end # Prepends a marker "=>" or an empty marker to the +line+. # # @param [Integer] marker_lineno If it is equal to the `lineno`, then # prepend a hashrocket. Otherwise, an empty marker. # @return [void] def add_marker(marker_lineno) tuple[0] = if lineno == marker_lineno " => #{line}" else " #{line}" end end # Indents the `line` with +distance+ spaces. # # @param [Integer] distance # @return [void] def indent(distance) tuple[0] = "#{' ' * distance}#{line}" end def handle_multiline_entries_from_edit_command(line, max_width) line.split("\n").map.with_index do |inner_line, i| i.zero? ? inner_line : "#{' ' * (max_width + 2)}#{inner_line}" end.join("\n") end end end end pry-0.13.1/lib/pry/code_object.rb000066400000000000000000000142071364454223300165650ustar00rootroot00000000000000# frozen_string_literal: true class Pry # This class is responsible for taking a string (identifying a # command/class/method/etc) and returning the relevant type of object. # For example, if the user looks up "show-source" then a # `Pry::Command` will be returned. Alternatively, if the user passes in "Pry#repl" then # a `Pry::Method` object will be returned. # # The `CodeObject.lookup` method is responsible for 1. figuring out what kind of # object the user wants (applying precedence rules in doing so -- i.e methods # get precedence over commands with the same name) and 2. Returning # the appropriate object. If the user fails to provide a string # identifer for the object (i.e they pass in `nil` or "") then the # object looked up will be the 'current method' or 'current class' # associated with the Binding. # # TODO: This class is a clusterfuck. We need a much more robust # concept of what a "Code Object" really is. Currently # commands/classes/candidates/methods and so on just share a very # ill-defined interface. class CodeObject module Helpers # we need this helper as some Pry::Method objects can wrap Procs # @return [Boolean] def real_method_object? is_a?(::Method) || is_a?(::UnboundMethod) end def c_method? real_method_object? && source_type == :c end def module_with_yard_docs? is_a?(WrappedModule) && yard_docs? end def command? is_a?(Module) && self <= Pry::Command end # @return [Boolean] `true` if this module was defined by means of the C API, # `false` if it's a Ruby module. # @note If a module defined by C was extended with a lot of methods written # in Ruby, this method would fail. def c_module? return unless is_a?(WrappedModule) method_locations = wrapped.methods(false).map do |m| wrapped.method(m).source_location end method_locations.concat( wrapped.instance_methods(false).map do |m| wrapped.instance_method(m).source_location end ) c_methods = method_locations.grep(nil).count ruby_methods = method_locations.count - c_methods c_methods > ruby_methods end end include Pry::Helpers::CommandHelpers class << self def lookup(str, pry_instance, options = {}) co = new(str, pry_instance, options) co.default_lookup || co.method_or_class_lookup || co.command_lookup || co.empty_lookup end end attr_accessor :str attr_accessor :target attr_accessor :pry_instance attr_accessor :super_level def initialize(str, pry_instance, options = {}) options = { super: 0 }.merge!(options) @str = str @pry_instance = pry_instance @target = pry_instance.current_context @super_level = options[:super] end # TODO: just make it so find_command_by_match_or_listing doesn't raise? def command_lookup pry_instance.commands.find_command_by_match_or_listing(str) rescue StandardError nil end # when no paramter is given (i.e CodeObject.lookup(nil)), then we # lookup the 'current object' from the binding. def empty_lookup return nil if str && !str.empty? obj = if internal_binding?(target) mod = target_self.is_a?(Module) ? target_self : target_self.class Pry::WrappedModule(mod) else Pry::Method.from_binding(target) end # respect the super level (i.e user might have specified a # --super flag to show-source) lookup_super(obj, super_level) end # lookup variables and constants and `self` that are not modules def default_lookup # we skip instance methods as we want those to fall through to # method_or_class_lookup() if safe_to_evaluate?(str) && !looks_like_an_instance_method?(str) obj = target.eval(str) # restrict to only objects we KNOW for sure support the full API # Do NOT support just any object that responds to source_location if sourcable_object?(obj) Pry::Method(obj) elsif !obj.is_a?(Module) Pry::WrappedModule(obj.class) end end rescue Pry::RescuableException nil end def method_or_class_lookup obj = case str when /\S+\(\)\z/ Pry::Method.from_str(str.sub(/\(\)\z/, ''), target) || Pry::WrappedModule.from_str(str, target) else Pry::WrappedModule.from_str(str, target) || Pry::Method.from_str(str, target) end lookup_super(obj, super_level) end private def sourcable_object?(obj) [::Proc, ::Method, ::UnboundMethod].any? { |o| obj.is_a?(o) } end # Returns true if `str` looks like a method, i.e Klass#method # We need to consider this case because method lookups should fall # through to the `method_or_class_lookup()` method but a # defined?() on a "Klass#method` string will see the `#` as a # comment and only evaluate the `Klass` part. # @param [String] str # @return [Boolean] Whether the string looks like an instance method. def looks_like_an_instance_method?(str) str =~ /\S#\S/ end # We use this method to decide whether code is safe to eval. Method's are # generally not, but everything else is. # TODO: is just checking != "method" enough?? # TODO: see duplication of this method in Pry::WrappedModule # @param [String] str The string to lookup # @return [Boolean] def safe_to_evaluate?(str) return true if str.strip == "self" return false if str =~ /%/ kind = target.eval("defined?(#{str})") kind =~ /variable|constant/ end def target_self target.eval('self') end # grab the nth (`super_level`) super of `obj # @param [Object] obj # @param [Fixnum] super_level How far up the super chain to ascend. def lookup_super(obj, super_level) return unless obj sup = obj.super(super_level) raise Pry::CommandError, "No superclass found for #{obj.wrapped}" unless sup sup end end end pry-0.13.1/lib/pry/color_printer.rb000066400000000000000000000036521364454223300172100ustar00rootroot00000000000000# frozen_string_literal: true require 'pp' require 'English' class Pry # PP subclass for streaming inspect output in color. class ColorPrinter < ::PP Pry::SyntaxHighlighter.overwrite_coderay_comment_token! def self.default(_output, value, pry_instance) pry_instance.pager.open do |pager| pager.print pry_instance.config.output_prefix pp(value, pager, pry_instance.output.width - 1) end end def self.pp(obj, output = $DEFAULT_OUTPUT, max_width = 79) queue = ColorPrinter.new(output, max_width, "\n") queue.guard_inspect_key { queue.pp(obj) } queue.flush output << "\n" end def pp(object) return super unless object.is_a?(String) # Avoid calling Ruby 2.4+ String#pretty_print that prints multiline # Strings prettier text(object.inspect) rescue StandardError => exception raise if exception.is_a?(Pry::Pager::StopPaging) text(highlight_object_literal(inspect_object(object))) end def text(str, max_width = str.length) if str.include?("\e[") super("#{str}\e[0m", max_width) elsif str.start_with?('#<') || %w[= >].include?(str) super(highlight_object_literal(str), max_width) else super(SyntaxHighlighter.highlight(str), max_width) end end private def highlight_object_literal(object_literal) code = Pry::SyntaxHighlighter.keyword_token_color obj_color = code.start_with?("\e") ? code : "\e[0m\e[0;#{code}m" "#{obj_color}#{object_literal}\e[0m" end def inspect_object(object) object.inspect rescue StandardError # Read the class name off of the singleton class to provide a default # inspect. singleton = class << object; self; end ancestors = Pry::Method.safe_send(singleton, :ancestors) klass = ancestors.find { |k| k != singleton } "#<#{klass}:0x#{object.__id__.to_s(16)}>" end end end pry-0.13.1/lib/pry/command.rb000066400000000000000000000354501364454223300157460ustar00rootroot00000000000000# frozen_string_literal: true require 'delegate' require 'shellwords' class Pry # The super-class of all commands, new commands should be created by calling # {Pry::CommandSet#command} which creates a BlockCommand or # {Pry::CommandSet#create_command} which creates a ClassCommand. Please don't # use this class directly. class Command extend Helpers::DocumentationHelpers extend CodeObject::Helpers include Pry::Helpers::BaseHelpers include Pry::Helpers::CommandHelpers include Pry::Helpers::Text # represents a void return value for a command VOID_VALUE = Object.new # give it a nice inspect def VOID_VALUE.inspect "void" end # Properties of the command itself (as passed as arguments to # {CommandSet#command} or {CommandSet#create_command}). class << self attr_writer :block attr_writer :description attr_writer :command_options attr_writer :match def match(arg = nil) if arg @command_options ||= default_options(arg) @command_options[:listing] = arg.is_a?(String) ? arg : arg.inspect @match = arg end @match ||= nil end # Define or get the command's description def description(arg = nil) @description = arg if arg @description ||= nil end # Define or get the command's options def command_options(arg = nil) @command_options ||= default_options(match) @command_options.merge!(arg) if arg @command_options end # backward compatibility alias options command_options alias options= command_options= # Define or get the command's banner def banner(arg = nil) @banner = arg if arg @banner ||= description end def block @block || instance_method(:process) end def source file, line = block.source_location strip_leading_whitespace(Pry::Code.from_file(file).expression_at(line)) end def doc new.help end def source_file Array(block.source_location).first end alias file source_file def source_line Array(block.source_location).last end alias line source_line def default_options(match) { keep_retval: false, argument_required: false, interpolate: true, shellwords: true, listing: (match.is_a?(String) ? match : match.inspect), use_prefix: true, takes_block: false } end def name super.to_s == "" ? "#" : super end def inspect name end def command_name options[:listing] end # Create a new command with the given properties. # @param [String, Regex] match The thing that triggers this command # @param [String] description The description to appear in `help` # @param [Hash] options Behavioral options (see {Pry::CommandSet#command}) # @param [Module] helpers A module of helper functions to be included. # @yield optional, used for BlockCommands # @return [Class] (a subclass of {Pry::Command}) def subclass(match, description, options, helpers, &block) klass = Class.new(self) klass.send(:include, helpers) klass.match = match klass.description = description klass.command_options = options klass.block = block klass end # Should this command be called for the given line? # @param [String] val A line input at the REPL # @return [Boolean] def matches?(val) command_regex =~ val end # How well does this command match the given line? # # Higher scores are better because they imply that this command matches # the line more closely. # # The score is calculated by taking the number of characters at the start # of the string that are used only to identify the command, not as part of # the arguments. # # @example # /\.(.*)/.match_score(".foo") #=> 1 # /\.*(.*)/.match_score("...foo") #=> 3 # 'hi'.match_score("hi there") #=> 2 # # @param [String] val A line input at the REPL # @return [Fixnum] def match_score(val) if command_regex =~ val if Regexp.last_match.size > 1 Regexp.last_match.begin(1) else Regexp.last_match.end(0) end else -1 end end def command_regex prefix = convert_to_regex(Pry.config.command_prefix) prefix = "(?:#{prefix})?" unless options[:use_prefix] /\A#{prefix}#{convert_to_regex(match)}(?!\S)/ end def convert_to_regex(obj) case obj when String Regexp.escape(obj) else obj end end # The group in which the command should be displayed in "help" output. # This is usually auto-generated from directory naming, but it can be # manually overridden if necessary. # Group should not be changed once it is initialized. def group(name = nil) @group ||= begin name || case Pry::Method(block).source_file when %r{/pry/.*_commands/(.*).rb} Regexp.last_match(1).capitalize.tr('_', " ") when /(pry-\w+)-([\d\.]+([\w\.]+)?)/ name = Regexp.last_match(1) version = Regexp.last_match(2) "#{name} (v#{version})" when /pryrc/ "pryrc" else "(other)" end end end def state Pry::CommandState.default.state_for(match) end end # Properties of one execution of a command (passed by {Pry#run_command} as a hash of # context and expanded in `#initialize` attr_accessor :output attr_accessor :target attr_accessor :captures attr_accessor :eval_string attr_accessor :arg_string attr_accessor :context attr_accessor :command_set attr_accessor :hooks attr_accessor :pry_instance alias _pry_= pry_instance= # The block we pass *into* a command so long as `:takes_block` is # not equal to `false` # @example # my-command | do # puts "block content" # end attr_accessor :command_block # Instantiate a command, in preparation for calling it. # @param [Hash] context The runtime context to use with this command. def initialize(context = {}) self.context = context self.target = context[:target] self.output = context[:output] self.eval_string = context[:eval_string] self.command_set = context[:command_set] self.hooks = context[:hooks] self.pry_instance = context[:pry_instance] end # Make those properties accessible to instances def name self.class.name end def match self.class.match end def description self.class.description end def block self.class.block end def command_options self.class.options end def command_name self.class.command_name end def source self.class.source end # Run a command from another command. # @param [String] command_string The string that invokes the command # @param [Array] args Further arguments to pass to the command # @example # run "show-input" # @example # run ".ls" # @example # run "amend-line", "5", 'puts "hello world"' def run(command_string, *args) command_string = pry_instance.config.command_prefix.to_s + command_string complete_string = "#{command_string} #{args.join(' ')}".rstrip command_set.process_line(complete_string, context) end def commands command_set.to_hash end def void VOID_VALUE end def _pry_ Pry::Warning.warn('_pry_ is deprecated, use pry_instance instead') pry_instance end # @return [Object] The value of `self` inside the `target` binding. def target_self target.eval('self') end # @return [Hash] Pry commands can store arbitrary state # here. This state persists between subsequent command invocations. # All state saved here is unique to the command, it does not # need to be namespaced. # @example # state.my_state = "my state" # this will not conflict with any # # `state.my_state` used in another command. def state self.class.state end # Revaluate the string (str) and perform interpolation. # @param [String] str The string to reevaluate with interpolation. # # @return [String] The reevaluated string with interpolations # applied (if any). def interpolate_string(str) dumped_str = str.dump if dumped_str.gsub!(/\\\#\{/, '#{') target.eval(dumped_str) else str end end # Display a warning if a command collides with a local/method in # the current scope. def check_for_command_collision(command_match, arg_string) collision_type = target.eval("defined?(#{command_match})") collision_type ||= 'local-variable' if arg_string =~ %r{\A\s*[-+*/%&|^]*=} if collision_type output.puts( "#{Helpers::Text.bold('WARNING:')} Calling Pry command '#{command_match}', " \ "which conflicts with a #{collision_type}.\n\n" ) end rescue Pry::RescuableException # rubocop:disable Lint/HandleExceptions end # Extract necessary information from a line that Command.matches? this # command. # # Returns an array of four elements: # # ``` # [String] the portion of the line that matched with the Command match # [String] a string of all the arguments (i.e. everything but the match) # [Array] the captures caught by the command_regex # [Array] the arguments obtained by splitting the arg_string # ``` # # @param [String] val The line of input # @return [Array] def tokenize(val) val = interpolate_string(val) if command_options[:interpolate] self.class.command_regex =~ val # please call Command.matches? before Command#call_safely unless Regexp.last_match raise CommandError, "fatal: called a command which didn't match?!" end captures = Regexp.last_match.captures pos = Regexp.last_match.end(0) arg_string = val[pos..-1] # remove the one leading space if it exists arg_string.slice!(0) if arg_string.start_with?(" ") # process and pass a block if one is found pass_block(arg_string) if command_options[:takes_block] args = if arg_string if command_options[:shellwords] Shellwords.shellwords(arg_string) else arg_string.split(" ") end else [] end [val[0..pos].rstrip, arg_string, captures, args] end # Process a line that Command.matches? this command. # @param [String] line The line to process # @return [Object, Command::VOID_VALUE] def process_line(line) command_match, arg_string, captures, args = tokenize(line) if Pry.config.collision_warning check_for_command_collision(command_match, arg_string) end self.arg_string = arg_string self.captures = captures call_safely(*(captures + args)) end # Generate completions for this command # # @param [String] _search The line typed so far # @return [Array] Completion words def complete(_search) [] end private # Run the command with the given `args`. # # This is a public wrapper around `#call` which ensures all preconditions # are met. # # @param [Array] args The arguments to pass to this command. # @return [Object] The return value of the `#call` method, or # {Command::VOID_VALUE}. def call_safely(*args) if command_options[:argument_required] && args.empty? raise CommandError, "The command '#{command_name}' requires an argument." end ret = use_unpatched_symbol do call_with_hooks(*args) end command_options[:keep_retval] ? ret : void end def use_unpatched_symbol call_method = Symbol.method_defined?(:call) && Symbol.instance_method(:call) Symbol.class_eval { undef :call } if call_method yield ensure Symbol.instance_eval { define_method(:call, call_method) } if call_method end # Pass a block argument to a command. # @param [String] arg_string The arguments (as a string) passed to the command. # We inspect these for a '| do' or a '| {' and if we find it we use it # to start a block input sequence. Once we have a complete # block, we save it to an accessor that can be retrieved from the command context. # Note that if we find the '| do' or '| {' we delete this and the # elements following it from `arg_string`. def pass_block(arg_string) # Workaround for weird JRuby bug where rindex in this case can return nil # even when there's a match. arg_string.scan(/\| *(?:do|\{)/) block_index = $LAST_MATCH_INFO && $LAST_MATCH_INFO.offset(0)[0] return unless block_index block_init_string = arg_string.slice!(block_index..-1)[1..-1] prime_string = "proc #{block_init_string}\n" block_string = if !Pry::Code.complete_expression?(prime_string) pry_instance.r(target, prime_string) else prime_string end begin self.command_block = target.eval(block_string) rescue Pry::RescuableException raise CommandError, "Incomplete block definition." end end def find_hooks(event) event_name = "#{event}_#{command_name}" (hooks || Pry.hooks || self.class.hooks).get_hooks(event_name).values end def before_hooks find_hooks('before') end def after_hooks find_hooks('after') end # Run the `#call` method and all the registered hooks. # @param [Array] args The arguments to `#call` # @return [Object] The return value from `#call` def call_with_hooks(*args) before_hooks.each { |block| instance_exec(*args, &block) } ret = call(*args) after_hooks.each do |block| ret = instance_exec(*args, &block) end ret end # Normalize method arguments according to its arity. # # @param [Integer] method # @param [Array] args # @return [Array] a (possibly shorter) array of the arguments to pass def normalize_method_args(method, args) case method.arity when -1 args when 0 [] else args.values_at(*(0..(method.arity - 1)).to_a) end end end end pry-0.13.1/lib/pry/command_set.rb000066400000000000000000000332121364454223300166130ustar00rootroot00000000000000# frozen_string_literal: true class Pry class NoCommandError < StandardError def initialize(match, owner) super "Command '#{match}' not found in command set #{owner}" end end # This class is used to create sets of commands. Commands can be imported from # different sets, aliased, removed, etc. class CommandSet include Enumerable include Pry::Helpers::BaseHelpers attr_reader :helper_module # @param [Array] imported_sets # Sets which will be imported automatically # @yield Optional block run to define commands def initialize(*imported_sets, &block) @commands = {} @helper_module = Module.new import(*imported_sets) instance_eval(&block) if block end # Defines a new Pry command. # @param [String, Regexp] match The start of invocations of this command. # @param [String] description A description of the command. # @param [Hash] options The optional configuration parameters. # @option options [Boolean] :keep_retval Whether or not to use return value # of the block for return of `command` or just to return `nil` # (the default). # @option options [Boolean] :interpolate Whether string #{} based # interpolation is applied to the command arguments before # executing the command. Defaults to true. # @option options [String] :listing The listing name of the # command. That is the name by which the command is looked up by # help and by show-source. Necessary for commands with regex matches. # @option options [Boolean] :use_prefix Whether the command uses # `Pry.config.command_prefix` prefix (if one is defined). Defaults # to true. # @option options [Boolean] :shellwords Whether the command's arguments # should be split using Shellwords instead of just split on spaces. # Defaults to true. # @yield The action to perform. The parameters in the block # determines the parameters the command will receive. All # parameters passed into the block will be strings. Successive # command parameters are separated by whitespace at the Pry prompt. # @example # MyCommands = Pry::CommandSet.new do # command "greet", "Greet somebody" do |name| # puts "Good afternoon #{name.capitalize}!" # end # end # # # From pry: # # pry(main)> pry_instance.commands = MyCommands # # pry(main)> greet john # # Good afternoon John! # # pry(main)> help greet # # Greet somebody # @example Regexp command # MyCommands = Pry::CommandSet.new do # command( # /number-(\d+)/, "number-N regex command", :listing => "number" # ) do |num, name| # puts "hello #{name}, nice number: #{num}" # end # end # # # From pry: # # pry(main)> pry_instance.commands = MyCommands # # pry(main)> number-10 john # # hello john, nice number: 10 # # pry(main)> help number # # number-N regex command def block_command(match, description = "No description.", options = {}, &block) if description.is_a?(Hash) options = description description = "No description." end options = Pry::Command.default_options(match).merge!(options) @commands[match] = Pry::BlockCommand.subclass( match, description, options, helper_module, &block ) end alias command block_command # Defines a new Pry command class. # # @param [String, Regexp] match The start of invocations of this command. # @param [String] description A description of the command. # @param [Hash] options The optional configuration parameters, see {#command} # @yield The class body's definition. # # @example # Pry::Commands.create_command "echo", "echo's the input", :shellwords => false do # def options(opt) # opt.banner "Usage: echo [-u | -d] " # opt.on :u, :upcase, "ensure the output is all upper-case" # opt.on :d, :downcase, "ensure the output is all lower-case" # end # # def process # if opts.present?(:u) && opts.present?(:d) # raise Pry::CommandError, "-u and -d makes no sense" # end # result = args.join(" ") # result.downcase! if opts.present?(:downcase) # result.upcase! if opts.present?(:upcase) # output.puts result # end # end # def create_command(match, description = "No description.", options = {}, &block) if description.is_a?(Hash) options = description description = "No description." end options = Pry::Command.default_options(match).merge!(options) @commands[match] = Pry::ClassCommand.subclass( match, description, options, helper_module, &block ) @commands[match].class_eval(&block) @commands[match] end def each(&block) @commands.each(&block) end # Removes some commands from the set # @param [Array] searches the matches or listings of the commands # to remove def delete(*searches) searches.each do |search| cmd = find_command_by_match_or_listing(search) @commands.delete cmd.match end end # Imports all the commands from one or more sets. # @param [Array] sets Command sets, all of the commands of which # will be imported. # @return [Pry::CommandSet] Returns the reciever (a command set). def import(*sets) sets.each do |set| @commands.merge! set.to_hash helper_module.send :include, set.helper_module end self end # Imports some commands from a set # @param [CommandSet] set Set to import commands from # @param [Array] matches Commands to import # @return [Pry::CommandSet] Returns the reciever (a command set). def import_from(set, *matches) helper_module.send :include, set.helper_module matches.each do |match| cmd = set.find_command_by_match_or_listing(match) @commands[cmd.match] = cmd end self end # @param [String, Regexp] match_or_listing The match or listing of a command. # of the command to retrieve. # @return [Command] The command object matched. def find_command_by_match_or_listing(match_or_listing) cmd = (@commands[match_or_listing] || Pry::Helpers::BaseHelpers.find_command(match_or_listing, @commands)) cmd || raise(ArgumentError, "cannot find a command: '#{match_or_listing}'") end # Aliases a command # @param [String, Regex] match The match of the alias (can be a regex). # @param [String] action The action to be performed (typically # another command). # @param [Hash] options The optional configuration parameters, # accepts the same as the `command` method, but also allows the # command description to be passed this way too as `:desc` # @example Creating an alias for `ls -M` # Pry.config.commands.alias_command "lM", "ls -M" # @example Pass explicit description (overriding default). # Pry.config.commands.alias_command "lM", "ls -M", :desc => "cutiepie" def alias_command(match, action, options = {}) (cmd = find_command(action)) || raise("command: '#{action}' not found") original_options = cmd.options.dup options = original_options.merge!( desc: "Alias for `#{action}`", listing: match.is_a?(String) ? match : match.inspect ).merge!(options) # ensure default description is used if desc is nil desc = options.delete(:desc).to_s c = block_command match, desc, options do |*args| run action, *args end # TODO: untested. What's this about? c.class_eval do define_method(:complete) do |input| cmd.new(context).complete(input) end end c.group "Aliases" c end # Rename a command. Accepts either match or listing for the search. # # @param [String, Regexp] new_match The new match for the command. # @param [String, Regexp] search The command's current match or listing. # @param [Hash] options The optional configuration parameters, # accepts the same as the `command` method, but also allows the # command description to be passed this way too. # @example Renaming the `ls` command and changing its description. # Pry.config.commands.rename "dir", "ls", :description => "DOS friendly ls" def rename_command(new_match, search, options = {}) cmd = find_command_by_match_or_listing(search) options = { listing: new_match, description: cmd.description }.merge!(options) @commands[new_match] = cmd.dup @commands[new_match].match = new_match @commands[new_match].description = options.delete(:description) @commands[new_match].options.merge!(options) @commands.delete(cmd.match) end # Sets or gets the description for a command (replacing the old # description). Returns current description if no description # parameter provided. # @param [String, Regexp] search The command match. # @param [String?] description (nil) The command description. # @example Setting # MyCommands = Pry::CommandSet.new do # desc "help", "help description" # end # @example Getting # Pry.config.commands.desc "amend-line" def desc(search, description = nil) cmd = find_command_by_match_or_listing(search) return cmd.description unless description cmd.description = description end # @return [Array] # The list of commands provided by the command set. def list_commands @commands.keys end alias keys list_commands def to_hash @commands.dup end alias to_h to_hash # Find a command that matches the given line # @param [String] pattern The line that might be a command invocation # @return [Pry::Command, nil] def [](pattern) commands = @commands.values.select do |command| command.matches?(pattern) end commands.max_by { |command| command.match_score(pattern) } end alias find_command [] # # Re-assign the command found at _pattern_ with _command_. # # @param [Regexp, String] pattern # The command to add or replace(found at _pattern_). # # @param [Pry::Command] command # The command to add. # # @return [Pry::Command] # Returns the new command (matched with "pattern".) # # @example # Pry.config.commands["help"] = MyHelpCommand # def []=(pattern, command) if command.equal?(nil) @commands.delete(pattern) return end unless command.is_a?(Class) && command < Pry::Command raise TypeError, "command is not a subclass of Pry::Command" end bind_command_to_pattern = pattern != command.match if bind_command_to_pattern command_copy = command.dup command_copy.match = pattern @commands[pattern] = command_copy else @commands[pattern] = command end end # # Add a command to set. # # @param [Command] command # a subclass of Pry::Command. # def add_command(command) self[command.match] = command end # Find the command that the user might be trying to refer to. # @param [String] search The user's search. # @return [Pry::Command?] def find_command_for_help(search) find_command(search) || (begin find_command_by_match_or_listing(search) rescue ArgumentError nil end) end # Is the given line a command invocation? # @param [String] val # @return [Boolean] def valid_command?(val) !!find_command(val) end # Process the given line to see whether it needs executing as a command. # @param [String] val The line to execute # @param [Hash] context The context to execute the commands with # @return [CommandSet::Result] def process_line(val, context = {}) if (command = find_command(val)) context = context.merge(command_set: self) retval = command.new(context).process_line(val) Result.new(true, retval) else Result.new(false) end end # Generate completions for the user's search. # @param [String] search The line to search for # @param [Hash] context The context to create the command with # @return [Array] def complete(search, context = {}) if (command = find_command(search)) command.new(context).complete(search) else keys = @commands.keys.select do |key| key.is_a?(String) && key.start_with?(search) end keys.map { |key| key + " " } end end private # Defines helpers methods for this command sets. # Those helpers are only defined in this command set. # # @yield A block defining helper methods # @example # helpers do # def hello # puts "Hello!" # end # # include OtherModule # end def helpers(&block) helper_module.class_eval(&block) end end # Wraps the return result of process_commands, indicates if the # result IS a command and what kind of command (e.g void) class Result attr_reader :retval def initialize(is_command, retval = nil) @is_command = is_command @retval = retval end # Is the result a command? # @return [Boolean] def command? @is_command end # Is the result a command and if it is, is it a void command? # (one that does not return a value) # @return [Boolean] def void_command? retval == Command::VOID_VALUE end end end pry-0.13.1/lib/pry/command_state.rb000066400000000000000000000012151364454223300171360ustar00rootroot00000000000000# frozen_string_literal: true require 'ostruct' class Pry # CommandState is a data structure to hold per-command state. # # Pry commands can store arbitrary state here. This state persists between # subsequent command invocations. All state saved here is unique to the # command. # # @since v0.13.0 # @api private class CommandState def self.default @default ||= new end def initialize @command_state = {} end def state_for(command_name) @command_state[command_name] ||= OpenStruct.new end def reset(command_name) @command_state[command_name] = OpenStruct.new end end end pry-0.13.1/lib/pry/commands/000077500000000000000000000000001364454223300155755ustar00rootroot00000000000000pry-0.13.1/lib/pry/commands/amend_line.rb000066400000000000000000000070301364454223300202150ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class AmendLine < Pry::ClassCommand match(/amend-line(?: (-?\d+)(?:\.\.(-?\d+))?)?/) group 'Editing' description 'Amend a line of input in multi-line mode.' command_options interpolate: false, listing: 'amend-line' banner <<-'BANNER' Amend a line of input in multi-line mode. `amend-line N`, where the N represents line to replace. Can also specify a range of lines using `amend-line N..M` syntax. Passing "!" as replacement content deletes the line(s) instead. amend-line 1 puts 'new' # replace line 1 amend-line 1..4 ! # delete lines 1..4 amend-line 3 >puts 'bye' # insert before line 3 amend-line puts 'appended' # no line number modifies immediately preceding line BANNER def process raise CommandError, "No input to amend." if eval_string.empty? eval_string.replace(amend_input) run "fix-indent" run "show-input" end private # @return [String] A new string with the amendments applied to it. def amend_input input_array = eval_string.each_line.to_a if arg_string == "!" delete_from_array(input_array, line_range) elsif arg_string.start_with?(">") insert_into_array(input_array, line_range) else replace_in_array(input_array, line_range) end input_array.join end def delete_from_array(array, range) array.slice!(range) end def insert_into_array(array, range) insert_slot = Array(range).first array.insert(insert_slot, arg_string[1..-1] << "\n") end def replace_in_array(array, range) array[range] = arg_string + "\n" end # @return [Fixnum] The number of lines currently in `eval_string` (the # input buffer) def line_count eval_string.lines.count end # Returns the (one-indexed) start and end lines given by the user. # The lines in this range will be affected by the `amend-line`. # Returns `nil` if no lines were specified by the user. # @return [Array, nil] def start_and_end_line_number start_line_number, end_line_number = args end_line_number ||= start_line_number.to_i [start_line_number.to_i, end_line_number.to_i] if start_line_number end # Takes two numbers that are 1-indexed, and returns a range (or # number) that is 0-indexed. 1-indexed means the first element is # indentified by 1 rather than by 0 (as is the case for Ruby arrays). # @param [Fixnum] start_line_number One-indexed number. # @param [Fixnum] end_line_number One-indexed number. # @return [Range] The zero-indexed range. def zero_indexed_range_from_one_indexed_numbers(start_line_number, end_line_number) # FIXME: one_index_number is a horrible name for this method one_index_number(start_line_number)..one_index_number(end_line_number) end # The lines (or line) that will be modified by the `amend-line`. # @return [Range, Fixnum] The lines or line. def line_range start_line_number, end_line_number = start_and_end_line_number if start_line_number zero_indexed_range_from_one_indexed_numbers(start_line_number, end_line_number) else line_count - 1 end end end Pry::Commands.add_command(Pry::Command::AmendLine) end end pry-0.13.1/lib/pry/commands/bang.rb000066400000000000000000000010671364454223300170350ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Bang < Pry::ClassCommand match(/^\s*!\s*$/) group 'Editing' description 'Clear the input buffer.' command_options use_prefix: false, listing: '!' banner <<-'BANNER' Clear the input buffer. Useful if the parsing process goes wrong and you get stuck in the read loop. BANNER def process output.puts 'Input buffer cleared!' eval_string.replace('') end end Pry::Commands.add_command(Pry::Command::Bang) end end pry-0.13.1/lib/pry/commands/bang_pry.rb000066400000000000000000000006721364454223300177300ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class BangPry < Pry::ClassCommand match '!pry' group 'Navigating Pry' description 'Start a Pry session on current self.' banner <<-'BANNER' Start a Pry session on current self. Also works mid multi-line expression. BANNER def process target.pry end end Pry::Commands.add_command(Pry::Command::BangPry) end end pry-0.13.1/lib/pry/commands/cat.rb000066400000000000000000000044751364454223300167030ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Cat < Pry::ClassCommand match 'cat' group 'Input and Output' description "Show code from a file, Pry's input buffer, or the last exception." banner <<-'BANNER' Usage: cat FILE cat --ex [STACK_INDEX] cat --in [INPUT_INDEX_OR_RANGE] `cat` is capable of showing part or all of a source file, the context of the last exception, or an expression from Pry's input history. `cat --ex` defaults to showing the lines surrounding the location of the last exception. Invoking it more than once travels up the exception's backtrace, and providing a number shows the context of the given index of the backtrace. BANNER def options(opt) opt.on :ex, "Show the context of the last exception", optional_argument: true, as: Integer opt.on :i, :in, "Show one or more entries from Pry's expression history", optional_argument: true, as: Range, default: -5..-1 opt.on :s, :start, "Starting line (defaults to the first line)", optional_argument: true, as: Integer opt.on :e, :end, "Ending line (defaults to the last line)", optional_argument: true, as: Integer opt.on :l, :'line-numbers', "Show line numbers" opt.on :t, :type, "The file type for syntax highlighting " \ "(e.g., 'ruby' or 'python')", argument: true, as: Symbol end def process output = if opts.present?(:ex) ExceptionFormatter.new( pry_instance.last_exception, pry_instance, opts ).format elsif opts.present?(:in) InputExpressionFormatter.new(pry_instance.input_ring, opts).format else FileFormatter.new(args.first, pry_instance, opts).format end pry_instance.pager.page output end def complete(search) super | load_path_completions end def load_path_completions $LOAD_PATH.flat_map do |path| Dir[path + '/**/*'].map do |f| next if File.directory?(f) f.sub!(path + '/', '') end end end end Pry::Commands.add_command(Pry::Command::Cat) end end pry-0.13.1/lib/pry/commands/cat/000077500000000000000000000000001364454223300163445ustar00rootroot00000000000000pry-0.13.1/lib/pry/commands/cat/abstract_formatter.rb000066400000000000000000000012471364454223300225630ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Cat class AbstractFormatter include Pry::Helpers::CommandHelpers include Pry::Helpers::BaseHelpers private def decorate(content) content.code_type = code_type content.between(*between_lines) .with_line_numbers(use_line_numbers?).highlighted end def code_type opts[:type] || :ruby end def use_line_numbers? opts.present?(:'line-numbers') || opts.present?(:ex) end def between_lines [opts[:start] || 1, opts[:end] || -1] end end end end end pry-0.13.1/lib/pry/commands/cat/exception_formatter.rb000066400000000000000000000045001364454223300227510ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Cat class ExceptionFormatter < AbstractFormatter attr_reader :ex attr_reader :opts attr_reader :pry_instance include Pry::Helpers::Text def initialize(exception, pry_instance, opts) @ex = exception @opts = opts @pry_instance = pry_instance end def format check_for_errors set_file_and_dir_locals( backtrace_file, pry_instance, pry_instance.current_context ) code = decorate( Pry::Code.from_file(backtrace_file) .between(*start_and_end_line_for_code_window) .with_marker(backtrace_line) ) "#{header}#{code}" end private def code_window_size pry_instance.config.default_window_size || 5 end def backtrace_level @backtrace_level ||= begin bl = if opts[:ex].nil? ex.bt_index else ex.bt_index = absolute_index_number(opts[:ex], ex.backtrace.size) end increment_backtrace_level bl end end def increment_backtrace_level ex.inc_bt_index end def backtrace_file Array(ex.bt_source_location_for(backtrace_level)).first end def backtrace_line Array(ex.bt_source_location_for(backtrace_level)).last end def check_for_errors raise CommandError, "No exception found." unless ex return if backtrace_file raise CommandError, "The given backtrace level is out of bounds." end def start_and_end_line_for_code_window start_line = backtrace_line - code_window_size start_line = 1 if start_line < 1 [start_line, backtrace_line + code_window_size] end def header unindent( "#{bold 'Exception:'} #{ex.class}: #{ex.message}\n" \ "--\n" \ "#{bold('From:')} #{backtrace_file}:#{backtrace_line} @ " \ "#{bold("level: #{backtrace_level}")} of backtrace " \ "(of #{ex.backtrace.size - 1}).\n\n" ) end end end end end pry-0.13.1/lib/pry/commands/cat/file_formatter.rb000066400000000000000000000035421364454223300216770ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Cat class FileFormatter < AbstractFormatter attr_reader :file_with_embedded_line attr_reader :opts attr_reader :pry_instance def initialize(file_with_embedded_line, pry_instance, opts) unless file_with_embedded_line raise CommandError, "Must provide a filename, --in, or --ex." end @file_with_embedded_line = file_with_embedded_line @opts = opts @pry_instance = pry_instance @code_from_file = Pry::Code.from_file(file_name) end def format set_file_and_dir_locals(file_name, pry_instance, pry_instance.current_context) decorate(@code_from_file) end def file_and_line file_name, line_num = file_with_embedded_line.split(%r{:(?!/|\\)}) [file_name, line_num ? line_num.to_i : nil] end private def file_name file_and_line.first end def line_number file_and_line.last end def code_window_size pry_instance.config.default_window_size || 7 end def decorate(content) if line_number super(content.around(line_number, code_window_size)) else super end end def code_type opts[:type] || detect_code_type_from_file(file_name) end def detect_code_type_from_file(file_name) code_type = @code_from_file.code_type if code_type == :unknown name = File.basename(file_name).split('.', 2).first case name when "Rakefile", "Gemfile" :ruby else :text end else code_type end end end end end end pry-0.13.1/lib/pry/commands/cat/input_expression_formatter.rb000066400000000000000000000024031364454223300243710ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Cat class InputExpressionFormatter < AbstractFormatter attr_accessor :input_expressions attr_accessor :opts def initialize(input_expressions, opts) @input_expressions = input_expressions @opts = opts end def format raise CommandError, "No input expressions!" if numbered_input_items.empty? if numbered_input_items.length > 1 content = '' numbered_input_items.each do |i, s| content += "#{Helpers::Text.bold(i.to_s)}:\n" content += decorate(Pry::Code(s).with_indentation(2)).to_s end content else decorate(Pry::Code(selected_input_items.first)) end end private def selected_input_items input_expressions[normalized_expression_range] || [] end def numbered_input_items @numbered_input_items ||= normalized_expression_range.zip(selected_input_items) .reject { |_, s| s.nil? || s == "" } end def normalized_expression_range absolute_index_range(opts[:i], input_expressions.count) end end end end end pry-0.13.1/lib/pry/commands/cd.rb000066400000000000000000000023501364454223300165100ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Cd < Pry::ClassCommand match 'cd' group 'Context' description 'Move into a new context (object or scope).' banner <<-'BANNER' Usage: cd [OPTIONS] [--help] Move into new context (object or scope). As in UNIX shells use `cd ..` to go back, `cd /` to return to Pry top-level and `cd -` to toggle between last two scopes. Complex syntax (e.g `cd ../@x/@y`) also supported. cd @x cd .. cd / cd - https://github.com/pry/pry/wiki/State-navigation#wiki-Changing_scope BANNER def process state.old_stack ||= [] if arg_string.strip == "-" unless state.old_stack.empty? pry_instance.binding_stack, state.old_stack = state.old_stack, pry_instance.binding_stack end else stack = ObjectPath.new(arg_string, pry_instance.binding_stack).resolve if stack && stack != pry_instance.binding_stack state.old_stack = pry_instance.binding_stack pry_instance.binding_stack = stack end end end end Pry::Commands.add_command(Pry::Command::Cd) end end pry-0.13.1/lib/pry/commands/change_inspector.rb000066400000000000000000000016451364454223300214430ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class ChangeInspector < Pry::ClassCommand match 'change-inspector' group 'Input and Output' description 'Change the current inspector proc.' command_options argument_required: true banner <<-BANNER Usage: change-inspector NAME Change the proc used to print return values. See list-inspectors for a list of available procs and a short description of what each one does. BANNER def process(inspector) unless inspector_map.key?(inspector) raise Pry::CommandError, "'#{inspector}' isn't a known inspector!" end pry_instance.print = inspector_map[inspector][:value] output.puts "Switched to the '#{inspector}' inspector!" end private def inspector_map Pry::Inspector::MAP end Pry::Commands.add_command(self) end end end pry-0.13.1/lib/pry/commands/change_prompt.rb000066400000000000000000000023531364454223300207530ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class ChangePrompt < Pry::ClassCommand match 'change-prompt' group 'Input and Output' description 'Change the current prompt.' command_options argument_required: true banner <<-BANNER Usage: change-prompt [OPTIONS] [NAME] Change the current prompt. BANNER def options(opt) opt.on(:l, :list, 'List the available prompts') end def process(prompt) if opts.present?(:l) list_prompts else change_prompt(prompt) end end private def list_prompts prompts = Pry::Prompt.all.map do |name, prompt| "#{bold(name)}#{red(' (selected)') if pry_instance.prompt == prompt}\n" + prompt.description end output.puts(prompts.join("\n" * 2)) end def change_prompt(prompt) if Pry::Prompt[prompt] pry_instance.prompt = Pry::Prompt[prompt] else raise Pry::CommandError, "'#{prompt}' isn't a known prompt. Run `change-prompt --list` " \ "to see the list of known prompts." end end Pry::Commands.add_command(self) end end end pry-0.13.1/lib/pry/commands/clear_screen.rb000066400000000000000000000010571364454223300205520ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class ClearScreen < Pry::ClassCommand match 'clear-screen' group 'Input and Output' description 'Clear the contents of the screen/window Pry is running in.' def process if Pry::Helpers::Platform.windows? pry_instance.config.system.call(pry_instance.output, 'cls', pry_instance) else pry_instance.config.system.call(pry_instance.output, 'clear', pry_instance) end end Pry::Commands.add_command(self) end end end pry-0.13.1/lib/pry/commands/code_collector.rb000066400000000000000000000125661364454223300211140ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class CodeCollector include Helpers::CommandHelpers attr_reader :args attr_reader :opts attr_reader :pry_instance # The name of the explicitly given file (if any). attr_accessor :file class << self attr_accessor :input_expression_ranges attr_accessor :output_result_ranges end @input_expression_ranges = [] @output_result_ranges = [] def initialize(args, opts, pry_instance) @args = args @opts = opts @pry_instance = pry_instance end # Add the `--lines`, `-o`, `-i`, `-s`, `-d` options. def self.inject_options(opt) @input_expression_ranges = [] @output_result_ranges = [] opt.on :l, :lines, "Restrict to a subset of lines. Takes a line number " \ "or range", optional_argument: true, as: Range, default: 1..-1 opt.on :o, :out, "Select lines from Pry's output result history. " \ "Takes an index or range", optional_argument: true, as: Range, default: -5..-1 do |r| output_result_ranges << (r || (-5..-1)) end opt.on :i, :in, "Select lines from Pry's input expression history. " \ "Takes an index or range", optional_argument: true, as: Range, default: -5..-1 do |r| input_expression_ranges << (r || (-5..-1)) end opt.on :s, :super, "Select the 'super' method. Can be repeated to " \ "traverse the ancestors", as: :count opt.on :d, :doc, "Select lines from the code object's documentation" end # The content (i.e code/docs) for the selected object. # If the user provided a bare code object, it returns the source. # If the user provided the `-i` or `-o` switches, it returns the # selected input/output lines joined as a string. If the user used # `-d CODE_OBJECT` it returns the docs for that code object. # # @return [String] def content @content ||= begin if bad_option_combination? raise CommandError, "Only one of --out, --in, --doc and CODE_OBJECT may " \ "be specified." end content = if opts.present?(:o) pry_output_content elsif opts.present?(:i) pry_input_content elsif opts.present?(:d) code_object_doc else code_object_source_or_file end restrict_to_lines(content, line_range) end end # The code object # # @return [Pry::WrappedModule, Pry::Method, Pry::Command] def code_object Pry::CodeObject.lookup(obj_name, pry_instance, super: opts[:super]) end # Given a string and a range, return the `range` lines of that # string. # # @param [String] content # @param [Range, Fixnum] range # @return [String] The string restricted to the given range def restrict_to_lines(content, range) Array(content.lines.to_a[range]).join end # The selected `pry_instance.output_ring` as a string, as specified by # the `-o` switch. # # @return [String] def pry_output_content pry_array_content_as_string( pry_instance.output_ring, self.class.output_result_ranges, &:pretty_inspect ) end # The selected `pry_instance.input_ring` as a string, as specified by # the `-i` switch. # # @return [String] def pry_input_content pry_array_content_as_string( pry_instance.input_ring, self.class.input_expression_ranges ) { |v| v } end # The line range passed to `--lines`, converted to a 0-indexed range. def line_range opts.present?(:lines) ? one_index_range_or_number(opts[:lines]) : 0..-1 end # Name of the object argument def obj_name @obj_name ||= args.empty? ? "" : args.join(" ") end private def bad_option_combination? [opts.present?(:in), opts.present?(:out), !args.empty?].count(true) > 1 end def pry_array_content_as_string(array, ranges) all = '' ranges.each do |range| if convert_to_range(range).first == 0 raise CommandError, "Minimum value for range is 1, not 0." end ranged_array = Array(array[range]) || [] ranged_array.compact.each { |v| all += yield(v) } end all end def code_object_doc (code_object && code_object.doc) || could_not_locate(obj_name) end def code_object_source_or_file (code_object && code_object.source) || file_content end def file_content if File.exist?(obj_name) # Set the file accessor. self.file = obj_name File.read(obj_name) else could_not_locate(obj_name) end end def could_not_locate(name) raise CommandError, "Cannot locate: #{name}!" end def convert_to_range(range) return range if range.is_a?(Range) (range..range) end end end end pry-0.13.1/lib/pry/commands/disable_pry.rb000066400000000000000000000016611364454223300204230ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class DisablePry < Pry::ClassCommand match 'disable-pry' group 'Navigating Pry' description 'Stops all future calls to pry and exits the current session.' banner <<-'BANNER' Usage: disable-pry After this command is run any further calls to pry will immediately return `nil` without interrupting the flow of your program. This is particularly useful when you've debugged the problem you were having, and now wish the program to run to the end. As alternatives, consider using `exit!` to force the current Ruby process to quit immediately; or using `edit -p` to remove the `binding.pry` from the code. BANNER def process ENV['DISABLE_PRY'] = 'true' pry_instance.run_command "exit" end end Pry::Commands.add_command(Pry::Command::DisablePry) end end pry-0.13.1/lib/pry/commands/easter_eggs.rb000066400000000000000000000057011364454223300204150ustar00rootroot00000000000000# frozen_string_literal: true class Pry Pry::Commands.instance_eval do command(%r{!s/(.*?)/(.*?)}, "") do |source, dest| eval_string.gsub!(/#{source}/) { dest } run "show-input" end command "east-coker", "" do txt = %( -- Now the light falls Across the open field, leaving the deep lane Shuttered with branches, dark in the afternoon, Where you lean against a bank while a van passes, And the deep lane insists on the direction Into the village, in the electric heat Hypnotised. In a warm haze the sultry light Is absorbed, not refracted, by grey stone. The dahlias sleep in the empty silence. Wait for the early owl. -- T.S Eliot ) output.puts txt txt end command "cohen-poem", "" do txt = %( -- When this American woman, whose thighs are bound in casual red cloth, comes thundering past my sitting place like a forest-burning Mongol tribe, the city is ravished and brittle buildings of a hundred years splash into the street; and my eyes are burnt for the embroidered Chinese girls, already old, and so small between the thin pines on these enormous landscapes, that if you turn your head they are lost for hours. -- Leonard Cohen ) output.puts txt txt end command "pessoa-poem", "" do output.puts <<-TEXT -- I've gone to bed with every feeling, I've been the pimp of every emotion, All felt sensations have bought me drinks, I've traded glances with every motive for every act, I've held hands with every urge to depart, .. Rage, foam, the vastness that doesn't fit in my handkerchief, The dog in heat howling in the night, The pond from the farm going in circles around my insomnia, The woods as they were, on our late-afternoon walks, the rose, The indifferent tuft of hair, the moss, the pines, The rage of not containing all this, not retaining all this, O abstract hunger for things, impotent libido for moments, Intellectual orgy of feeling life! -- Fernando Pessoa TEXT end command "test-ansi", "" do prev_color = pry_instance.config.color pry_instance.config.color = true picture = unindent <<-'OUTPUT'.gsub(/[[:alpha:]!]/) { |s| red(s) } ____ _______________________ / \ | A W G | / O O \ | N I O N ! | | | | S S R I ! | \ \__/ / __| I K ! | \____/ \________________________| OUTPUT move_up = if Helpers::Platform.windows_ansi? proc { |n| "\e[#{n}F" } else proc { |n| "\e[#{n}A\e[0G" } end output.puts "\n" * 6 output.puts picture.lines.map(&:chomp).reverse.join(move_up[1]) output.puts "\n" * 6 output.puts "** ENV['TERM'] is #{ENV['TERM']} **\n\n" pry_instance.config.color = prev_color end end end pry-0.13.1/lib/pry/commands/edit.rb000066400000000000000000000161141364454223300170520ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Edit < Pry::ClassCommand match 'edit' group 'Editing' description 'Invoke the default editor on a file.' banner <<-'BANNER' Usage: edit [--no-reload|--reload|--patch] [--line LINE] [--temp|--ex|FILE[:LINE]|OBJECT|--in N] Open a text editor. When no FILE is given, edits the pry input buffer. When a method/module/command is given, the code is opened in an editor. Ensure `Pry.config.editor` or `pry_instance.config.editor` is set to your editor of choice. edit sample.rb edit -p MyClass#my_method edit sample.rb --line 105 edit MyClass edit MyClass#my_method edit --ex edit --method edit --ex -p https://github.com/pry/pry/wiki/Editor-integration#wiki-Edit_command BANNER def options(opt) opt.on :e, :ex, "Open the file that raised the most recent exception " \ "(_ex_.file)", optional_argument: true, as: Integer opt.on :i, :in, "Open a temporary file containing the Nth input " \ "expression. N may be a range", optional_argument: true, as: Range, default: -1..-1 opt.on :t, :temp, "Open an empty temporary file" opt.on :l, :line, "Jump to this line in the opened file", argument: true, as: Integer opt.on :n, :"no-reload", "Don't automatically reload the edited file" opt.on :c, :current, "Open the current __FILE__ and at __LINE__ (as " \ "returned by `whereami`)" opt.on :r, :reload, "Reload the edited code immediately (default for " \ "ruby files)" opt.on :p, :patch, "Instead of editing the object's file, try to edit " \ "in a tempfile and apply as a monkey patch" opt.on :m, :method, "Explicitly edit the _current_ method (when " \ "inside a method context)." end def process if bad_option_combination? raise CommandError, "Only one of --ex, --temp, --in, --method and " \ "FILE may be specified." end if repl_edit? # code defined in pry, eval'd within pry. repl_edit elsif runtime_patch? # patch code without persisting changes, implies future changes are patches apply_runtime_patch else # code stored in actual files, eval'd at top-level file_edit end end def repl_edit? !opts.present?(:ex) && !opts.present?(:current) && !opts.present?(:method) && filename_argument.empty? end def repl_edit content = Pry::Editor.new(pry_instance).edit_tempfile_with_content( initial_temp_file_content, initial_temp_file_content.lines.count ) pry_instance.eval_string = content Pry.history.push(content) end def file_based_exception? opts.present?(:ex) && !opts.present?(:patch) end def runtime_patch? !file_based_exception? && (opts.present?(:patch) || previously_patched?(code_object) || pry_method?(code_object)) end def apply_runtime_patch if patch_exception? ExceptionPatcher.new( pry_instance, state, file_and_line_for_current_exception ).perform_patch elsif code_object.is_a?(Pry::Method) code_object.redefine( Pry::Editor.new(pry_instance).edit_tempfile_with_content( code_object.source ) ) else raise NotImplementedError, "Cannot yet patch #{code_object} objects!" end end def ensure_file_name_is_valid(file_name) unless file_name raise CommandError, "Cannot find a valid file for #{filename_argument}" end return unless not_a_real_file?(file_name) raise CommandError, "#{file_name} is not a valid file name, cannot edit!" end def file_and_line_for_current_exception FileAndLineLocator.from_exception(pry_instance.last_exception, opts[:ex].to_i) end def file_and_line file_name, line = if opts.present?(:current) FileAndLineLocator.from_binding(target) elsif opts.present?(:ex) file_and_line_for_current_exception elsif code_object FileAndLineLocator.from_code_object(code_object, filename_argument) else # when file and line are passed as a single arg, e.g my_file.rb:30 FileAndLineLocator.from_filename_argument(filename_argument) end [file_name, opts.present?(:line) ? opts[:l].to_i : line] end def file_edit file_name, line = file_and_line ensure_file_name_is_valid(file_name) Pry::Editor.new(pry_instance).invoke_editor(file_name, line, reload?(file_name)) set_file_and_dir_locals(file_name) return unless reload?(file_name) silence_warnings { load(file_name) } end def filename_argument args.join(' ') end def code_object @code_object ||= !probably_a_file?(filename_argument) && Pry::CodeObject.lookup(filename_argument, pry_instance) end def pry_method?(code_object) code_object.is_a?(Pry::Method) && code_object.pry_method? end def previously_patched?(code_object) code_object.is_a?(Pry::Method) && Pry::Method::Patcher.code_for(code_object.source_location.first) end def patch_exception? opts.present?(:ex) && opts.present?(:patch) end def bad_option_combination? [ opts.present?(:ex), opts.present?(:temp), opts.present?(:in), opts.present?(:method), !filename_argument.empty? ].count(true) > 1 end def input_expression case opts[:i] when Range (pry_instance.input_ring[opts[:i]] || []).join when Integer pry_instance.input_ring[opts[:i]] || "" else raise Pry::CommandError, "Not a valid range: #{opts[:i]}" end end def reloadable? opts.present?(:reload) || opts.present?(:ex) end def never_reload? opts.present?(:'no-reload') || pry_instance.config.disable_auto_reload end def reload?(file_name = "") (reloadable? || file_name.end_with?(".rb")) && !never_reload? end def initial_temp_file_content if opts.present?(:temp) "" elsif opts.present?(:in) input_expression elsif eval_string.strip != "" eval_string else pry_instance.input_ring.to_a.reverse_each.find { |x| x && x.strip != "" } || "" end end def probably_a_file?(str) [".rb", ".c", ".py", ".yml", ".gemspec"].include?(File.extname(str)) || str =~ %r{/|\\} end end Pry::Commands.add_command(Pry::Command::Edit) end end pry-0.13.1/lib/pry/commands/edit/000077500000000000000000000000001364454223300165225ustar00rootroot00000000000000pry-0.13.1/lib/pry/commands/edit/exception_patcher.rb000066400000000000000000000014151364454223300225540ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Edit class ExceptionPatcher attr_accessor :pry_instance attr_accessor :state attr_accessor :file_and_line def initialize(pry_instance, state, exception_file_and_line) @pry_instance = pry_instance @state = state @file_and_line = exception_file_and_line end # perform the patch def perform_patch file_name, = file_and_line lines = state.dynamical_ex_file || File.read(file_name) source = Pry::Editor.new(pry_instance).edit_tempfile_with_content(lines) pry_instance.evaluate_ruby source state.dynamical_ex_file = source.split("\n") end end end end end pry-0.13.1/lib/pry/commands/edit/file_and_line_locator.rb000066400000000000000000000026541364454223300233510ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Edit module FileAndLineLocator class << self def from_binding(target) if target.respond_to?(:source_location) target.source_location else target.eval("[__FILE__, __LINE__]") end end def from_code_object(code_object, filename_argument) unless File.exist?(code_object.source_file.to_s) raise CommandError, "Cannot find a file for #{filename_argument}!" end [code_object.source_file, code_object.source_line] end def from_exception(exception, backtrace_level) raise CommandError, "No exception found." if exception.nil? file_name, line = exception.bt_source_location_for(backtrace_level) raise CommandError, "Exception has no associated file." if file_name.nil? if Pry.eval_path == file_name raise CommandError, "Cannot edit exceptions raised in REPL." end [file_name, line] end # when file and line are passed as a single arg, e.g my_file.rb:30 def from_filename_argument(filename_argument) f = File.expand_path(filename_argument) l = f.sub!(/:(\d+)$/, "") ? Regexp.last_match(1).to_i : 1 [f, l] end end end end end end pry-0.13.1/lib/pry/commands/exit.rb000066400000000000000000000024301364454223300170720ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Exit < Pry::ClassCommand match 'exit' group 'Navigating Pry' description 'Pop the previous binding.' command_options keep_retval: true banner <<-'BANNER' Usage: exit [OPTIONS] [--help] Aliases: quit Pop the previous binding (does NOT exit program). It can be useful to exit a context with a user-provided value. For instance an exit value can be used to determine program flow. exit "pry this" exit https://github.com/pry/pry/wiki/State-navigation#wiki-Exit_with_value BANNER def process if pry_instance.binding_stack.one? pry_instance.run_command "exit-all #{arg_string}" else # otherwise just pop a binding and return user supplied value process_pop_and_return end end def process_pop_and_return popped_object = pry_instance.binding_stack.pop.eval('self') # return a user-specified value if given otherwise return the object return target.eval(arg_string) unless arg_string.empty? popped_object end end Pry::Commands.add_command(Pry::Command::Exit) Pry::Commands.alias_command 'quit', 'exit' end end pry-0.13.1/lib/pry/commands/exit_all.rb000066400000000000000000000014371364454223300177300ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class ExitAll < Pry::ClassCommand match 'exit-all' group 'Navigating Pry' description 'End the current Pry session.' banner <<-'BANNER' Usage: exit-all [--help] Aliases: !!@ End the current Pry session (popping all bindings and returning to caller). Accepts optional return value. BANNER def process # calculate user-given value exit_value = target.eval(arg_string) # clear the binding stack pry_instance.binding_stack.clear # break out of the repl loop throw(:breakout, exit_value) end end Pry::Commands.add_command(Pry::Command::ExitAll) Pry::Commands.alias_command '!!@', 'exit-all' end end pry-0.13.1/lib/pry/commands/exit_program.rb000066400000000000000000000011721364454223300206230ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class ExitProgram < Pry::ClassCommand match 'exit-program' group 'Navigating Pry' description 'End the current program.' banner <<-'BANNER' Usage: exit-program [--help] Aliases: quit-program !!! End the current program. BANNER def process Kernel.exit target.eval(arg_string).to_i end end Pry::Commands.add_command(Pry::Command::ExitProgram) Pry::Commands.alias_command 'quit-program', 'exit-program' Pry::Commands.alias_command '!!!', 'exit-program' end end pry-0.13.1/lib/pry/commands/find_method.rb000066400000000000000000000135231364454223300204060ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class FindMethod < Pry::ClassCommand extend Pry::Helpers::BaseHelpers match 'find-method' group 'Context' description 'Recursively search for a method within a Class/Module or ' \ 'the current namespace.' command_options shellwords: false banner <<-'BANNER' Usage: find-method [-n|-c] METHOD [NAMESPACE] Recursively search for a method within a Class/Module or the current namespace. Use the `-n` switch (the default) to search for methods whose name matches the given regex. Use the `-c` switch to search for methods that contain the given code. # Find all methods whose name match /re/ inside # the Pry namespace. Matches Pry#repl, etc. find-method re Pry # Find all methods that contain the code: # output.puts inside the Pry namespace. find-method -c 'output.puts' Pry BANNER def options(opt) opt.on :n, :name, "Search for a method by name" opt.on :c, :content, "Search for a method based on content in Regex form" end def process return if args.empty? klass = search_class matches = opts.content? ? content_search(klass) : name_search(klass) show_search_results(matches) end private # @return [Regexp] The pattern to search for. def pattern @pattern ||= ::Regexp.new args[0] end # Output the result of the search. # # @param [Array] matches def show_search_results(matches) if matches.empty? output.puts bold("No Methods Matched") else print_matches(matches) end end # The class to search for methods. # We only search classes, so if the search object is an # instance, return its class. If no search object is given # search `target_self`. def search_class klass = if args[1] target.eval(args[1]) else target_self end klass.is_a?(Module) ? klass : klass.class end # pretty-print a list of matching methods. # # @param [Array] matches def print_matches(matches) grouped = matches.group_by(&:owner) order = grouped.keys.sort_by { |x| x.name || x.to_s } order.each do |klass| print_matches_for_class(klass, grouped) end end # Print matched methods for a class def print_matches_for_class(klass, grouped) output.puts bold(klass.name) grouped[klass].each do |method| header = method.name_with_owner output.puts header + additional_info(header, method) end end # Return the matched lines of method source if `-c` is given or "" # if `-c` was not given def additional_info(header, method) if opts.content? ': ' + colorize_code(matched_method_lines(header, method)) else "" end end def matched_method_lines(header, method) method.source.split(/\n/).select { |x| x =~ pattern }.join( "\n#{' ' * header.length}" ) end # Run the given block against every constant in the provided namespace. # # @param [Module] klass The namespace in which to start the search. # @param [Hash] done The namespaces we've already visited (private) # @yieldparam klass Each class/module in the namespace. # def recurse_namespace(klass, done = {}, &block) return if !klass.is_a?(Module) || done[klass] done[klass] = true yield klass klass.constants.each do |name| next if klass.autoload?(name) begin const = klass.const_get(name) rescue RescuableException # rubocop:disable Lint/HandleExceptions # constant loading is an inexact science at the best of times, # this often happens when a constant was .autoload? but someone # tried to load it. It's now not .autoload? but will still raise # a NameError when you access it. else recurse_namespace(const, done, &block) end end end # Gather all the methods in a namespace that pass the given block. # # @param [Module] namespace The namespace in which to search. # @yieldparam [Method] method The method to test # @yieldreturn [Boolean] # @return [Array] # def search_all_methods(namespace) done = Hash.new { |h, k| h[k] = {} } matches = [] recurse_namespace(namespace) do |klass| methods = Pry::Method.all_from_class(klass) + Pry::Method.all_from_obj(klass) methods.each do |method| next if done[method.owner][method.name] done[method.owner][method.name] = true matches << method if yield method end end matches end # Search for all methods with a name that matches the given regex # within a namespace. # # @param [Module] namespace The namespace to search # @return [Array] # def name_search(namespace) search_all_methods(namespace) do |meth| meth.name =~ pattern end end # Search for all methods who's implementation matches the given regex # within a namespace. # # @param [Module] namespace The namespace to search # @return [Array] # def content_search(namespace) search_all_methods(namespace) do |meth| begin meth.source =~ pattern rescue RescuableException false end end end end Pry::Commands.add_command(Pry::Command::FindMethod) end end pry-0.13.1/lib/pry/commands/fix_indent.rb000066400000000000000000000007621364454223300202560ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class FixIndent < Pry::ClassCommand match 'fix-indent' group 'Input and Output' description "Correct the indentation for contents of the input buffer" banner <<-USAGE Usage: fix-indent USAGE def process indented_str = Pry::Indent.indent(eval_string) pry_instance.eval_string = indented_str end end Pry::Commands.add_command(Pry::Command::FixIndent) end end pry-0.13.1/lib/pry/commands/help.rb000066400000000000000000000122451364454223300170560ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Help < Pry::ClassCommand match 'help' group 'Help' description 'Show a list of commands or information about a specific command.' banner <<-'BANNER' Usage: help [COMMAND] With no arguments, help lists all the available commands along with their descriptions. When given a command name as an argument, shows the help for that command. BANNER # We only want to show commands that have descriptions, so that the # easter eggs don't show up. def visible_commands visible = {} commands.each do |key, command| visible[key] = command if command.description && !command.description.empty? end visible end # Get a hash of available commands grouped by the "group" name. def command_groups visible_commands.values.group_by(&:group) end def process if args.empty? display_index(command_groups) else display_search(args.first) end end # Display the index view, with headings and short descriptions per command. # # @param [Hash>] groups def display_index(groups) help_text = [] sorted_group_names(groups).each do |group_name| commands = sorted_commands(groups[group_name]) help_text << help_text_for_commands(group_name, commands) if commands.any? end pry_instance.pager.page help_text.join("\n\n") end # Given a group name and an array of commands, # return the help string for those commands. # # @param [String] name The group name. # @param [Array] commands # @return [String] The generated help string. def help_text_for_commands(name, commands) "#{bold(name.capitalize)}\n" + commands.map do |command| " #{command.options[:listing].to_s.ljust(18)} " \ "#{command.description.capitalize}" end.join("\n") end # @param [Hash] groups # @return [Array] An array of sorted group names. def sorted_group_names(groups) groups.keys.sort_by(&method(:group_sort_key)) end # Sort an array of commands by their `listing` name. # # @param [Array] commands The commands to sort # @return [Array] commands sorted by listing name. def sorted_commands(commands) commands.sort_by { |command| command.options[:listing].to_s } end # Display help for an individual command or group. # # @param [String] search The string to search for. def display_search(search) if (command = command_set.find_command_for_help(search)) display_command(command) else display_filtered_search_results(search) end end # Display help for a searched item, filtered first by group # and if that fails, filtered by command name. # # @param [String] search The string to search for. def display_filtered_search_results(search) groups = search_hash(search, command_groups) if !groups.empty? display_index(groups) else display_filtered_commands(search) end end # Display help for a searched item, filtered by group # # @param [String] search The string to search for. def display_filtered_commands(search) filtered = search_hash(search, visible_commands) raise CommandError, "No help found for '#{args.first}'" if filtered.empty? if filtered.size == 1 display_command(filtered.values.first) else display_index("'#{search}' commands" => filtered.values) end end # Display help for an individual command. # # @param [Pry::Command] command def display_command(command) pry_instance.pager.page command.new.help end # Find a subset of a hash that matches the user's search term. # # If there's an exact match a Hash of one element will be returned, # otherwise a sub-Hash with every key that matches the search will # be returned. # # @param [String] search the search term # @param [Hash] hash the hash to search def search_hash(search, hash) matching = {} hash.each_pair do |key, value| next unless key.is_a?(String) return { key => value } if normalize(key) == normalize(search) next unless normalize(key).start_with?(normalize(search)) matching[key] = value end matching end # Clean search terms to make it easier to search group names # # @param [String] key # @return [String] def normalize(key) key.downcase.gsub(/pry\W+/, '') end def group_sort_key(group_name) [ %w[ Help Context Editing Introspection Input_and_output Navigating_pry Gems Basic Commands ].index(group_name.tr(' ', '_')) || 99, group_name ] end end Pry::Commands.add_command(Pry::Command::Help) end end pry-0.13.1/lib/pry/commands/hist.rb000066400000000000000000000132531364454223300170750ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Hist < Pry::ClassCommand match 'hist' group 'Editing' description 'Show and replay Readline history.' banner <<-'BANNER' Usage: hist [--head|--tail] hist --all hist --head N hist --tail N hist --show START..END hist --grep PATTERN hist --clear hist --replay START..END hist --save [START..END] FILE Aliases: history Show and replay Readline history. BANNER def options(opt) opt.on :a, :all, "Display all history" opt.on :H, :head, "Display the first N items", optional_argument: true, as: Integer opt.on :T, :tail, "Display the last N items", optional_argument: true, as: Integer opt.on :s, :show, "Show the given range of lines", optional_argument: true, as: Range opt.on :G, :grep, "Show lines matching the given pattern", argument: true, as: String opt.on :c, :clear, "Clear the current session's history" opt.on :r, :replay, "Replay a line or range of lines", argument: true, as: Range opt.on :save, "Save history to a file", argument: true, as: Range opt.on :e, :'exclude-pry', "Exclude Pry commands from the history" opt.on :n, :'no-numbers', "Omit line numbers" end def process @history = find_history @history = @history.between(opts[:show]) if opts.present?(:show) @history = @history.grep(opts[:grep]) if opts.present?(:grep) @history = if opts.present?(:head) @history.take_lines(1, opts[:head] || 10) elsif opts.present?(:tail) @history.take_lines(-(opts[:tail] || 10), opts[:tail] || 10) else @history end if opts.present?(:'exclude-pry') @history = @history.reject do |loc| command_set.valid_command?(loc.line) end end if opts.present?(:save) process_save elsif opts.present?(:clear) process_clear elsif opts.present?(:replay) process_replay else process_display end end private def process_display @history = @history.with_line_numbers unless opts.present?(:'no-numbers') pry_instance.pager.open do |pager| @history.print_to_output(pager, true) end end def process_save case opts[:save] when Range @history = @history.between(opts[:save]) raise CommandError, "Must provide a file name." unless args.first file_name = File.expand_path(args.first) when String file_name = File.expand_path(opts[:save]) end output.puts "Saving history in #{file_name}..." File.open(file_name, 'w') { |f| f.write(@history.raw) } output.puts "History saved." end def process_clear Pry.history.clear output.puts "History cleared." end def process_replay @history = @history.between(opts[:r]) replay_sequence = @history.raw # If we met follow-up "hist" call, check for the "--replay" option # presence. If "hist" command is called with other options, proceed # further. check_for_juxtaposed_replay(replay_sequence) replay_sequence.lines.each do |line| pry_instance.eval line, generated: true end end # Checks +replay_sequence+ for the presence of neighboring replay calls. # @example # [1] pry(main)> hist --show 46894 # 46894: hist --replay 46675..46677 # [2] pry(main)> hist --show 46675..46677 # 46675: 1+1 # 46676: a = 100 # 46677: hist --tail # [3] pry(main)> hist --replay 46894 # Error: Replay index 46894 points out to another replay call: # `hist -r 46675..46677` # [4] pry(main)> # # @raise [Pry::CommandError] If +replay_sequence+ contains another # "hist --replay" call # @param [String] replay_sequence The sequence of commands to be replayed # (per saltum) # @return [Boolean] `false` if +replay_sequence+ does not contain another # "hist --replay" call def check_for_juxtaposed_replay(replay_sequence) if replay_sequence =~ /\Ahist(?:ory)?\b/ # Create *fresh* instance of Options for parsing of "hist" command. slop_instance = slop slop_instance.parse(replay_sequence.split(' ')[1..-1]) if slop_instance.present?(:r) replay_sequence = replay_sequence.split("\n").join('; ') index = opts[:r] index = index.min if index.min == index.max || index.max.nil? raise CommandError, "Replay index #{index} points out to another replay call: " \ "`#{replay_sequence}`" end else false end end # Finds history depending on the given switch. # # @return [Pry::Code] if it finds `--all` (or `-a`) switch, returns all # entries in history. Without the switch returns only the entries from the # current Pry session. def find_history h = if opts.present?(:all) Pry.history.to_a else Pry.history.to_a.last(Pry.history.session_line_count) end Pry::Code(Pry.history.filter(h[0..-2])) end end Pry::Commands.add_command(Pry::Command::Hist) Pry::Commands.alias_command 'history', 'hist' end end pry-0.13.1/lib/pry/commands/import_set.rb000066400000000000000000000012621364454223300203100ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class ImportSet < Pry::ClassCommand match 'import-set' group 'Commands' # TODO: Provide a better description with examples and a general conception # of this command. description 'Import a Pry command set.' banner <<-'BANNER' Import a Pry command set. BANNER # TODO: resolve unused parameter. def process(_command_set_name) raise CommandError, "Provide a command set name" if command_set.nil? set = target.eval(arg_string) pry_instance.commands.import set end end Pry::Commands.add_command(Pry::Command::ImportSet) end end pry-0.13.1/lib/pry/commands/jump_to.rb000066400000000000000000000016761364454223300176110ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class JumpTo < Pry::ClassCommand match 'jump-to' group 'Navigating Pry' description 'Jump to a binding further up the stack.' banner <<-'BANNER' Jump to a binding further up the stack, popping all bindings below. BANNER def process(break_level) break_level = break_level.to_i nesting_level = pry_instance.binding_stack.size - 1 max_nest_level = nesting_level - 1 case break_level when nesting_level output.puts "Already at nesting level #{nesting_level}" when 0..max_nest_level pry_instance.binding_stack = pry_instance.binding_stack[0..break_level] else output.puts "Invalid nest level. Must be between 0 and " \ "#{max_nest_level}. Got #{break_level}." end end end Pry::Commands.add_command(Pry::Command::JumpTo) end end pry-0.13.1/lib/pry/commands/list_inspectors.rb000066400000000000000000000020361364454223300213470ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class ListInspectors < Pry::ClassCommand match 'list-inspectors' group 'Input and Output' description 'List the inspector procs available for use.' banner <<-BANNER Usage: list-inspectors List the inspector procs available to print return values. You can use change-inspector to switch between them. BANNER def process output.puts heading("Available inspectors") + "\n" inspector_map.each do |name, inspector| output.write "Name: #{bold(name)}" output.puts selected_inspector?(inspector) ? selected_text : "" output.puts inspector[:description] output.puts end end private def inspector_map Pry::Inspector::MAP end def selected_text red " (selected) " end def selected_inspector?(inspector) pry_instance.print == inspector[:value] end Pry::Commands.add_command(self) end end end pry-0.13.1/lib/pry/commands/ls.rb000066400000000000000000000130511364454223300165400ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Ls < Pry::ClassCommand DEFAULT_OPTIONS = { heading_color: :bright_blue, public_method_color: :default, private_method_color: :blue, protected_method_color: :blue, method_missing_color: :bright_red, local_var_color: :yellow, pry_var_color: :default, # e.g. _, pry_instance, _file_ instance_var_color: :blue, # e.g. @foo class_var_color: :bright_blue, # e.g. @@foo global_var_color: :default, # e.g. $CODERAY_DEBUG, $eventmachine_library builtin_global_color: :cyan, # e.g. $stdin, $-w, $PID pseudo_global_color: :cyan, # e.g. $~, $1..$9, $LAST_MATCH_INFO constant_color: :default, # e.g. VERSION, ARGF class_constant_color: :blue, # e.g. Object, Kernel exception_constant_color: :magenta, # e.g. Exception, RuntimeError unloaded_constant_color: :yellow, # Any constant that is still in .autoload? state separator: " ", ceiling: [Object, Module, Class] }.freeze match 'ls' group 'Context' description 'Show the list of vars and methods in the current scope.' command_options shellwords: false, interpolate: false banner <<-'BANNER' Usage: ls [-m|-M|-p|-pM] [-q|-v] [-c|-i] [Object] ls [-g] [-l] ls shows you which methods, constants and variables are accessible to Pry. By default it shows you the local variables defined in the current shell, and any public methods or instance variables defined on the current object. The colours used are configurable using Pry.config.ls.*_color, and the separator is Pry.config.ls.separator. Pry.config.ls.ceiling is used to hide methods defined higher up in the inheritance chain, this is by default set to [Object, Module, Class] so that methods defined on all Objects are omitted. The -v flag can be used to ignore this setting and show all methods, while the -q can be used to set the ceiling much lower and show only methods defined on the object or its direct class. Also check out `find-method` command (run `help find-method`). BANNER def options(opt) opt.on :m, :methods, "Show public methods defined on the Object" opt.on :M, "instance-methods", "Show public methods defined in a " \ "Module or Class" opt.on :p, :ppp, "Show public, protected (in yellow) and private " \ "(in green) methods" opt.on :q, :quiet, "Show only methods defined on object.singleton_class " \ "and object.class" opt.on :v, :verbose, "Show methods and constants on all super-classes " \ "(ignores Pry.config.ls.ceiling)" opt.on :g, :globals, "Show global variables, including those builtin to " \ "Ruby (in cyan)" opt.on :l, :locals, "Show hash of local vars, sorted by descending size" opt.on :c, :constants, "Show constants, highlighting classes (in blue), " \ "and exceptions (in purple).\n" \ "#{' ' * 32}Constants that are pending autoload? " \ "are also shown (in yellow)" opt.on :i, :ivars, "Show instance variables (in blue) and class " \ "variables (in bright blue)" opt.on :G, :grep, "Filter output by regular expression", argument: true if Object.respond_to?(:deprecate_constant) opt.on :d, :dconstants, "Show deprecated constants" end return unless Helpers::Platform.jruby? opt.on :J, "all-java", "Show all the aliases for methods from java " \ "(default is to show only prettiest)" end # Exclude -q, -v and --grep because they, # don't specify what the user wants to see. def no_user_opts? !(opts[:methods] || opts['instance-methods'] || opts[:ppp] || opts[:globals] || opts[:locals] || opts[:constants] || opts[:ivars]) end def process @interrogatee = args.empty? ? target_self : target.eval(args.join(' ')) raise_errors_if_arguments_are_weird ls_entity = LsEntity.new( interrogatee: @interrogatee, no_user_opts: no_user_opts?, opts: opts, args: args, pry_instance: pry_instance ) pry_instance.pager.page ls_entity.entities_table end private def error_list any_args = args.any? # rubocop:disable Style/CaseEquality non_mod_interrogatee = !(Module === @interrogatee) # rubocop:enable Style/CaseEquality [ ['-l does not make sense with a specified Object', :locals, any_args], ['-g does not make sense with a specified Object', :globals, any_args], ['-q does not make sense with -v', :quiet, opts.present?(:verbose)], [ '-M only makes sense with a Module or a Class', 'instance-methods', non_mod_interrogatee ], [ '-c only makes sense with a Module or a Class', :constants, any_args && non_mod_interrogatee ] ] end def raise_errors_if_arguments_are_weird error_list.each do |message, option, invalid_expr| raise Pry::CommandError, message if opts.present?(option) && invalid_expr end end end Pry::Commands.add_command(Pry::Command::Ls) end end pry-0.13.1/lib/pry/commands/ls/000077500000000000000000000000001364454223300162135ustar00rootroot00000000000000pry-0.13.1/lib/pry/commands/ls/constants.rb000066400000000000000000000043421364454223300205570ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Ls < Pry::ClassCommand class Constants < Pry::Command::Ls::Formatter DEPRECATED_CONSTANTS = [ :Data, :Fixnum, :Bignum, :TimeoutError, :NIL, :FALSE, :TRUE ].tap do |constants| constants << :JavaPackageModuleTemplate if Helpers::Platform.jruby? end include Pry::Command::Ls::Interrogatable def initialize(interrogatee, no_user_opts, opts, pry_instance) super(pry_instance) @interrogatee = interrogatee @no_user_opts = no_user_opts @default_switch = opts[:constants] @verbose_switch = opts[:verbose] @dconstants = opts.dconstants? end def correct_opts? super || (@no_user_opts && interrogating_a_module?) end def output_self mod = interrogatee_mod constants = WrappedModule.new(mod).constants(@verbose_switch) output_section('constants', grep.regexp[format(mod, constants)]) end private def show_deprecated_constants? @dconstants == true end def format(mod, constants) constants.sort_by(&:downcase).map do |name| if Object.respond_to?(:deprecate_constant) && DEPRECATED_CONSTANTS.include?(name) && !show_deprecated_constants? next end if (const = (begin !mod.autoload?(name) && (mod.const_get(name) || true) rescue StandardError nil end)) if begin const < Exception rescue StandardError false end color(:exception_constant, name) elsif begin mod.const_get(name).is_a?(Module) rescue StandardError false end color(:class_constant, name) else color(:constant, name) end else color(:unloaded_constant, name) end end end end end end end pry-0.13.1/lib/pry/commands/ls/formatter.rb000066400000000000000000000023561364454223300205510ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Ls < Pry::ClassCommand class Formatter attr_writer :grep attr_reader :pry_instance def initialize(pry_instance) @pry_instance = pry_instance @target = pry_instance.current_context @default_switch = nil end def write_out return false unless correct_opts? output_self end private def color(type, str) Pry::Helpers::Text.send pry_instance.config.ls.send("#{type}_color"), str end # Add a new section to the output. # Outputs nothing if the section would be empty. def output_section(heading, body) return '' if body.compact.empty? fancy_heading = Pry::Helpers::Text.bold(color(:heading, heading)) Pry::Helpers.tablify_or_one_line(fancy_heading, body, @pry_instance) end def format_value(value) Pry::ColorPrinter.pp(value, ''.dup) end def correct_opts? @default_switch end def output_self raise NotImplementedError end def grep @grep || proc { |x| x } end end end end end pry-0.13.1/lib/pry/commands/ls/globals.rb000066400000000000000000000035131364454223300201650ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Ls < Pry::ClassCommand class Globals < Pry::Command::Ls::Formatter # Taken from "puts global_variables.inspect". BUILTIN_GLOBALS = %w[$" $$ $* $, $-0 $-F $-I $-K $-W $-a $-d $-i $-l $-p $-v $-w $. $/ $\\ $: $; $< $= $> $0 $ARGV $CONSOLE $DEBUG $DEFAULT_INPUT $DEFAULT_OUTPUT $FIELD_SEPARATOR $FILENAME $FS $IGNORECASE $INPUT_LINE_NUMBER $INPUT_RECORD_SEPARATOR $KCODE $LOADED_FEATURES $LOAD_PATH $NR $OFS $ORS $OUTPUT_FIELD_SEPARATOR $OUTPUT_RECORD_SEPARATOR $PID $PROCESS_ID $PROGRAM_NAME $RS $VERBOSE $deferr $defout $stderr $stdin $stdout].freeze # `$SAFE` and `$?` are thread-local, the exception stuff only works in a # rescue clause, everything else is basically a local variable with a `$` # in its name. PSEUDO_GLOBALS = %w[$! $' $& $` $@ $? $+ $_ $~ $1 $2 $3 $4 $5 $6 $7 $8 $9 $CHILD_STATUS $SAFE $ERROR_INFO $ERROR_POSITION $LAST_MATCH_INFO $LAST_PAREN_MATCH $LAST_READ_LINE $MATCH $POSTMATCH $PREMATCH].freeze def initialize(opts, pry_instance) super(pry_instance) @default_switch = opts[:globals] end def output_self variables = format(@target.eval('global_variables')) output_section('global variables', grep.regexp[variables]) end private def format(globals) globals.map(&:to_s).sort_by(&:downcase).map do |name| if PSEUDO_GLOBALS.include?(name) color(:pseudo_global, name) elsif BUILTIN_GLOBALS.include?(name) color(:builtin_global, name) else color(:global_var, name) end end end end end end end pry-0.13.1/lib/pry/commands/ls/grep.rb000066400000000000000000000006461364454223300175030ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Ls < Pry::ClassCommand class Grep def initialize(grep_regexp) @grep_regexp = grep_regexp end def regexp proc { |x| if x.instance_of?(Array) x.grep(@grep_regexp) else x =~ @grep_regexp end } end end end end end pry-0.13.1/lib/pry/commands/ls/instance_vars.rb000066400000000000000000000023341364454223300214010ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Ls < Pry::ClassCommand class InstanceVars < Pry::Command::Ls::Formatter include Pry::Command::Ls::Interrogatable def initialize(interrogatee, no_user_opts, opts, pry_instance) super(pry_instance) @interrogatee = interrogatee @no_user_opts = no_user_opts @default_switch = opts[:ivars] end def correct_opts? super || @no_user_opts end def output_self ivars = if Object === @interrogatee # rubocop:disable Style/CaseEquality Pry::Method.safe_send(@interrogatee, :instance_variables) else [] # TODO: BasicObject support end kvars = Pry::Method.safe_send(interrogatee_mod, :class_variables) ivars_out = output_section('instance variables', format(:instance_var, ivars)) kvars_out = output_section('class variables', format(:class_var, kvars)) ivars_out + kvars_out end private def format(type, vars) vars.sort_by { |var| var.to_s.downcase }.map { |var| color(type, var) } end end end end end pry-0.13.1/lib/pry/commands/ls/interrogatable.rb000066400000000000000000000010711364454223300215410ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Ls < Pry::ClassCommand module Interrogatable private def interrogating_a_module? Module === @interrogatee # rubocop:disable Style/CaseEquality end def interrogatee_mod if interrogating_a_module? @interrogatee else singleton = Pry::Method.singleton_class_of(@interrogatee) singleton.ancestors.grep(::Class).reject { |c| c == singleton }.first end end end end end end pry-0.13.1/lib/pry/commands/ls/jruby_hacks.rb000066400000000000000000000035051364454223300210470ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Ls < Pry::ClassCommand module JRubyHacks private # JRuby creates lots of aliases for methods imported from java in an attempt # to make life easier for ruby programmers. (e.g. getFooBar becomes # get_foo_bar and foo_bar, and maybe foo_bar? if it returns a Boolean). The # full transformations are in the assignAliases method of: # https://github.com/jruby/jruby/blob/master/src/org/jruby/javasupport/JavaClass.java # # This has the unfortunate side-effect of making the output of ls even more # incredibly verbose than it normally would be for these objects; and so we # filter out all but the nicest of these aliases here. # # TODO: This is a little bit vague, better heuristics could be used. # JRuby also has a lot of scala-specific logic, which we don't copy. def trim_jruby_aliases(methods) grouped = methods.group_by do |m| m.name.sub(/\A(is|get|set)(?=[A-Z_])/, '').gsub(/[_?=]/, '').downcase end grouped.flat_map do |_key, values| values = values.sort_by do |m| rubbishness(m.name) end found = [] values.select do |x| (found.none? { |y| x == y }) && found << x end end end # When removing jruby aliases, we want to keep the alias that is # "least rubbish" according to this metric. def rubbishness(name) name.each_char.map do |x| case x when /[A-Z]/ 1 when '?', '=', '!' -2 else 0 end end.inject(&:+) + (name.size / 100.0) end end end end end pry-0.13.1/lib/pry/commands/ls/local_names.rb000066400000000000000000000016251364454223300210210ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Ls < Pry::ClassCommand class LocalNames < Pry::Command::Ls::Formatter def initialize(no_user_opts, args, pry_instance) super(pry_instance) @no_user_opts = no_user_opts @args = args @sticky_locals = pry_instance.sticky_locals end def correct_opts? super || (@no_user_opts && @args.empty?) end def output_self local_vars = grep.regexp[@target.eval('local_variables')] output_section('locals', format(local_vars)) end private def format(locals) locals.sort_by(&:downcase).map do |name| if @sticky_locals.include?(name.to_sym) color(:pry_var, name) else color(:local_var, name) end end end end end end end pry-0.13.1/lib/pry/commands/ls/local_vars.rb000066400000000000000000000025171364454223300206720ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Ls < Pry::ClassCommand class LocalVars < Pry::Command::Ls::Formatter def initialize(opts, pry_instance) super(pry_instance) @default_switch = opts[:locals] @sticky_locals = pry_instance.sticky_locals end def output_self locals = @target.eval('local_variables').reject do |e| @sticky_locals.key?(e.to_sym) end name_value_pairs = locals.map do |name| [name, @target.eval(name.to_s)] end format(name_value_pairs).join('') end private def format(name_value_pairs) sorted = name_value_pairs.sort_by do |_name, value| value.to_s.size end sorted.reverse.map do |name, value| colorized_assignment_style(name, format_value(value)) end end def colorized_assignment_style(lhs, rhs, desired_width = 7) colorized_lhs = color(:local_var, lhs) color_escape_padding = colorized_lhs.size - lhs.size pad = desired_width + color_escape_padding Kernel.format( "%-#{pad}s = %s", name: color(:local_var, colorized_lhs), value: rhs ) end end end end end pry-0.13.1/lib/pry/commands/ls/ls_entity.rb000066400000000000000000000030561364454223300205560ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Ls < Pry::ClassCommand class LsEntity attr_reader :pry_instance def initialize(opts) @interrogatee = opts[:interrogatee] @no_user_opts = opts[:no_user_opts] @opts = opts[:opts] @args = opts[:args] @grep = Grep.new(Regexp.new(opts[:opts][:G] || '.')) @pry_instance = opts.delete(:pry_instance) end def entities_table entities.map(&:write_out).select { |o| o }.join('') end private def grep(entity) entity.tap { |o| o.grep = @grep } end def globals grep Globals.new(@opts, pry_instance) end def constants grep Constants.new(@interrogatee, @no_user_opts, @opts, pry_instance) end def methods grep(Methods.new(@interrogatee, @no_user_opts, @opts, pry_instance)) end def self_methods grep SelfMethods.new(@interrogatee, @no_user_opts, @opts, pry_instance) end def instance_vars grep InstanceVars.new(@interrogatee, @no_user_opts, @opts, pry_instance) end def local_names grep LocalNames.new(@no_user_opts, @args, pry_instance) end def local_vars LocalVars.new(@opts, pry_instance) end def entities [ globals, constants, methods, self_methods, instance_vars, local_names, local_vars ] end end end end end pry-0.13.1/lib/pry/commands/ls/methods.rb000066400000000000000000000036321364454223300202070ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Ls < Pry::ClassCommand class Methods < Pry::Command::Ls::Formatter include Pry::Command::Ls::Interrogatable include Pry::Command::Ls::MethodsHelper def initialize(interrogatee, no_user_opts, opts, pry_instance) super(pry_instance) @interrogatee = interrogatee @no_user_opts = no_user_opts @default_switch = opts[:methods] @instance_methods_switch = opts['instance-methods'] @ppp_switch = opts[:ppp] @jruby_switch = opts['all-java'] @quiet_switch = opts[:quiet] @verbose_switch = opts[:verbose] end def output_self methods = all_methods.group_by(&:owner) # Reverse the resolution order so that the most useful information # appears right by the prompt. resolution_order.take_while(&below_ceiling).reverse.map do |klass| methods_here = (methods[klass] || []).select { |m| grep.regexp[m.name] } heading = "#{Pry::WrappedModule.new(klass).method_prefix}methods" output_section(heading, format(methods_here)) end.join('') end private def correct_opts? super || @instance_methods_switch || @ppp_switch || @no_user_opts end # Get a lambda that can be used with `take_while` to prevent over-eager # traversal of the Object's ancestry graph. def below_ceiling ceiling = if @quiet_switch [Pry::Method.safe_send(interrogatee_mod, :ancestors)[1]] + pry_instance.config.ls.ceiling elsif @verbose_switch [] else pry_instance.config.ls.ceiling.dup end ->(klass) { !ceiling.include?(klass) } end end end end end pry-0.13.1/lib/pry/commands/ls/methods_helper.rb000066400000000000000000000027321364454223300215460ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Ls < Pry::ClassCommand module MethodsHelper include Pry::Command::Ls::JRubyHacks private # Get all the methods that we'll want to output. def all_methods(instance_methods = false) methods = if instance_methods || @instance_methods_switch Pry::Method.all_from_class(@interrogatee) else Pry::Method.all_from_obj(@interrogatee) end if Pry::Helpers::Platform.jruby? && !@jruby_switch methods = trim_jruby_aliases(methods) end methods.select { |method| @ppp_switch || method.visibility == :public } end def resolution_order if @instance_methods_switch Pry::Method.instance_resolution_order(@interrogatee) else Pry::Method.resolution_order(@interrogatee) end end def format(methods) methods.sort_by(&:name).map do |method| if method.name == 'method_missing' color(:method_missing, 'method_missing') elsif method.visibility == :private color(:private_method, method.name) elsif method.visibility == :protected color(:protected_method, method.name) else color(:public_method, method.name) end end end end end end end pry-0.13.1/lib/pry/commands/ls/self_methods.rb000066400000000000000000000016601364454223300212170ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Ls < Pry::ClassCommand class SelfMethods < Pry::Command::Ls::Formatter include Pry::Command::Ls::Interrogatable include Pry::Command::Ls::MethodsHelper def initialize(interrogatee, no_user_opts, opts, pry_instance) super(pry_instance) @interrogatee = interrogatee @no_user_opts = no_user_opts @ppp_switch = opts[:ppp] @jruby_switch = opts['all-java'] end def output_self methods = all_methods(true).select do |m| m.owner == @interrogatee && grep.regexp[m.name] end heading = "#{Pry::WrappedModule.new(@interrogatee).method_prefix}methods" output_section(heading, format(methods)) end private def correct_opts? @no_user_opts && interrogating_a_module? end end end end end pry-0.13.1/lib/pry/commands/nesting.rb000066400000000000000000000013171364454223300175730ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Nesting < Pry::ClassCommand match 'nesting' group 'Navigating Pry' description 'Show nesting information.' banner <<-'BANNER' Show nesting information. BANNER def process output.puts 'Nesting status:' output.puts '--' pry_instance.binding_stack.each_with_index do |obj, level| if level == 0 output.puts "#{level}. #{Pry.view_clip(obj.eval('self'))} (Pry top level)" else output.puts "#{level}. #{Pry.view_clip(obj.eval('self'))}" end end end end Pry::Commands.add_command(Pry::Command::Nesting) end end pry-0.13.1/lib/pry/commands/play.rb000066400000000000000000000063351364454223300170760ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Play < Pry::ClassCommand match 'play' group 'Editing' description 'Playback a string variable, method, line, or file as input.' banner <<-'BANNER' Usage: play [OPTIONS] [--help] The play command enables you to replay code from files and methods as if they were entered directly in the Pry REPL. play --lines 149..153 # assumes current context play -i 20 --lines 1..3 # assumes lines of the input expression at 20 play -o 4 # the output of an expression at 4 play Pry#repl -l 1..-1 # play the contents of Pry#repl method play -e 2 # play from specified line until end of valid expression play hello.rb # play a file play Rakefile -l 5 # play line 5 of a file play -d hi # play documentation of hi method play hi --open # play hi method and leave it open https://github.com/pry/pry/wiki/User-Input#wiki-Play BANNER def options(opt) CodeCollector.inject_options(opt) opt.on :open, 'Plays the selected content except the last line. Useful' \ ' for replaying methods and leaving the method definition' \ ' "open". `amend-line` can then be used to' \ ' modify the method.' opt.on :e, :expression=, 'Executes until end of valid expression', as: Integer opt.on :p, :print, 'Prints executed code' end def process @cc = CodeCollector.new(args, opts, pry_instance) perform_play show_input end def perform_play eval_string << content_after_options run "fix-indent" end def show_input return unless opts.present?(:print) return unless Pry::Code.complete_expression?(eval_string) run 'show-input' end def content_after_options if opts.present?(:open) restrict_to_lines(content, (0..-2)) elsif opts.present?(:expression) content_at_expression else content end end def content_at_expression code_object.expression_at(opts[:expression]) end def code_object Pry::Code.new(content) end def should_use_default_file? !args.first && !opts.present?(:in) && !opts.present?(:out) end def content if should_use_default_file? file_content else @cc.content end end # The file to play from when no code object is specified. # e.g `play --lines 4..10` def default_file file = if target.respond_to?(:source_location) target.source_location.first else target.eval("__FILE__") end file && File.expand_path(file) end def file_content if !default_file || !File.exist?(default_file) raise CommandError, "File does not exist! File was: #{default_file.inspect}" end @cc.restrict_to_lines(File.read(default_file), @cc.line_range) end end Pry::Commands.add_command(Pry::Command::Play) end end pry-0.13.1/lib/pry/commands/pry_backtrace.rb000066400000000000000000000016651364454223300207430ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class PryBacktrace < Pry::ClassCommand match 'pry-backtrace' group 'Context' description 'Show the backtrace for the Pry session.' banner <<-BANNER Usage: pry-backtrace [OPTIONS] [--help] Show the backtrace for the position in the code where Pry was started. This can be used to infer the behavior of the program immediately before it entered Pry, just like the backtrace property of an exception. NOTE: if you are looking for the backtrace of the most recent exception raised, just type: `_ex_.backtrace` instead. See: https://github.com/pry/pry/wiki/Special-Locals BANNER def process text = "#{bold('Backtrace:')}\n--\n#{pry_instance.backtrace.join("\n")}" pry_instance.pager.page(text) end end Pry::Commands.add_command(Pry::Command::PryBacktrace) end end pry-0.13.1/lib/pry/commands/pry_version.rb000066400000000000000000000006441364454223300205050ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Version < Pry::ClassCommand match 'pry-version' group 'Misc' description 'Show Pry version.' banner <<-'BANNER' Show Pry version. BANNER def process output.puts "Pry version: #{Pry::VERSION} on Ruby #{RUBY_VERSION}." end end Pry::Commands.add_command(Pry::Command::Version) end end pry-0.13.1/lib/pry/commands/raise_up.rb000066400000000000000000000023761364454223300177410ustar00rootroot00000000000000# frozen_string_literal: true class Pry # N.B. using a regular expresion here so that "raise-up 'foo'" does the right thing. class Command class RaiseUp < Pry::ClassCommand match(/raise-up(!?\b.*)/) group 'Context' description 'Raise an exception out of the current pry instance.' command_options listing: 'raise-up' banner <<-BANNER Raise up, like exit, allows you to quit pry. Instead of returning a value however, it raises an exception. If you don't provide the exception to be raised, it will use the most recent exception (in pry `_ex_`). When called as raise-up! (with an exclamation mark), this command raises the exception through any nested prys you have created by "cd"ing into objects. raise-up "get-me-out-of-here" # This is equivalent to the command above. raise "get-me-out-of-here" raise-up BANNER def process return _pry.pager.page help if captures[0] =~ /(-h|--help)\b/ # Handle 'raise-up', 'raise-up "foo"', 'raise-up RuntimeError, 'farble' # in a rubyesque manner target.eval("pry_instance.raise_up#{captures[0]}") end end Pry::Commands.add_command(Pry::Command::RaiseUp) end end pry-0.13.1/lib/pry/commands/reload_code.rb000066400000000000000000000043341364454223300203660ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class ReloadCode < Pry::ClassCommand match 'reload-code' group 'Misc' description 'Reload the source file that contains the specified code object.' banner <<-'BANNER' Reload the source file that contains the specified code object. e.g reload-code MyClass#my_method #=> reload a method reload-code MyClass #=> reload a class reload-code my-command #=> reload a pry command reload-code self #=> reload the current object reload-code #=> reload the current file or object BANNER def process if !args.empty? reload_object(args.join(" ")) elsif internal_binding?(target) reload_object("self") else reload_current_file end end private def current_file file = if target.respond_to?(:source_location) target.source_location.first else target.eval("__FILE__") end File.expand_path file end def reload_current_file unless File.exist?(current_file) raise CommandError, "Current file: #{current_file} cannot be found on disk!" end load current_file output.puts "The current file: #{current_file} was reloaded!" end def reload_object(identifier) code_object = Pry::CodeObject.lookup(identifier, pry_instance) check_for_reloadability(code_object, identifier) load code_object.source_file output.puts "#{identifier} was reloaded!" end def check_for_reloadability(code_object, identifier) if !code_object || !code_object.source_file raise CommandError, "Cannot locate #{identifier}!" end return if File.exist?(code_object.source_file) raise CommandError, "Cannot reload #{identifier} as it has no associated file on disk. " \ "File found was: #{code_object.source_file}" end end Pry::Commands.add_command(Pry::Command::ReloadCode) Pry::Commands.alias_command 'reload-method', 'reload-code' end end pry-0.13.1/lib/pry/commands/reset.rb000066400000000000000000000006431364454223300172470ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Reset < Pry::ClassCommand match 'reset' group 'Context' description 'Reset the REPL to a clean state.' banner <<-'BANNER' Reset the REPL to a clean state. BANNER def process output.puts 'Pry reset.' exec 'pry' end end Pry::Commands.add_command(Pry::Command::Reset) end end pry-0.13.1/lib/pry/commands/ri.rb000066400000000000000000000034661364454223300165450ustar00rootroot00000000000000# frozen_string_literal: true require 'stringio' class Pry class Command class Ri < Pry::ClassCommand match 'ri' group 'Introspection' description 'View ri documentation.' banner <<-'BANNER' Usage: ri [spec] View ri documentation. Relies on the "rdoc" gem being installed. See also "show-doc" command. ri Array#each BANNER def process(spec) unless spec return output.puts( "Please provide a class, module, or method name (e.g: ri Array#push)" ) end # Lazily load RI require 'rdoc/ri/driver' unless defined? RDoc::RI::PryDriver # Subclass RI so that it formats its output nicely, and uses `lesspipe`. subclass = Class.new(RDoc::RI::Driver) # the hard way. subclass.class_eval do def initialize(pager, opts) @pager = pager super opts end def page paging_text = StringIO.new yield paging_text @pager.page(paging_text.string) end def formatter(_io) if @formatter_klass @formatter_klass.new else RDoc::Markup::ToAnsi.new end end end RDoc::RI.const_set :PryDriver, subclass # hook it up! end # Spin-up an RI insance. ri = RDoc::RI::PryDriver.new( pry_instance.pager, use_stdout: true, interactive: false ) begin ri.display_names [spec] # Get the documentation (finally!) rescue RDoc::RI::Driver::NotFoundError => e output.puts "error: '#{e.name}' not found" end end end Pry::Commands.add_command(Pry::Command::Ri) end end pry-0.13.1/lib/pry/commands/save_file.rb000066400000000000000000000030311364454223300200540ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class SaveFile < Pry::ClassCommand match 'save-file' group 'Input and Output' description 'Export to a file using content from the REPL.' banner <<-'BANNER' Usage: save-file [OPTIONS] --to [FILE] Export to a file using content from the REPL. save-file my_method --to hello.rb save-file -i 1..10 --to hello.rb --append save-file show-method --to my_command.rb save-file sample_file.rb --lines 2..10 --to output_file.rb BANNER def options(opt) CodeCollector.inject_options(opt) opt.on :to=, "Specify the output file path" opt.on :a, :append, "Append output to file" end def process @cc = CodeCollector.new(args, opts, pry_instance) raise CommandError, "Found no code to save." if @cc.content.empty? if !file_name display_content else save_file end end def file_name opts[:to] || nil end def save_file File.open(file_name, mode) do |f| f.puts @cc.content end output.puts "#{file_name} successfully saved" end def display_content output.puts @cc.content output.puts "\n\n--\nPlease use `--to FILE` to export to a file." output.puts "No file saved!\n--" end def mode opts.present?(:append) ? "a" : "w" end end Pry::Commands.add_command(Pry::Command::SaveFile) end end pry-0.13.1/lib/pry/commands/shell_command.rb000066400000000000000000000036061364454223300207340ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class ShellCommand < Pry::ClassCommand match(/\.(.*)/) group 'Input and Output' description "All text following a '.' is forwarded to the shell." command_options listing: '.', use_prefix: false, takes_block: true banner <<-'BANNER' Usage: .COMMAND_NAME All text following a "." is forwarded to the shell. .ls -aF .uname BANNER def process(cmd) if cmd =~ /^cd\s*(.*)/i process_cd parse_destination(Regexp.last_match(1)) else pass_block(cmd) if command_block command_block.call `#{cmd}` else pry_instance.config.system.call(output, cmd, pry_instance) end end end private def parse_destination(dest) return "~" if dest.empty? return dest unless dest == "-" state.old_pwd || raise(CommandError, "No prior directory available") end def process_cd(dest) state.old_pwd = Dir.pwd Dir.chdir(File.expand_path(path_from_cd_path(dest) || dest)) rescue Errno::ENOENT raise CommandError, "No such directory: #{dest}" end def cd_path_env Pry::Env['CDPATH'] end def cd_path_exists? cd_path_env && cd_path_env.length.nonzero? end def path_from_cd_path(dest) return if !(dest && cd_path_exists?) || special_case_path?(dest) cd_path_env.split(File::PATH_SEPARATOR).each do |path| return path if File.directory?(path) && path.split(File::SEPARATOR).last == dest end nil end def special_case_path?(dest) ['.', '..', '-'].include?(dest) || dest =~ /\A[#{File::PATH_SEPARATOR}~]/ end end Pry::Commands.add_command(Pry::Command::ShellCommand) end end pry-0.13.1/lib/pry/commands/shell_mode.rb000066400000000000000000000013401364454223300202330ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class ShellMode < Pry::ClassCommand match 'shell-mode' group 'Input and Output' description 'Toggle shell mode. Bring in pwd prompt and file completion.' banner <<-'BANNER' Toggle shell mode. Bring in pwd prompt and file completion. BANNER def process state.disabled ^= true if state.disabled state.prev_prompt = pry_instance.prompt pry_instance.prompt = Pry::Prompt[:shell] else pry_instance.prompt = state.prev_prompt end end end Pry::Commands.add_command(Pry::Command::ShellMode) Pry::Commands.alias_command 'file-mode', 'shell-mode' end end pry-0.13.1/lib/pry/commands/show_doc.rb000066400000000000000000000056541364454223300177410ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class ShowDoc < Command::ShowInfo include Pry::Helpers::DocumentationHelpers match 'show-doc' group 'Introspection' description 'Show the documentation for a method or class.' banner <<-BANNER Usage: show-doc [OPTIONS] [METH] Aliases: ? Show the documentation for a method or class. Tries instance methods first and then methods by default. show-doc hi_method # docs for hi_method show-doc Pry # for Pry class show-doc Pry -a # for all definitions of Pry class (all monkey patches) BANNER def process super output.puts( "\nWARNING: the show-doc command is deprecated. It will be removed " \ "from future Pry versions.\nPlease use 'show-source' with the -d " \ "(or --doc) switch instead\nExample: show-source #{obj_name} -d" ) end # The docs for code_object prepared for display. def content_for(code_object) Code.new( render_doc_markup_for(code_object), start_line_for(code_object), :text ).with_line_numbers(use_line_numbers?).to_s end # process the markup (if necessary) and apply colors def render_doc_markup_for(code_object) docs = docs_for(code_object) if code_object.command? # command '--help' shouldn't use markup highlighting docs else if docs.empty? raise CommandError, "No docs found for: #{obj_name || 'current context'}" end process_comment_markup(docs) end end # Return docs for the code_object, adjusting for whether the code_object # has yard docs available, in which case it returns those. # (note we only have to check yard docs for modules since they can # have multiple docs, but methods can only be doc'd once so we # dont need to check them) def docs_for(code_object) if code_object.module_with_yard_docs? # yard docs code_object.yard_doc else # normal docs (i.e comments above method/module/command) code_object.doc end end # Which sections to include in the 'header', can toggle: :owner, # :signature and visibility. def header_options super.merge signature: true end # figure out start line of docs by back-calculating based on # number of lines in the comment and the start line of the code_object # @return [Fixnum] start line of docs def start_line_for(code_object) return 1 if code_object.command? || opts.present?(:'base-one') return 1 unless code_object.source_line code_object.source_line - code_object.doc.lines.count end end Pry::Commands.add_command(Pry::Command::ShowDoc) Pry::Commands.alias_command '?', 'show-doc' end end pry-0.13.1/lib/pry/commands/show_info.rb000066400000000000000000000176341364454223300201300ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class ShowInfo < Pry::ClassCommand extend Pry::Helpers::BaseHelpers command_options shellwords: false, interpolate: false def initialize(*) super @used_super = nil end def options(opt) opt.on :s, :super, "Select the 'super' method. Can be repeated to " \ "traverse the ancestors", as: :count opt.on :l, "line-numbers", "Show line numbers" opt.on :b, "base-one", "Show line numbers but start numbering at 1 " \ "(useful for `amend-line` and `play` commands)" opt.on :a, :all, "Show all definitions and monkeypatches of the " \ "module/class" end def process code_object = Pry::CodeObject.lookup(obj_name, pry_instance, super: opts[:super]) raise CommandError, no_definition_message unless code_object @original_code_object = code_object if !obj_name && code_object.c_module? && !opts[:all] result = "You're inside an object, whose class is defined by means of " \ "the C Ruby API.\nPry cannot display the information for this " \ "class." if code_object.candidates.any? result += "\nHowever, you can view monkey-patches applied to this " \ "class.\n.Just execute the same command with the '--all' " \ "switch." end elsif show_all_modules?(code_object) # show all monkey patches for a module result = content_and_headers_for_all_module_candidates(code_object) else # show a specific code object co = code_object_with_accessible_source(code_object) result = content_and_header_for_code_object(co) end set_file_and_dir_locals(code_object.source_file) pry_instance.pager.page result end # This method checks whether the `code_object` is a WrappedModule, if it # is, then it returns the first candidate (monkeypatch) with accessible # source (or docs). If `code_object` is not a WrappedModule (i.e a method # or a command) then the `code_object` itself is just returned. # # @return [Pry::WrappedModule, Pry::Method, Pry::Command] def code_object_with_accessible_source(code_object) return code_object unless code_object.is_a?(WrappedModule) candidate = code_object.candidates.find(&:source) return candidate if candidate raise CommandError, no_definition_message unless valid_superclass?(code_object) @used_super = true code_object_with_accessible_source(code_object.super) end def valid_superclass?(code_object) code_object.super && code_object.super.wrapped != Object end def content_and_header_for_code_object(code_object) header(code_object) + content_for(code_object) end def content_and_headers_for_all_module_candidates(mod) result = "Found #{mod.number_of_candidates} candidates for " \ "`#{mod.name}` definition:\n" mod.number_of_candidates.times do |v| candidate = mod.candidate(v) begin result += "\nCandidate #{v + 1}/#{mod.number_of_candidates}: " \ "#{candidate.source_file}:#{candidate.source_line}\n" content = content_for(candidate) result += "Number of lines: #{content.lines.count}\n\n" + content rescue Pry::RescuableException result += "\nNo content found.\n" next end end result end def no_definition_message "Couldn't locate a definition for #{obj_name}" end # Generate a header (meta-data information) for all the code # object types: methods, modules, commands, procs... def header(code_object) file_name, line_num = file_and_line_for(code_object) content = content_for(code_object) h = "\n#{bold('From:')} #{file_name}" h += code_object_header(code_object, line_num) h += "\n#{bold('Number of lines:')} " + "#{content.lines.count}\n\n" if @used_super h += bold('** Warning:') h += " Cannot find code for #{@original_code_object.nonblank_name}. " \ "Showing superclass #{code_object.nonblank_name} instead. **\n\n" end if content.lines.none? h += bold('** Warning:') h += " Cannot find code for '#{code_object.name}' (source_location is nil)" end h end def code_object_header(code_object, line_num) if code_object.real_method_object? method_header(code_object, line_num) # It sucks we have to test for both Pry::WrappedModule and # WrappedModule::Candidate, probably indicates a deep refactor needs # to happen in those classes. elsif code_object.is_a?(Pry::WrappedModule) || code_object.is_a?(Pry::WrappedModule::Candidate) module_header(code_object, line_num) else "" end end def method_header(code_object, line_num) h = "" h += (code_object.c_method? ? ' (C Method):' : ":#{line_num}:") h += method_sections(code_object)[:owner] h += method_sections(code_object)[:visibility] h += method_sections(code_object)[:signature] h end def module_header(code_object, line_num) h = "" h += ":#{line_num}\n" h += bold(code_object.module? ? "Module" : "Class") h += " #{bold('name:')} #{code_object.nonblank_name}" if code_object.number_of_candidates > 1 h += bold("\nNumber of monkeypatches: ") h += code_object.number_of_candidates.to_s h += ". Use the `-a` option to display all available monkeypatches" end h end def method_sections(code_object) { owner: "\n#{bold('Owner:')} #{code_object.owner || 'N/A'}\n", visibility: "#{bold('Visibility:')} #{code_object.visibility}", signature: "\n#{bold('Signature:')} #{code_object.signature}" }.merge(header_options) { |_key, old, new| (new && old).to_s } end def header_options { owner: true, visibility: true, signature: nil } end def show_all_modules?(code_object) code_object.is_a?(Pry::WrappedModule) && opts.present?(:all) end def obj_name @obj_name ||= args.empty? ? nil : args.join(' ') end def use_line_numbers? opts.present?(:b) || opts.present?(:l) end def start_line_for(code_object) if opts.present?(:'base-one') 1 else code_object.source_line || 1 end end # takes into account possible yard docs, and returns yard_file / yard_line # Also adjusts for start line of comments (using start_line_for), which it # has to infer by subtracting number of lines of comment from start line # of code_object def file_and_line_for(code_object) if code_object.module_with_yard_docs? [code_object.yard_file, code_object.yard_line] else [code_object.source_file, start_line_for(code_object)] end end def complete(input) if input =~ /([^ ]*)#([a-z0-9_]*)\z/ prefix = Regexp.last_match(1) search = Regexp.last_match(2) methods = begin # rubocop:disable Security/Eval Pry::Method.all_from_class(binding.eval(prefix)) # rubocop:enable Security/Eval rescue RescuableException return super end methods.map do |method| [prefix, method.name].join('#') if method.name.start_with?(search) end.compact else super end end end end end pry-0.13.1/lib/pry/commands/show_input.rb000066400000000000000000000010471364454223300203230ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class ShowInput < Pry::ClassCommand match 'show-input' group 'Editing' description 'Show the contents of the input buffer for the current ' \ 'multi-line expression.' banner <<-'BANNER' Show the contents of the input buffer for the current multi-line expression. BANNER def process output.puts Code.new(eval_string).with_line_numbers end end Pry::Commands.add_command(Pry::Command::ShowInput) end end pry-0.13.1/lib/pry/commands/show_source.rb000066400000000000000000000074751364454223300204770ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class ShowSource < Command::ShowInfo include Pry::Helpers::DocumentationHelpers match 'show-source' group 'Introspection' description 'Show the source for a method or class.' banner <<-'BANNER' Usage: show-source [OPTIONS] [METH|CLASS] Aliases: $, show-method Show the source for a method or class. Tries instance methods first and then methods by default. show-source hi_method show-source hi_method show-source Pry#rep # source for Pry#rep method show-source Pry # for Pry class show-source Pry -a # for all Pry class definitions (all monkey patches) show-source Pry.foo -e # for class of the return value of expression `Pry.foo` show-source Pry --super # for superclass of Pry (Object class) show-source Pry -d # include documentation https://github.com/pry/pry/wiki/Source-browsing#wiki-Show_method BANNER def options(opt) opt.on :e, :eval, "evaluate the command's argument as a ruby " \ "expression and show the class its return value" opt.on :d, :doc, 'include documentation in the output' super(opt) end def process if opts.present?(:e) obj = target.eval(args.first) self.args = Array.new(1) { obj.is_a?(Module) ? obj.name : obj.class.name } end super end # The source for code_object prepared for display. def content_for(code_object) content = '' if opts.present?(:d) code = Code.new( render_doc_markup_for(code_object), start_line_for(code_object), :text ) content += code.with_line_numbers(use_line_numbers?).to_s content += "\n" end code = Code.new( code_object.source || [], start_line_for(code_object) ) content += code.with_line_numbers(use_line_numbers?).highlighted content end # process the markup (if necessary) and apply colors def render_doc_markup_for(code_object) docs = docs_for(code_object) if code_object.command? # command '--help' shouldn't use markup highlighting docs else if docs.empty? raise CommandError, "No docs found for: #{obj_name || 'current context'}" end process_comment_markup(docs) end end # Return docs for the code_object, adjusting for whether the code_object # has yard docs available, in which case it returns those. # (note we only have to check yard docs for modules since they can # have multiple docs, but methods can only be doc'd once so we # dont need to check them) def docs_for(code_object) if code_object.module_with_yard_docs? # yard docs code_object.yard_doc else # normal docs (i.e comments above method/module/command) code_object.doc end end # Which sections to include in the 'header', can toggle: :owner, # :signature and visibility. def header_options super.merge signature: true end # figure out start line of docs by back-calculating based on # number of lines in the comment and the start line of the code_object # @return [Fixnum] start line of docs def start_line_for(code_object) return 1 if code_object.command? || opts.present?(:'base-one') return 1 unless code_object.source_line code_object.source_line - code_object.doc.lines.count end end Pry::Commands.add_command(Pry::Command::ShowSource) Pry::Commands.alias_command 'show-method', 'show-source' Pry::Commands.alias_command '$', 'show-source' end end pry-0.13.1/lib/pry/commands/stat.rb000066400000000000000000000022741364454223300171020ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Stat < Pry::ClassCommand match 'stat' group 'Introspection' description 'View method information and set _file_ and _dir_ locals.' command_options shellwords: false banner <<-'BANNER' Usage: stat [OPTIONS] [METH] Show method information for method METH and set _file_ and _dir_ locals. stat hello_method BANNER def options(opt) method_options(opt) end def process meth = method_object aliases = meth.aliases output.puts(unindent(<<-OUTPUT)) Method Information: -- Name: #{meth.name} Alias#{'es' if aliases.length > 1}: #{aliases.any? ? aliases.join(', ') : 'None.'} Owner: #{meth.owner || 'Unknown'} Visibility: #{meth.visibility} Type: #{meth.is_a?(::Method) ? 'Bound' : 'Unbound'} Arity: #{meth.arity} Method Signature: #{meth.signature} Source Location: #{meth.source_location ? meth.source_location.join(':') : 'Not found.'} OUTPUT end end Pry::Commands.add_command(Pry::Command::Stat) end end pry-0.13.1/lib/pry/commands/switch_to.rb000066400000000000000000000014561364454223300201330ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class SwitchTo < Pry::ClassCommand match 'switch-to' group 'Navigating Pry' description 'Start a new subsession on a binding in the current stack.' banner <<-'BANNER' Start a new subsession on a binding in the current stack (numbered by nesting). BANNER def process(selection) selection = selection.to_i if selection < 0 || selection > pry_instance.binding_stack.size - 1 raise CommandError, "Invalid binding index #{selection} - use `nesting` command " \ "to view valid indices." else Pry.start(pry_instance.binding_stack[selection]) end end end Pry::Commands.add_command(Pry::Command::SwitchTo) end end pry-0.13.1/lib/pry/commands/toggle_color.rb000066400000000000000000000010661364454223300206040ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class ToggleColor < Pry::ClassCommand match 'toggle-color' group 'Misc' description 'Toggle syntax highlighting.' banner <<-'BANNER' Usage: toggle-color Toggle syntax highlighting. BANNER def process pry_instance.color = color_toggle output.puts "Syntax highlighting #{pry_instance.color ? 'on' : 'off'}" end def color_toggle !pry_instance.color end Pry::Commands.add_command(self) end end end pry-0.13.1/lib/pry/commands/watch_expression.rb000066400000000000000000000066371364454223300215230ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class WatchExpression < Pry::ClassCommand match 'watch' group 'Context' description 'Watch the value of an expression and print a notification ' \ 'whenever it changes.' command_options use_prefix: false banner <<-'BANNER' Usage: watch [EXPRESSION] watch watch --delete [INDEX] watch [EXPRESSION] adds an expression to the list of those being watched. It will be re-evaluated every time you hit enter in pry. If its value has changed, the new value will be printed to the console. This is useful if you are step-through debugging and want to see how something changes over time. It's also useful if you're trying to write a method inside pry and want to check that it gives the right answers every time you redefine it. watch on its own displays all the currently watched expressions and their values, and watch --delete [INDEX] allows you to delete expressions from the list being watched. BANNER def options(opt) opt.on :d, :delete, "Delete the watch expression with the given index. If no index " \ "is given; clear all watch expressions.", optional_argument: true, as: Integer opt.on :l, :list, "Show all current watch expressions and their values. Calling " \ "watch with no expressions or options will also show the watch " \ "expressions." end def process if opts.present?(:delete) delete opts[:delete] elsif opts.present?(:list) || args.empty? list else add_hook add_expression(args) end end private def expressions state.watch_expressions ||= [] end def delete(index) if index output.puts "Deleting watch expression ##{index}: #{expressions[index - 1]}" expressions.delete_at(index - 1) else output.puts "Deleting all watched expressions" expressions.clear end end def list if expressions.empty? output.puts "No watched expressions" else pry_instance.pager.open do |pager| pager.puts "Listing all watched expressions:" pager.puts "" expressions.each_with_index do |expr, index| pager.print with_line_numbers(expr.to_s, index + 1) end pager.puts "" end end end def eval_and_print_changed(output) expressions.each do |expr| expr.eval! output.puts "#{blue 'watch'}: #{expr}" if expr.changed? end end # TODO: fix arguments. # https://github.com/pry/pry/commit/b031df2f2f5850ee6e9018f33d35f3485a9b0423 def add_expression(_arguments) expressions << Expression.new(pry_instance, target, arg_string) output.puts "Watching #{Code.new(arg_string).highlighted}" end def add_hook hook = [:after_eval, :watch_expression] return if pry_instance.hooks.hook_exists?(*hook) pry_instance.hooks.add_hook(*hook) do |_, pry_instance| eval_and_print_changed pry_instance.output end end end Pry::Commands.add_command(Pry::Command::WatchExpression) end end pry-0.13.1/lib/pry/commands/watch_expression/000077500000000000000000000000001364454223300211625ustar00rootroot00000000000000pry-0.13.1/lib/pry/commands/watch_expression/expression.rb000066400000000000000000000020731364454223300237100ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class WatchExpression class Expression attr_reader :target, :source, :value, :previous_value, :pry_instance def initialize(pry_instance, target, source) @pry_instance = pry_instance @target = target @source = Code.new(source).strip end def eval! @previous_value = value @value = Pry::ColorPrinter.pp(target_eval(target, source), ''.dup) end def to_s "#{Code.new(source).highlighted.strip} => #{value}" end # Has the value of the expression changed? # # We use the pretty-printed string represenation to detect differences # as this avoids problems with dup (causes too many differences) and == # (causes too few) def changed? (value != previous_value) end private def target_eval(target, source) target.eval(source) rescue StandardError => e e end end end end end pry-0.13.1/lib/pry/commands/whereami.rb000066400000000000000000000131111364454223300177200ustar00rootroot00000000000000# frozen_string_literal: true require 'method_source' class Pry class Command class Whereami < Pry::ClassCommand def initialize(*) super @method_code = nil end class << self attr_accessor :method_size_cutoff end @method_size_cutoff = 30 match 'whereami' description 'Show code surrounding the current context.' group 'Context' banner <<-'BANNER' Usage: whereami [-qn] [LINES] Describe the current location. If you use `binding.pry` inside a method then whereami will print out the source for that method. If a number is passed, then LINES lines before and after the current line will be shown instead of the method itself. The `-q` flag can be used to suppress error messages in the case that there's no code to show. This is used by pry in the default before_session hook to show you when you arrive at a `binding.pry`. The `-n` flag can be used to hide line numbers so that code can be copy/pasted effectively. When pry was started on an Object and there is no associated method, whereami will instead output a brief description of the current object. BANNER def setup if target.respond_to?(:source_location) file, @line = target.source_location @file = expand_path(file) else @file = expand_path(target.eval('__FILE__')) @line = target.eval('__LINE__') end @method = Pry::Method.from_binding(target) end def options(opt) opt.on :q, :quiet, "Don't display anything in case of an error" opt.on :n, :"no-line-numbers", "Do not display line numbers" opt.on :m, :method, "Show the complete source for the current method." opt.on :c, :class, "Show the complete source for the current class or module." opt.on :f, :file, "Show the complete source for the current file." end def code @code ||= if opts.present?(:m) method_code || raise(CommandError, "Cannot find method code.") elsif opts.present?(:c) class_code || raise(CommandError, "Cannot find class code.") elsif opts.present?(:f) Pry::Code.from_file(@file) elsif args.any? code_window else default_code end end def code? !!code rescue MethodSource::SourceNotFoundError false end def bad_option_combination? [opts.present?(:m), opts.present?(:f), opts.present?(:c), args.any?].count(true) > 1 end def location "#{@file}:#{@line} #{@method && @method.name_with_owner}" end def process if bad_option_combination? raise CommandError, "Only one of -m, -c, -f, and LINES may be specified." end return if nothing_to_do? if internal_binding?(target) handle_internal_binding return end set_file_and_dir_locals(@file) pretty_code = code.with_line_numbers(use_line_numbers?) .with_marker(marker) .highlighted pry_instance.pager.page( "\n#{bold('From:')} #{location}:\n\n" + pretty_code + "\n" ) end private def nothing_to_do? opts.quiet? && (internal_binding?(target) || !code?) end def use_line_numbers? !opts.present?(:n) end def marker !opts.present?(:n) && @line end def top_level? target_self == Pry.main end def handle_internal_binding if top_level? output.puts "At the top level." else output.puts "Inside #{Pry.view_clip(target_self)}." end end def small_method? @method.source_range.count < self.class.method_size_cutoff end def default_code if method_code && small_method? method_code else code_window end end def code_window Pry::Code.from_file(@file).around(@line, window_size) end def method_code return @method_code if @method_code @method_code = Pry::Code.from_method(@method) if valid_method? end # This either returns the `target_self` # or it returns the class of `target_self` if `target_self` is not a class. # @return [Pry::WrappedModule] def target_class return Pry::WrappedModule(target_self) if target_self.is_a?(Module) Pry::WrappedModule(target_self.class) end def class_code @class_code ||= begin mod = @method ? Pry::WrappedModule(@method.owner) : target_class idx = mod.candidates.find_index { |v| expand_path(v.source_file) == @file } idx && Pry::Code.from_module(mod, idx) end end def valid_method? @method && @method.source? && expand_path(@method.source_file) == @file && @method.source_range.include?(@line) end def expand_path(filename) return unless filename return filename if Pry.eval_path == filename File.expand_path(filename) end def window_size if args.empty? pry_instance.config.default_window_size else args.first.to_i end end end Pry::Commands.add_command(Pry::Command::Whereami) Pry::Commands.alias_command '@', 'whereami' Pry::Commands.alias_command(/whereami[!?]+/, 'whereami') end end pry-0.13.1/lib/pry/commands/wtf.rb000066400000000000000000000047561364454223300167360ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Command class Wtf < Pry::ClassCommand match(/wtf([?!]*)/) group 'Context' description 'Show the backtrace of the most recent exception.' options listing: 'wtf?' banner <<-'BANNER' Usage: wtf[?|!] Shows a few lines of the backtrace of the most recent exception (also available as `_ex_.backtrace`). If you want to see more lines, add more question marks or exclamation marks. wtf? wtf?!???!?!? # To see the entire backtrace, pass the `-v` or `--verbose` flag. wtf -v BANNER RUBY_FRAME_PATTERN = /\A(?(.+)):(?(\d+))/.freeze def options(opt) opt.on :v, :verbose, "Show the full backtrace" opt.on :c, :code, "Show code corresponding to the backtrace frame" end def process unless pry_instance.last_exception raise Pry::CommandError, "No most-recent exception" end text = ''.dup unwind_exceptions.each_with_index do |exception, i| title = (i == 0 ? 'Exception' : 'Caused by') text << format_header(title, exception) text << format_backtrace(exception.backtrace) end output.puts(text) end private def unwind_exceptions exception_list = [] exception = pry_instance.last_exception while exception exception_list << exception exception = (exception.cause if exception.respond_to?(:cause)) end exception_list end def format_header(title, exception) "#{bold(title + ':')} #{exception.class}: #{exception}\n--\n" end def format_backtrace(backtrace) lines = trim_backtrace(backtrace).map do |frame| next frame unless opts.code? match = frame.match(RUBY_FRAME_PATTERN) code = read_line(match[:file], match[:line].to_i) [bold(frame), code].join("\n") end Pry::Code.new(lines.compact, 0, :text).with_line_numbers.to_s end def trim_backtrace(backtrace) return backtrace if opts.verbose? size_of_backtrace = [captures[0].size, 0.5].max * 10 backtrace.first(size_of_backtrace) end def read_line(file, line) File.open(file, 'r') do |f| (line - 1).times { f.gets } f.gets end rescue Errno::ENOENT nil end end Pry::Commands.add_command(Pry::Command::Wtf) end end pry-0.13.1/lib/pry/config.rb000066400000000000000000000217051364454223300155730ustar00rootroot00000000000000# frozen_string_literal: true require 'ostruct' class Pry # @api private class Config extend Attributable # @return [IO, #readline] he object from which Pry retrieves its lines of # input attribute :input # @return [IO, #puts] where Pry should output results provided by {input} attribute :output # @return [Pry::CommandSet] attribute :commands # @return [Proc] the printer for Ruby expressions (not commands) attribute :print # @return [Proc] the printer for exceptions attribute :exception_handler # @return [Array] Exception that Pry shouldn't rescue attribute :unrescued_exceptions # @deprecated # @return [Array] Exception that Pry shouldn't rescue attribute :exception_whitelist # @return [Integer] The number of lines of context to show before and after # exceptions attribute :default_window_size # @return [Pry::Hooks] attribute :hooks # @return [Pry::Prompt] attribute :prompt # @return [String] The display name that is part of the prompt attribute :prompt_name # @return [Array] the list of objects that are known to have a # 1-line #inspect output suitable for prompt attribute :prompt_safe_contexts # If it is a String, then that String is used as the shell # command to invoke the editor. # # If it responds to #call is callable then `file`, `line`, and `reloading` # are passed to it. `reloading` indicates whether Pry will be reloading code # after the shell command returns. All parameters are optional. # @return [String, #call] attribute :editor # A string that must precede all commands. For example, if is is # set to "%", the "cd" command must be invoked as "%cd"). # @return [String] attribute :command_prefix # @return [Boolean] attribute :color # @return [Boolean] attribute :pager # @return [Boolean] whether the global ~/.pryrc should be loaded attribute :should_load_rc # @return [Boolean] whether the local ./.pryrc should be loaded attribute :should_load_local_rc # @return [Boolean] attribute :should_load_plugins # @return [Boolean] whether to load files specified with the -r flag attribute :should_load_requires # @return [Boolean] whether to disable edit-method's auto-reloading behavior attribute :disable_auto_reload # Whether Pry should trap SIGINT and cause it to raise an Interrupt # exception. This is only useful on JRuby, MRI does this for us. # @return [Boolean] attribute :should_trap_interrupts # @return [Pry::History] attribute :history # @return [Boolean] attribute :history_save # @return [Boolean] attribute :history_load # @return [String] attribute :history_file # @return [Array] attribute :history_ignorelist # @return [Array] Ruby files to be required attribute :requires # @return [Integer] how many input/output lines to keep in memory attribute :memory_size # @return [Proc] The proc that runs system commands attribute :system # @return [Boolean] attribute :auto_indent # @return [Boolean] attribute :correct_indent # @return [Boolean] whether or not display a warning when a command name # collides with a method/local in the current context. attribute :collision_warning # @return [Hash{Symbol=>Proc}] attribute :extra_sticky_locals # @return [#build_completion_proc] a completer to use attribute :completer # @return [Boolean] suppresses whereami output on `binding.pry` attribute :quiet # @return [Boolean] displays a warning about experience improvement on # Windows attribute :windows_console_warning # @return [Proc] attribute :command_completions # @return [Proc] attribute :file_completions # @return [Hash] attribute :ls # @return [String] a line of code to execute in context before the session # starts attribute :exec_string # @return [String] attribute :output_prefix # @return [String] # @since v0.13.0 attribute :rc_file def initialize merge!( input: MemoizedValue.new { lazy_readline }, output: $stdout.tap { |out| out.sync = true }, commands: Pry::Commands, prompt_name: 'pry', prompt: Pry::Prompt[:default], prompt_safe_contexts: [String, Numeric, Symbol, nil, true, false], print: Pry::ColorPrinter.method(:default), quiet: false, exception_handler: Pry::ExceptionHandler.method(:handle_exception), unrescued_exceptions: [ ::SystemExit, ::SignalException, Pry::TooSafeException ], exception_whitelist: MemoizedValue.new do output.puts( '[warning] Pry.config.exception_whitelist is deprecated, ' \ 'please use Pry.config.unrescued_exceptions instead.' ) unrescued_exceptions end, hooks: Pry::Hooks.default, pager: true, system: Pry::SystemCommandHandler.method(:default), color: Pry::Helpers::BaseHelpers.use_ansi_codes?, default_window_size: 5, editor: Pry::Editor.default, rc_file: default_rc_file, should_load_rc: true, should_load_local_rc: true, should_trap_interrupts: Pry::Helpers::Platform.jruby?, disable_auto_reload: false, command_prefix: '', auto_indent: Pry::Helpers::BaseHelpers.use_ansi_codes?, correct_indent: true, collision_warning: false, output_prefix: '=> ', requires: [], should_load_requires: true, should_load_plugins: true, windows_console_warning: true, control_d_handler: Pry::ControlDHandler.method(:default), memory_size: 100, extra_sticky_locals: {}, command_completions: proc { commands.keys }, file_completions: proc { Dir['.'] }, ls: OpenStruct.new(Pry::Command::Ls::DEFAULT_OPTIONS), completer: Pry::InputCompleter, history_save: true, history_load: true, history_file: Pry::History.default_file, history_ignorelist: [], history: MemoizedValue.new do if defined?(input::HISTORY) Pry::History.new(history: input::HISTORY) else Pry::History.new end end, exec_string: '' ) @custom_attrs = {} end def merge!(config_hash) config_hash.each_pair { |attr, value| __send__("#{attr}=", value) } self end def merge(config_hash) dup.merge!(config_hash) end def []=(attr, value) @custom_attrs[attr.to_s] = Config::Value.new(value) end def [](attr) @custom_attrs[attr.to_s].call end # rubocop:disable Style/MethodMissingSuper def method_missing(method_name, *args, &_block) name = method_name.to_s if name.end_with?('=') self[name[0..-2]] = args.first elsif @custom_attrs.key?(name) self[name] end end # rubocop:enable Style/MethodMissingSuper def respond_to_missing?(method_name, include_all = false) @custom_attrs.key?(method_name.to_s.tr('=', '')) || super end def initialize_dup(other) super @custom_attrs = @custom_attrs.dup end attr_reader :control_d_handler def control_d_handler=(value) proxy_proc = if value.arity == 2 Pry::Warning.warn( "control_d_handler's arity of 2 parameters was deprecated " \ '(eval_string, pry_instance). Now it gets passed just 1 ' \ 'parameter (pry_instance)' ) proc do |*args| if args.size == 2 value.call(args.first, args[1]) else value.call(args.first.eval_string, args.first) end end else proc do |*args| if args.size == 2 value.call(args[1]) else value.call(args.first) end end end @control_d_handler = proxy_proc end private def lazy_readline require 'readline' ::Readline rescue LoadError output.puts( "Sorry, you can't use Pry without Readline or a compatible library. \n" \ "Possible solutions: \n" \ " * Rebuild Ruby with Readline support using `--with-readline` \n" \ " * Use the rb-readline gem, which is a pure-Ruby port of Readline \n" \ " * Use the pry-coolline gem, a pure-ruby alternative to Readline" ) raise end def default_rc_file if (pryrc = Pry::Env['PRYRC']) pryrc elsif (xdg_home = Pry::Env['XDG_CONFIG_HOME']) # See XDG Base Directory Specification at # https://standards.freedesktop.org/basedir-spec/basedir-spec-0.8.html xdg_home + '/pry/pryrc' elsif File.exist?(File.expand_path('~/.pryrc')) '~/.pryrc' else '~/.config/pry/pryrc' end end end end pry-0.13.1/lib/pry/config/000077500000000000000000000000001364454223300152415ustar00rootroot00000000000000pry-0.13.1/lib/pry/config/attributable.rb000066400000000000000000000010651364454223300202520ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Config # Attributable provides the ability to create "attribute" # accessors. Attribute accessors create a standard "attr_writer" and a # customised "attr_reader". This reader is Proc-aware (lazy). # # @since v0.13.0 # @api private module Attributable def attribute(attr_name) define_method(attr_name) do value = Config::Value.new(instance_variable_get("@#{attr_name}")) value.call end attr_writer(attr_name) end end end end pry-0.13.1/lib/pry/config/lazy_value.rb000066400000000000000000000011541364454223300177420ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Config # LazyValue is a Proc (block) wrapper. It is meant to be used as a # configuration value. Subsequent `#call` calls always evaluate the given # block. # # @example # num = 19 # value = Pry::Config::LazyValue.new { num += 1 } # value.foo # => 20 # value.foo # => 21 # value.foo # => 22 # # @api private # @since v0.13.0 # @see Pry::Config::MemoizedValue class LazyValue def initialize(&block) @block = block end def call @block.call end end end end pry-0.13.1/lib/pry/config/memoized_value.rb000066400000000000000000000013431364454223300205740ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Config # MemoizedValue is a Proc (block) wrapper. It is meant to be used as a # configuration value. Subsequent `#call` calls return the same memoized # result. # # @example # num = 19 # value = Pry::Config::MemoizedValue.new { num += 1 } # value.call # => 20 # value.call # => 20 # value.call # => 20 # # @api private # @since v0.13.0 # @see Pry::Config::LazyValue class MemoizedValue def initialize(&block) @block = block @called = false @call = nil end def call return @call if @called @called = true @call = @block.call end end end end pry-0.13.1/lib/pry/config/value.rb000066400000000000000000000007671364454223300167140ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Config # Value holds a value for the given attribute and decides how it should # be read. Procs get called, other values are returned as is. # # @since v0.13.0 # @api private class Value def initialize(value) @value = value end def call unless [Config::MemoizedValue, Config::LazyValue].include?(@value.class) return @value end @value.call end end end end pry-0.13.1/lib/pry/control_d_handler.rb000066400000000000000000000017041364454223300200030ustar00rootroot00000000000000# frozen_string_literal: true class Pry # @api private # @since v0.13.0 module ControlDHandler # Deal with the ^D key being pressed. Different behaviour in different # cases: # 1. In an expression behave like `!` command. # 2. At top-level session behave like `exit` command. # 3. In a nested session behave like `cd ..`. def self.default(pry_instance) if !pry_instance.eval_string.empty? # Clear input buffer. pry_instance.eval_string = '' elsif pry_instance.binding_stack.one? pry_instance.binding_stack.clear throw(:breakout) else # Otherwise, saves current binding stack as old stack and pops last # binding out of binding stack (the old stack still has that binding). cd_state = Pry::CommandState.default.state_for('cd') cd_state.old_stack = pry_instance.binding_stack.dup pry_instance.binding_stack.pop end end end end pry-0.13.1/lib/pry/core_extensions.rb000066400000000000000000000123041364454223300175300ustar00rootroot00000000000000# frozen_string_literal: true class Pry # @return [Array] Code of the method used when implementing Pry's # __binding__, along with line indication to be used with instance_eval (and # friends). # # @see Object#__binding__ BINDING_METHOD_IMPL = [<<-METHOD, __FILE__, __LINE__ + 1].freeze # Get a binding with 'self' set to self, and no locals. # # The default definee is determined by the context in which the # definition is eval'd. # # Please don't call this method directly, see {__binding__}. # # @return [Binding] def __pry__ binding end METHOD end class Object # Start a Pry REPL on self. # # If `self` is a Binding then that will be used to evaluate expressions; # otherwise a new binding will be created. # # @param [Object] object the object or binding to pry # (__deprecated__, use `object.pry`) # @param [Hash] hash the options hash # @example With a binding # binding.pry # @example On any object # "dummy".pry # @example With options # def my_method # binding.pry :quiet => true # end # my_method() # @see Pry.start def pry(object = nil, hash = {}) if object.nil? || Hash === object # rubocop:disable Style/CaseEquality Pry.start(self, object || {}) else Pry.start(object, hash) end end # Return a binding object for the receiver. # # The `self` of the binding is set to the current object, and it contains no # local variables. # # The default definee (http://yugui.jp/articles/846) is set such that: # # * If `self` is a class or module, then new methods created in the binding # will be defined in that class or module (as in `class Foo; end`). # * If `self` is a normal object, then new methods created in the binding will # be defined on its singleton class (as in `class << self; end`). # * If `self` doesn't have a real singleton class (i.e. it is a Fixnum, Float, # Symbol, nil, true, or false), then new methods will be created on the # object's class (as in `self.class.class_eval{ }`) # # Newly created constants, including classes and modules, will also be added # to the default definee. # # @return [Binding] def __binding__ # If you ever feel like changing this method, be careful about variables # that you use. They shouldn't be inserted into the binding that will # eventually be returned. # When you're cd'd into a class, methods you define should be added to it. if is_a?(Module) # A special case, for JRuby. # Module.new.class_eval("binding") has different behaviour than CRuby, # where this is not needed: class_eval("binding") vs class_eval{binding}. # Using a block works around the difference of behaviour on JRuby. # The scope is clear of local variabless. Don't add any. # # This fixes the following two spec failures, at https://travis-ci.org/pry/pry/jobs/274470002 # 1) ./spec/pry_spec.rb:360:in `block in (root)' # 2) ./spec/pry_spec.rb:366:in `block in (root)' return class_eval { binding } if Pry::Helpers::Platform.jruby? && name.nil? # class_eval sets both self and the default definee to this class. return class_eval("binding", __FILE__, __LINE__) end unless self.class.method_defined?(:__pry__) # The easiest way to check whether an object has a working singleton class # is to try and define a method on it. (just checking for the presence of # the singleton class gives false positives for `true` and `false`). # __pry__ is just the closest method we have to hand, and using # it has the nice property that we can memoize this check. begin # instance_eval sets the default definee to the object's singleton class instance_eval(*Pry::BINDING_METHOD_IMPL) # If we can't define methods on the Object's singleton_class. Then we fall # back to setting the default definee to be the Object's class. That seems # nicer than having a REPL in which you can't define methods. rescue TypeError, Pry::FrozenObjectException # class_eval sets the default definee to self.class self.class.class_eval(*Pry::BINDING_METHOD_IMPL) end end __pry__ end end class BasicObject # Return a binding object for the receiver. # # The `self` of the binding is set to the current object, and it contains no # local variables. # # The default definee (http://yugui.jp/articles/846) is set such that new # methods defined will be added to the singleton class of the BasicObject. # # @return [Binding] def __binding__ # BasicObjects don't have respond_to?, so we just define the method # every time. As they also don't have `.freeze`, this call won't # fail as it can for normal Objects. (class << self; self; end).class_eval(<<-METHOD, __FILE__, __LINE__ + 1) # Get a binding with 'self' set to self, and no locals. # # The default definee is determined by the context in which the # definition is eval'd. # # Please don't call this method directly, see {__binding__}. # # @return [Binding] def __pry__ ::Kernel.binding end METHOD __pry__ end end pry-0.13.1/lib/pry/editor.rb000066400000000000000000000111721364454223300156110ustar00rootroot00000000000000# frozen_string_literal: true require 'shellwords' class Pry class Editor def self.default if (visual = Pry::Env['VISUAL']) return visual end if (editor = Pry::Env['EDITOR']) return editor end return 'notepad' if Helpers::Platform.windows? %w[editor nano vi].find do |editor_exe| Kernel.system("which #{editor_exe} > /dev/null 2>&1") end end include Pry::Helpers::CommandHelpers attr_reader :pry_instance def initialize(pry_instance) @pry_instance = pry_instance end def edit_tempfile_with_content(initial_content, line = 1) temp_file do |f| f.puts(initial_content) f.flush f.close(false) invoke_editor(f.path, line, true) File.read(f.path) end end def invoke_editor(file, line, blocking = true) unless pry_instance.config.editor raise CommandError, "Please set Pry.config.editor or export $VISUAL or $EDITOR" end editor_invocation = build_editor_invocation_string(file, line, blocking) return nil unless editor_invocation if Helpers::Platform.jruby? open_editor_on_jruby(editor_invocation) else open_editor(editor_invocation) end end # Generate the string that's used to start the editor. This includes # all the flags we want as well as the file and line number we # want to open at. def build_editor_invocation_string(file, line, blocking) if pry_instance.config.editor.respond_to?(:call) args = [file, line, blocking][0...(pry_instance.config.editor.arity)] pry_instance.config.editor.call(*args) else sanitized_file = Helpers::Platform.windows? ? file : Shellwords.escape(file) editor = pry_instance.config.editor flag = blocking_flag_for_editor(blocking) start_line = start_line_syntax_for_editor(sanitized_file, line) "#{editor} #{flag} #{start_line}" end end private # Start the editor running, using the calculated invocation string def open_editor(editor_invocation) # Note we dont want to use Pry.config.system here as that # may be invoked non-interactively (i.e via Open4), whereas we want to # ensure the editor is always interactive system(*Shellwords.split(editor_invocation)) || raise( CommandError, "`#{editor_invocation}` gave exit status: #{$CHILD_STATUS.exitstatus}" ) end # We need JRuby specific code here cos just shelling out using # system() appears to be pretty broken :/ def open_editor_on_jruby(editor_invocation) require 'spoon' pid = Spoon.spawnp(*Shellwords.split(editor_invocation)) Process.waitpid(pid) rescue FFI::NotFoundError system(editor_invocation) end # Some editors that run outside the terminal allow you to control whether or # not to block the process from which they were launched (in this case, Pry). # For those editors, return the flag that produces the desired behavior. def blocking_flag_for_editor(blocking) case editor_name when /^emacsclient/ '--no-wait' unless blocking when /^[gm]vim/ '--nofork' if blocking when /^jedit/ '-wait' if blocking when /^mate/, /^subl/, /^redcar/ '-w' if blocking end end # Return the syntax for a given editor for starting the editor # and moving to a particular line within that file def start_line_syntax_for_editor(file_name, line_number) # special case for 1st line return file_name if line_number <= 1 case editor_name when /^[gm]?vi/, /^emacs/, /^nano/, /^pico/, /^gedit/, /^kate/ "+#{line_number} #{file_name}" when /^mate/, /^geany/ "-l #{line_number} #{file_name}" when /^subl/ "#{file_name}:#{line_number}" when /^uedit32/ "#{file_name}/#{line_number}" when /^jedit/ "#{file_name} +line:#{line_number}" when /^redcar/ "-l#{line_number} #{file_name}" else if Helpers::Platform.windows? file_name.to_s else "+#{line_number} #{file_name}" end end end # Get the name of the binary that Pry.config.editor points to. # # This is useful for deciding which flags we pass to the editor as # we can just use the program's name and ignore any absolute paths. # # @example # Pry.config.editor="/home/conrad/bin/textmate -w" # editor_name # # => textmate # def editor_name File.basename(pry_instance.config.editor).split(" ").first end end end pry-0.13.1/lib/pry/env.rb000066400000000000000000000004471364454223300151160ustar00rootroot00000000000000# frozen_string_literal: true class Pry # Env is a helper module to work with environment variables. # # @since v0.13.0 # @api private module Env def self.[](key) return unless ENV.key?(key) value = ENV[key] return if value == '' value end end end pry-0.13.1/lib/pry/exception_handler.rb000066400000000000000000000021121364454223300200100ustar00rootroot00000000000000# frozen_string_literal: true class Pry # @api private # @since v0.13.0 module ExceptionHandler class << self # Will only show the first line of the backtrace. def handle_exception(output, exception, _pry_instance) if exception.is_a?(UserError) && exception.is_a?(SyntaxError) output.puts "SyntaxError: #{exception.message.sub(/.*syntax error, */m, '')}" else output.puts standard_error_text_for(exception) end end private def standard_error_text_for(exception) text = exception_text(exception) return text unless exception.respond_to?(:cause) cause = exception.cause while cause text += cause_text(cause) cause = cause.cause end text end def exception_text(exception) "#{exception.class}: #{exception.message}\n" \ "from #{exception.backtrace.first}\n" end def cause_text(cause) "Caused by #{cause.class}: #{cause}\n" \ "from #{cause.backtrace.first}\n" end end end end pry-0.13.1/lib/pry/exceptions.rb000066400000000000000000000045231364454223300165060ustar00rootroot00000000000000# frozen_string_literal: true class Pry # As a REPL, we often want to catch any unexpected exceptions that may have # been raised; however we don't want to go overboard and prevent the user # from exiting Pry when they want to. module RescuableException def self.===(exception) case exception # Catch when the user hits ^C (Interrupt < SignalException), and assume # that they just wanted to stop the in-progress command (just like bash # etc.) when Interrupt true # Don't catch signals (particularly not SIGTERM) as these are unlikely # to be intended for pry itself. We should also make sure that # Kernel#exit works. when *Pry.config.unrescued_exceptions false # All other exceptions will be caught. else true end end end # Catches SecurityErrors if $SAFE is set module TooSafeException def self.===(exception) if Pry::HAS_SAFE_LEVEL $SAFE > 0 && exception.is_a?(SecurityError) else exception.is_a?(SecurityError) end end end # An Exception Tag (cf. Exceptional Ruby) that instructs Pry to show the error # in a more user-friendly manner. This should be used when the exception # happens within Pry itself as a direct consequence of the user typing # something wrong. # # This allows us to distinguish between the user typing: # # pry(main)> def ) # SyntaxError: unexpected ) # # pry(main)> method_that_evals("def )") # SyntaxError: (eval):1: syntax error, unexpected ')' # from ./a.rb:2 in `eval' module UserError; end # When we try to get a binding for an object, we try to define a method on # that Object's singleton class. This doesn't work for "frozen" Object's, and # the exception is just a vanilla RuntimeError. module FrozenObjectException def self.===(exception) [ "can't modify frozen class/module", "can't modify frozen Class", "can't modify frozen object" ].include?(exception.message) end end # CommandErrors are caught by the REPL loop and displayed to the user. They # indicate an exceptional condition that's fatal to the current command. class CommandError < StandardError; end class MethodNotFound < CommandError; end # indicates obsolete API class ObsoleteError < StandardError; end end pry-0.13.1/lib/pry/forwardable.rb000066400000000000000000000015101364454223300166060ustar00rootroot00000000000000# frozen_string_literal: true class Pry module Forwardable require 'forwardable' include ::Forwardable # # Since Ruby 2.4, Forwardable will print a warning when # calling a method that is private on a delegate, and # in the future it could be an error: https://bugs.ruby-lang.org/issues/12782#note-3 # # That's why we revert to a custom implementation for delegating one # private method to another. # def def_private_delegators(target, *private_delegates) private_delegates.each do |private_delegate| define_method(private_delegate) do |*a, &b| instance_variable_get(target).__send__(private_delegate, *a, &b) end end class_eval do private(*private_delegates) # rubocop:disable Style/AccessModifierDeclarations end end end end pry-0.13.1/lib/pry/helpers.rb000066400000000000000000000003441364454223300157640ustar00rootroot00000000000000# frozen_string_literal: true require "pry/helpers/base_helpers" require "pry/helpers/options_helpers" require "pry/helpers/command_helpers" require "pry/helpers/text" require "pry/helpers/table" require "pry/helpers/platform" pry-0.13.1/lib/pry/helpers/000077500000000000000000000000001364454223300154365ustar00rootroot00000000000000pry-0.13.1/lib/pry/helpers/base_helpers.rb000066400000000000000000000037101364454223300204200ustar00rootroot00000000000000# frozen_string_literal: true class Pry module Helpers module BaseHelpers extend self def silence_warnings old_verbose = $VERBOSE $VERBOSE = nil begin yield ensure $VERBOSE = old_verbose end end # Acts like send but ignores any methods defined below Object or Class in the # inheritance hierarchy. # This is required to introspect methods on objects like Net::HTTP::Get that # have overridden the `method` method. def safe_send(obj, method, *args, &block) (obj.is_a?(Module) ? Module : Object).instance_method(method) .bind(obj).call(*args, &block) end def find_command(name, set = Pry::Commands) command_match = set.find do |_, command| (listing = command.options[:listing]) == name && !listing.nil? end command_match.last if command_match end def not_a_real_file?(file) file =~ /^(\(.*\))$|^<.*>$/ || file =~ /__unknown__/ || file == "" || file == "-e" end def use_ansi_codes? Pry::Helpers::Platform.windows_ansi? || ((term = Pry::Env['TERM']) && term != "dumb") end def colorize_code(code) SyntaxHighlighter.highlight(code) end def highlight(string, regexp, highlight_color = :bright_yellow) string.gsub(regexp) do |match| "<#{highlight_color}>#{match}" end end # formatting def heading(text) text = "#{text}\n--" "\e[1m#{text}\e[0m" end # Send the given text through the best available pager (if Pry.config.pager is # enabled). Infers where to send the output if used as a mixin. # DEPRECATED. def stagger_output(text, _out = nil) if defined?(pry_instance) && pry_instance pry_instance.pager.page text else Pry.new.pager.page text end end end end end pry-0.13.1/lib/pry/helpers/command_helpers.rb000066400000000000000000000114071364454223300211260ustar00rootroot00000000000000# frozen_string_literal: true require 'tempfile' class Pry module Helpers module CommandHelpers include OptionsHelpers extend self # Open a temp file and yield it to the block, closing it after # @return [String] The path of the temp file def temp_file(ext = '.rb') file = Tempfile.new(['pry', ext]) yield(file) ensure file.close(true) end def internal_binding?(context) method_name = context.eval("::Kernel.__method__").to_s # class_eval is here because of http://jira.codehaus.org/browse/JRUBY-6753 %w[__binding__ __pry__ class_eval].include?(method_name) # TODO: codehaus is dead, there was no test for this and the # description for the commit doesn't exist. Probably a candidate for # removal so we have a chance to introduce a regression and document it # properly. end def get_method_or_raise(method_name, context, opts = {}) method = Pry::Method.from_str(method_name, context, opts) if !method && method_name raise Pry::MethodNotFound, "method '#{method_name}' could not be found." end (opts[:super] || 0).times do if method.super method = method.super else raise Pry::MethodNotFound, "'#{method.name_with_owner}' has no super method" end end if !method || (!method_name && internal_binding?(context)) raise Pry::MethodNotFound, 'no method name given, and context is not a method' end set_file_and_dir_locals(method.source_file) method end # Remove any common leading whitespace from every line in `text`. This # can be used to make a HEREDOC line up with the left margin, without # sacrificing the indentation level of the source code. # # @example # opt.banner(unindent(<<-USAGE)) # Lorem ipsum dolor sit amet, consectetur adipisicing elit, # sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. # "Ut enim ad minim veniam." # USAGE # # @param [String] dirty_text The text from which to remove indentation # @return [String] the text with indentation stripped def unindent(dirty_text, left_padding = 0) text = dirty_text.sub(/\A[ \t]+\z/, '') # Empty blank lines. # Find the longest common whitespace to all indented lines. Ignore lines # containing just -- or ++ as these seem to be used by comment authors # as delimeters. scanned_text = text.scan(/^[ \t]*(?!--\n|\+\+\n)(?=[^ \t\n])/) margin = scanned_text.inject do |current_margin, next_indent| if next_indent.start_with?(current_margin) current_margin elsif current_margin.start_with?(next_indent) next_indent else '' end end text.gsub(/^#{margin}/, ' ' * left_padding) end # Restrict a string to the given range of lines (1-indexed) # @param [String] content The string. # @param [Range, Integer] lines The line(s) to restrict it to. # @return [String] The resulting string. def restrict_to_lines(content, lines) line_range = one_index_range_or_number(lines) Array(content.lines.to_a[line_range]).join end def one_index_number(line_number) line_number > 0 ? line_number - 1 : line_number end # convert a 1-index range to a 0-indexed one def one_index_range(range) Range.new(one_index_number(range.begin), one_index_number(range.end)) end def one_index_range_or_number(range_or_number) case range_or_number when Range one_index_range(range_or_number) else one_index_number(range_or_number) end end def absolute_index_number(line_number, array_length) if line_number >= 0 line_number else [array_length + line_number, 0].max end end def absolute_index_range(range_or_number, array_length) case range_or_number when Range a = absolute_index_number(range_or_number.begin, array_length) b = absolute_index_number(range_or_number.end, array_length) else a = b = absolute_index_number(range_or_number, array_length) end Range.new(a, b) end def set_file_and_dir_locals(file_name, pry = pry_instance, ctx = target) return if !ctx || !file_name pry.last_file = File.expand_path(file_name) pry.inject_local("_file_", pry.last_file, ctx) pry.last_dir = File.dirname(pry.last_file) pry.inject_local("_dir_", pry.last_dir, ctx) end end end end pry-0.13.1/lib/pry/helpers/documentation_helpers.rb000066400000000000000000000052341364454223300223620ustar00rootroot00000000000000# frozen_string_literal: true class Pry module Helpers # This class contains methods useful for extracting # documentation from methods and classes. module DocumentationHelpers YARD_TAGS = %w[ param return option yield attr attr_reader attr_writer deprecate example raise ].freeze module_function def process_rdoc(comment) comment = comment.dup last_match_ruby = proc do SyntaxHighlighter.highlight(Regexp.last_match(1)) end comment.gsub(%r{(?:\s*\n)?(.*?)\s*}m, &last_match_ruby) .gsub(%r{(?:\s*\n)?(.*?)\s*}m) { "\e[1m#{Regexp.last_match(1)}\e[0m" } .gsub(%r{(?:\s*\n)?(.*?)\s*}m) { "\e[1m#{Regexp.last_match(1)}\e[0m" } .gsub(%r{(?:\s*\n)?(.*?)\s*}m, &last_match_ruby) .gsub(/\B\+(\w+?)\+\B/) { "\e[32m#{Regexp.last_match(1)}\e[0m" } .gsub(/((?:^[ \t]+.+(?:\n+|\Z))+)/, &last_match_ruby) .gsub(/`(?:\s*\n)?([^\e]*?)\s*`/) { "`#{last_match_ruby.call}`" } end def process_yardoc_tag(comment, tag) in_tag_block = nil comment.lines.map do |v| if in_tag_block && v !~ /^\S/ Pry::Helpers::Text.strip_color Pry::Helpers::Text.strip_color(v) elsif in_tag_block in_tag_block = false v else in_tag_block = true if v =~ /^@#{tag}/ v end end.join end def process_yardoc(comment) (YARD_TAGS - %w[example]) .inject(comment) { |a, v| process_yardoc_tag(a, v) } .gsub(/^@(#{YARD_TAGS.join("|")})/) { "\e[33m#{Regexp.last_match(1)}\e[0m" } end def process_comment_markup(comment) process_yardoc process_rdoc(comment) end # @param [String] code # @return [String] def strip_comments_from_c_code(code) code.sub(%r{\A\s*/\*.*?\*/\s*}m, '') end # Given a string that makes up a comment in a source-code file parse out the content # that the user is intended to read. (i.e. without leading indentation, #-characters # or shebangs) # # @param [String] comment # @return [String] def get_comment_content(comment) comment = comment.dup # Remove #!/usr/bin/ruby comment.gsub!(/\A\#!.*$/, '') # Remove leading empty comment lines comment.gsub!(/\A\#+?$/, '') comment.gsub!(/^\s*#/, '') strip_leading_whitespace(comment) end # @param [String] text # @return [String] def strip_leading_whitespace(text) Pry::Helpers::CommandHelpers.unindent(text) end end end end pry-0.13.1/lib/pry/helpers/options_helpers.rb000066400000000000000000000021501364454223300211760ustar00rootroot00000000000000# frozen_string_literal: true class Pry module Helpers module OptionsHelpers module_function # Add method options to the Pry::Slop instance def method_options(opt) @method_target = target opt.on :M, "instance-methods", "Operate on instance methods." opt.on :m, :methods, "Operate on methods." opt.on :s, :super, "Select the 'super' method. Can be repeated to " \ "traverse the ancestors.", as: :count opt.on( :c, :context, "Select object context to run under.", argument: true ) do |context| @method_target = Pry.binding_for(target.eval(context)) end end # Get the method object parsed by the slop instance def method_object @method_object ||= get_method_or_raise( args.empty? ? nil : args.join(" "), @method_target, super: opts[:super], instance: opts.present?(:'instance-methods') && !opts.present?(:methods), methods: opts.present?(:methods) && !opts.present?(:'instance-methods') ) end end end end pry-0.13.1/lib/pry/helpers/platform.rb000066400000000000000000000025561364454223300176170ustar00rootroot00000000000000# frozen_string_literal: true require 'rbconfig' class Pry module Helpers # Contains methods for querying the platform that Pry is running on # @api public # @since v0.12.0 module Platform # @return [Boolean] def self.mac_osx? !!(RbConfig::CONFIG['host_os'] =~ /\Adarwin/i) end # @return [Boolean] def self.linux? !!(RbConfig::CONFIG['host_os'] =~ /linux/i) end # @return [Boolean] true when Pry is running on Windows with ANSI support, # false otherwise def self.windows? !!(RbConfig::CONFIG['host_os'] =~ /mswin|mingw/) end # @return [Boolean] def self.windows_ansi? return false unless windows? !!(defined?(Win32::Console) || Pry::Env['ANSICON'] || mri_2?) end # @return [Boolean] def self.jruby? RbConfig::CONFIG['ruby_install_name'] == 'jruby' end # @return [Boolean] def self.jruby_19? jruby? && RbConfig::CONFIG['ruby_version'] == '1.9' end # @return [Boolean] def self.mri? RbConfig::CONFIG['ruby_install_name'] == 'ruby' end # @return [Boolean] def self.mri_19? mri? && RUBY_VERSION.start_with?('1.9') end # @return [Boolean] def self.mri_2? mri? && RUBY_VERSION.start_with?('2') end end end end pry-0.13.1/lib/pry/helpers/table.rb000066400000000000000000000062051364454223300170550ustar00rootroot00000000000000# frozen_string_literal: true class Pry module Helpers def self.tablify_or_one_line(heading, things, pry_instance = Pry.new) plain_heading = Pry::Helpers::Text.strip_color(heading) attempt = Table.new(things, column_count: things.size) if attempt.fits_on_line?(pry_instance.output.width - plain_heading.size - 2) "#{heading}: #{attempt}\n" else content = tablify_to_screen_width(things, { indent: ' ' }, pry_instance) "#{heading}: \n#{content}\n" end end def self.tablify_to_screen_width(things, options, pry_instance = Pry.new) options ||= {} things = things.compact if (indent = options[:indent]) usable_width = pry_instance.output.width - indent.size tablify(things, usable_width, pry_instance).to_s.gsub(/^/, indent) else tablify(things, pry_instance.output.width, pry_instance).to_s end end def self.tablify(things, line_length, pry_instance = Pry.new) table = Table.new(things, { column_count: things.size }, pry_instance) until (table.column_count == 1) || table.fits_on_line?(line_length) table.column_count -= 1 end table end class Table attr_reader :items, :column_count def initialize(items, args, pry_instance = Pry.new) @column_count = args[:column_count] @config = pry_instance.config self.items = items end def to_s rows_to_s.join("\n") end def rows_to_s(style = :color_on) widths = columns.map { |e| _max_width(e) } @rows_without_colors.map do |r| padded = [] r.each_with_index do |e, i| next unless e item = e.ljust(widths[i]) item.sub! e, _recall_color_for(e) if style == :color_on padded << item end padded.join(@config.ls.separator) end end def items=(items) @items = items _rebuild_colorless_cache _recolumn end def column_count=(count) @column_count = count _recolumn end def fits_on_line?(line_length) _max_width(rows_to_s(:no_color)) <= line_length end def columns @rows_without_colors.transpose end def ==(other) items == other.to_a end def to_a items.to_a end private def _max_width(things) things.compact.map(&:size).max || 0 end def _rebuild_colorless_cache @colorless_cache = {} @plain_items = [] items.map do |e| plain = Pry::Helpers::Text.strip_color(e) @colorless_cache[plain] = e @plain_items << plain end end def _recolumn @rows_without_colors = [] return if items.size.zero? row_count = (items.size.to_f / column_count).ceil row_count.times do |i| row_indices = (0...column_count).map { |e| row_count * e + i } @rows_without_colors << row_indices.map { |e| @plain_items[e] } end end def _recall_color_for(thing) @colorless_cache[thing] end end end end pry-0.13.1/lib/pry/helpers/text.rb000066400000000000000000000057151364454223300167570ustar00rootroot00000000000000# frozen_string_literal: true class Pry module Helpers # The methods defined on {Text} are available to custom commands via # {Pry::Command#text}. module Text extend self COLORS = { "black" => 0, "red" => 1, "green" => 2, "yellow" => 3, "blue" => 4, "purple" => 5, "magenta" => 5, "cyan" => 6, "white" => 7 }.freeze COLORS.each_pair do |color, value| define_method color do |text| "\033[0;#{30 + value}m#{text}\033[0m" end define_method "bright_#{color}" do |text| "\033[1;#{30 + value}m#{text}\033[0m" end COLORS.each_pair do |bg_color, bg_value| define_method "#{color}_on_#{bg_color}" do |text| "\033[0;#{30 + value};#{40 + bg_value}m#{text}\033[0m" end define_method "bright_#{color}_on_#{bg_color}" do |text| "\033[1;#{30 + value};#{40 + bg_value}m#{text}\033[0m" end end end # Remove any color codes from _text_. # # @param [String, #to_s] text # @return [String] _text_ stripped of any color codes. def strip_color(text) text.to_s.gsub(/(\001)?\e\[.*?(\d)+m(\002)?/, '') end # Returns _text_ as bold text for use on a terminal. # # @param [String, #to_s] text # @return [String] _text_ def bold(text) "\e[1m#{text}\e[0m" end # Returns `text` in the default foreground colour. # Use this instead of "black" or "white" when you mean absence of colour. # # @param [String, #to_s] text # @return [String] def default(text) text.to_s end # # @yield # Yields a block with color turned off. # # @return [void] # def no_color boolean = Pry.config.color Pry.config.color = false yield ensure Pry.config.color = boolean end # # @yield # Yields a block with paging turned off. # # @return [void] # def no_pager boolean = Pry.config.pager Pry.config.pager = false yield ensure Pry.config.pager = boolean end # Returns _text_ in a numbered list, beginning at _offset_. # # @param [#each_line] text # @param [Fixnum] offset # @return [String] def with_line_numbers(text, offset, color = :blue) lines = text.each_line.to_a max_width = (offset + lines.count).to_s.length lines.each_with_index.map do |line, index| adjusted_index = (index + offset).to_s.rjust(max_width) "#{send(color, adjusted_index)}: #{line}" end.join end # Returns _text_ indented by _chars_ spaces. # # @param [String] text # @param [Fixnum] chars def indent(text, chars) text.lines.map { |l| "#{' ' * chars}#{l}" }.join end end end end pry-0.13.1/lib/pry/history.rb000066400000000000000000000106561364454223300160320ustar00rootroot00000000000000# frozen_string_literal: true class Pry # The History class is responsible for maintaining the user's input history, # both internally and within Readline. class History def self.default_file history_file = if (xdg_home = Pry::Env['XDG_DATA_HOME']) # See XDG Base Directory Specification at # https://standards.freedesktop.org/basedir-spec/basedir-spec-0.8.html xdg_home + '/pry/pry_history' elsif File.exist?(File.expand_path('~/.pry_history')) '~/.pry_history' else '~/.local/share/pry/pry_history' end File.expand_path(history_file) end attr_accessor :loader, :saver # @return [Fixnum] Number of lines in history when Pry first loaded. attr_reader :original_lines # @return [Integer] total number of lines, including original lines attr_reader :history_line_count def initialize(options = {}) @history = options[:history] || [] @history_line_count = @history.count @file_path = options[:file_path] @original_lines = 0 @loader = method(:read_from_file) @saver = method(:save_to_file) end # Load the input history using `History.loader`. # @return [Integer] The number of lines loaded def load @loader.call do |line| next if invalid_readline_line?(line) @history << line.chomp @original_lines += 1 @history_line_count += 1 end end # Add a line to the input history, ignoring blank and duplicate lines. # @param [String] line # @return [String] The same line that was passed in def push(line) return line if line.empty? || invalid_readline_line?(line) begin last_line = @history[-1] rescue IndexError last_line = nil end return line if line == last_line @history << line @history_line_count += 1 @saver.call(line) if !should_ignore?(line) && Pry.config.history_save line end alias << push # Clear this session's history. This won't affect the contents of the # history file. def clear @history.clear @history_line_count = 0 @original_lines = 0 end # @return [Fixnum] The number of lines in history from just this session. def session_line_count @history_line_count - @original_lines end # Return an Array containing all stored history. # @return [Array] An Array containing all lines of history loaded # or entered by the user in the current session. def to_a @history.to_a end # Filter the history with the histignore options # @return [Array] An array containing all the lines that are not # included in the histignore. def filter(history) history.select { |l| l unless should_ignore?(l) } end private # Check if the line match any option in the histignore # [Pry.config.history_ignorelist] # @return [Boolean] a boolean that notifies if the line was found in the # histignore array. def should_ignore?(line) hist_ignore = Pry.config.history_ignorelist return false if hist_ignore.nil? || hist_ignore.empty? hist_ignore.any? { |p| line.to_s.match(p) } end # The default loader. Yields lines from `Pry.config.history_file`. def read_from_file path = history_file_path File.foreach(path) { |line| yield(line) } if File.exist?(path) rescue SystemCallError => error warn "Unable to read history file: #{error.message}" end # The default saver. Appends the given line to `Pry.config.history_file`. def save_to_file(line) history_file.puts line if history_file end # The history file, opened for appending. def history_file if defined?(@history_file) @history_file else unless File.exist?(history_file_path) FileUtils.mkdir_p(File.dirname(history_file_path)) end @history_file = File.open(history_file_path, 'a', 0o600).tap do |file| file.sync = true end end rescue SystemCallError => error warn "Unable to write history file: #{error.message}" @history_file = false end def history_file_path File.expand_path(@file_path || Pry.config.history_file) end def invalid_readline_line?(line) # `Readline::HISTORY << line` raises an `ArgumentError` if `line` # includes a null byte line.include?("\0") end end end pry-0.13.1/lib/pry/hooks.rb000066400000000000000000000126021364454223300154450ustar00rootroot00000000000000# frozen_string_literal: true class Pry # Implements a hooks system for Pry. A hook is a callable that is associated # with an event. A number of events are currently provided by Pry, these # include: `:when_started`, `:before_session`, `:after_session`. A hook must # have a name, and is connected with an event by the `Pry::Hooks#add_hook` # method. # # @example Adding a hook for the `:before_session` event. # Pry.config.hooks.add_hook(:before_session, :say_hi) do # puts "hello" # end class Hooks def self.default hooks = new hooks.add_hook(:before_session, :default) do |_out, _target, pry_instance| next if pry_instance.quiet? pry_instance.run_command('whereami --quiet') end hooks end def initialize @hooks = Hash.new { |h, k| h[k] = [] } end # Ensure that duplicates have their @hooks object. def initialize_copy(_orig) hooks_dup = @hooks.dup @hooks.each do |k, v| hooks_dup[k] = v.dup end @hooks = hooks_dup end def errors @errors ||= [] end # Destructively merge the contents of two `Pry:Hooks` instances. # # @param [Pry::Hooks] other The `Pry::Hooks` instance to merge # @return [Pry:Hooks] The receiver. # @see #merge def merge!(other) @hooks.merge!(other.dup.hooks) do |_key, array, other_array| temp_hash = {} output = [] (array + other_array).reverse_each do |pair| temp_hash[pair.first] ||= output.unshift(pair) end output end self end # @example # hooks = Pry::Hooks.new.add_hook(:before_session, :say_hi) { puts "hi!" } # Pry::Hooks.new.merge(hooks) # @param [Pry::Hooks] other The `Pry::Hooks` instance to merge # @return [Pry::Hooks] a new `Pry::Hooks` instance containing a merge of the # contents of two `Pry:Hooks` instances. def merge(other) dup.tap do |v| v.merge!(other) end end # Add a new hook to be executed for the `event_name` event. # @param [Symbol] event_name The name of the event. # @param [Symbol] hook_name The name of the hook. # @param [#call] callable The callable. # @yield The block to use as the callable (if no `callable` provided). # @return [Pry:Hooks] The receiver. def add_hook(event_name, hook_name, callable = nil, &block) event_name = event_name.to_s # do not allow duplicates, but allow multiple `nil` hooks # (anonymous hooks) if hook_exists?(event_name, hook_name) && !hook_name.nil? raise ArgumentError, "Hook with name '#{hook_name}' already defined!" end raise ArgumentError, "Must provide a block or callable." if !block && !callable # ensure we only have one anonymous hook @hooks[event_name].delete_if { |h, _k| h.nil? } if hook_name.nil? if block @hooks[event_name] << [hook_name, block] elsif callable @hooks[event_name] << [hook_name, callable] end self end # Execute the list of hooks for the `event_name` event. # @param [Symbol] event_name The name of the event. # @param [Array] args The arguments to pass to each hook function. # @return [Object] The return value of the last executed hook. def exec_hook(event_name, *args, &block) @hooks[event_name.to_s].map do |_hook_name, callable| begin callable.call(*args, &block) rescue RescuableException => e errors << e e end end.last end # @param [Symbol] event_name The name of the event. # @return [Fixnum] The number of hook functions for `event_name`. def hook_count(event_name) @hooks[event_name.to_s].size end # @param [Symbol] event_name The name of the event. # @param [Symbol] hook_name The name of the hook # @return [#call] a specific hook for a given event. def get_hook(event_name, hook_name) hook = @hooks[event_name.to_s].find do |current_hook_name, _callable| current_hook_name == hook_name end hook.last if hook end # @param [Symbol] event_name The name of the event. # @return [Hash] The hash of hook names / hook functions. # @note Modifying the returned hash does not alter the hooks, use # `add_hook`/`delete_hook` for that. def get_hooks(event_name) Hash[@hooks[event_name.to_s]] end # @param [Symbol] event_name The name of the event. # @param [Symbol] hook_name The name of the hook. # to delete. # @return [#call] The deleted hook. def delete_hook(event_name, hook_name) deleted_callable = nil @hooks[event_name.to_s].delete_if do |current_hook_name, callable| if current_hook_name == hook_name deleted_callable = callable true else false end end deleted_callable end # Clear all hooks functions for a given event. # # @param [String] event_name The name of the event. # @return [void] def clear_event_hooks(event_name) @hooks[event_name.to_s] = [] end # @param [Symbol] event_name Name of the event. # @param [Symbol] hook_name Name of the hook. # @return [Boolean] Whether the hook by the name `hook_name`. def hook_exists?(event_name, hook_name) @hooks[event_name.to_s].map(&:first).include?(hook_name) end protected attr_reader :hooks end end pry-0.13.1/lib/pry/indent.rb000066400000000000000000000334051364454223300156070ustar00rootroot00000000000000# frozen_string_literal: true class Pry ## # Pry::Indent is a class that can be used to indent a number of lines # containing Ruby code similar as to how IRB does it (but better). The class # works by tokenizing a string using CodeRay and then looping over those # tokens. Based on the tokens in a line of code that line (or the next one) # will be indented or un-indented by correctly. # class Indent include Helpers::BaseHelpers # Raised if {#module_nesting} would not work. class UnparseableNestingError < StandardError; end # @return [String] String containing the spaces to be inserted before the next line. attr_reader :indent_level # @return [Array] The stack of open tokens. attr_reader :stack # The amount of spaces to insert for each indent level. SPACES = ' '.freeze # Hash containing all the tokens that should increase the indentation # level. The keys of this hash are open tokens, the values the matching # tokens that should prevent a line from being indented if they appear on # the same line. OPEN_TOKENS = { 'def' => 'end', 'class' => 'end', 'module' => 'end', 'do' => 'end', 'if' => 'end', 'unless' => 'end', 'while' => 'end', 'until' => 'end', 'for' => 'end', 'case' => 'end', 'begin' => 'end', '[' => ']', '{' => '}', '(' => ')' }.freeze # Which tokens can either be open tokens, or appear as modifiers on # a single-line. SINGLELINE_TOKENS = %w[if while until unless rescue].freeze # Which tokens can be followed by an optional "do" keyword. OPTIONAL_DO_TOKENS = %w[for while until].freeze # Collection of token types that should be ignored. Without this list # keywords such as "class" inside strings would cause the code to be # indented incorrectly. # # :pre_constant and :preserved_constant are the CodeRay 0.9.8 and 1.0.0 # classifications of "true", "false", and "nil". IGNORE_TOKENS = [:space, :content, :string, :method, :ident, :constant, :pre_constant, :predefined_constant].freeze # Tokens that indicate the end of a statement (i.e. that, if they appear # directly before an "if" indicates that that if applies to the same line, # not the next line) # # :reserved and :keywords are the CodeRay 0.9.8 and 1.0.0 respectively # classifications of "super", "next", "return", etc. STATEMENT_END_TOKENS = IGNORE_TOKENS + [:regexp, :integer, :float, :keyword, :delimiter, :reserved, :instance_variable, :class_variable, :global_variable] # Collection of tokens that should appear dedented even though they # don't affect the surrounding code. MIDWAY_TOKENS = %w[when else elsif ensure rescue].freeze # Clean the indentation of a fragment of ruby. # # @param [String] str # @return [String] def self.indent(str) new.indent(str) end # Get the module nesting at the given point in the given string. # # NOTE If the line specified contains a method definition, then the nesting # at the start of the method definition is used. Otherwise the nesting from # the end of the line is used. # # @param [String] str The ruby code to analyze # @param [Fixnum] line_number The line number (starting from 1) # @return [Array] def self.nesting_at(str, line_number) indent = new lines = str.split("\n") n = line_number - 1 to_indent = lines[0...n] << (lines[n] || "").split("def").first(1) indent.indent(to_indent.join("\n") << "\n") indent.module_nesting end def initialize(pry_instance = Pry.new) @pry_instance = pry_instance reset end # reset internal state def reset @stack = [] @indent_level = '' @heredoc_queue = [] @close_heredocs = {} @string_start = nil @awaiting_class = false @module_nesting = [] self end # Indents a string and returns it. This string can either be a single line # or multiple ones. # # @example # str = <] def module_nesting @module_nesting.map do |(kind, token)| raise UnparseableNestingError, @module_nesting.inspect if token.nil? "#{kind} #{token}" end end # Return a string which, when printed, will rewrite the previous line with # the correct indentation. Mostly useful for fixing 'end'. # # @param [String] prompt The user's prompt # @param [String] code The code the user just typed in # @param [Integer] overhang The number of characters to erase afterwards (the # the difference in length between the old line and the new one) # # @return [String] correctly indented line def correct_indentation(prompt, code, overhang = 0) prompt = prompt.delete("\001\002") line_to_measure = Pry::Helpers::Text.strip_color(prompt) << code whitespace = ' ' * overhang cols = @pry_instance.output.width lines = cols == 0 ? 1 : (line_to_measure.length / cols + 1).to_i if Helpers::Platform.windows_ansi? move_up = "\e[#{lines}F" move_down = "\e[#{lines}E" else move_up = "\e[#{lines}A\e[0G" move_down = "\e[#{lines}B\e[0G" end "#{move_up}#{prompt}#{colorize_code(code)}#{whitespace}#{move_down}" end end end pry-0.13.1/lib/pry/input_completer.rb000066400000000000000000000243361364454223300175420ustar00rootroot00000000000000# frozen_string_literal: true # taken from irb # Implements tab completion for Readline in Pry class Pry class InputCompleter NUMERIC_REGEXP = /^(-?(0[dbo])?[0-9_]+(\.[0-9_]+)?([eE]-?[0-9]+)?)\.([^.]*)$/.freeze ARRAY_REGEXP = /^([^\]]*\])\.([^.]*)$/.freeze SYMBOL_REGEXP = /^(:[^:.]*)$/.freeze SYMBOL_METHOD_CALL_REGEXP = /^(:[^:.]+)\.([^.]*)$/.freeze REGEX_REGEXP = %r{^(/[^/]*/)\.([^.]*)$}.freeze PROC_OR_HASH_REGEXP = /^([^\}]*\})\.([^.]*)$/.freeze TOPLEVEL_LOOKUP_REGEXP = /^::([A-Z][^:\.\(]*)$/.freeze CONSTANT_REGEXP = /^([A-Z][A-Za-z0-9]*)$/.freeze CONSTANT_OR_METHOD_REGEXP = /^([A-Z].*)::([^:.]*)$/.freeze HEX_REGEXP = /^(-?0x[0-9a-fA-F_]+)\.([^.]*)$/.freeze GLOBALVARIABLE_REGEXP = /^(\$[^.]*)$/.freeze VARIABLE_REGEXP = /^([^."].*)\.([^.]*)$/.freeze RESERVED_WORDS = %w[ BEGIN END alias and begin break case class def defined do else elsif end ensure false for if in module next nil not or redo rescue retry return self super then true undef unless until when while yield ].freeze WORD_ESCAPE_STR = " \t\n\"\\'`><=;|&{(".freeze def initialize(input, pry = nil) @pry = pry @input = input if @input.respond_to?(:basic_word_break_characters=) @input.basic_word_break_characters = WORD_ESCAPE_STR end return unless @input.respond_to?(:completion_append_character=) @input.completion_append_character = nil end # Return a new completion proc for use by Readline. # rubocop:disable Metrics/AbcSize, Metrics/MethodLength def call(str, options = {}) custom_completions = options[:custom_completions] || [] # if there are multiple contexts e.g. cd 1/2/3 # get new target for 1/2 and find candidates for 3 path, input = build_path(str) if path.call.empty? target = options[:target] else # Assume the user is tab-completing the 'cd' command begin target = Pry::ObjectPath.new(path.call, @pry.binding_stack).resolve.last # but if that doesn't work, assume they're doing division with no spaces rescue Pry::CommandError target = options[:target] end end begin bind = target # Complete stdlib symbols case input when REGEX_REGEXP # Regexp receiver = Regexp.last_match(1) message = Regexp.quote(Regexp.last_match(2)) candidates = Regexp.instance_methods.collect(&:to_s) select_message(path, receiver, message, candidates) when ARRAY_REGEXP # Array receiver = Regexp.last_match(1) message = Regexp.quote(Regexp.last_match(2)) candidates = Array.instance_methods.collect(&:to_s) select_message(path, receiver, message, candidates) when PROC_OR_HASH_REGEXP # Proc or Hash receiver = Regexp.last_match(1) message = Regexp.quote(Regexp.last_match(2)) candidates = Proc.instance_methods.collect(&:to_s) candidates |= Hash.instance_methods.collect(&:to_s) select_message(path, receiver, message, candidates) when SYMBOL_REGEXP # Symbol if Symbol.respond_to?(:all_symbols) sym = Regexp.quote(Regexp.last_match(1)) candidates = Symbol.all_symbols.collect { |s| ":" + s.id2name } candidates.grep(/^#{sym}/) else [] end when TOPLEVEL_LOOKUP_REGEXP # Absolute Constant or class methods receiver = Regexp.last_match(1) candidates = Object.constants.collect(&:to_s) candidates.grep(/^#{receiver}/).collect { |e| "::" + e } when CONSTANT_REGEXP # Constant message = Regexp.last_match(1) begin context = target.eval("self") context = context.class unless context.respond_to? :constants candidates = context.constants.collect(&:to_s) rescue StandardError candidates = [] end candidates = candidates.grep(/^#{message}/).collect(&path) when CONSTANT_OR_METHOD_REGEXP # Constant or class methods receiver = Regexp.last_match(1) message = Regexp.quote(Regexp.last_match(2)) begin candidates = eval( # rubocop:disable Security/Eval "#{receiver}.constants.collect(&:to_s)", bind, __FILE__, __LINE__ ) candidates |= eval( # rubocop:disable Security/Eval "#{receiver}.methods.collect(&:to_s)", bind, __FILE__, __LINE__ ) rescue Pry::RescuableException candidates = [] end candidates.grep(/^#{message}/).collect { |e| receiver + "::" + e } when SYMBOL_METHOD_CALL_REGEXP # method call on a Symbol receiver = Regexp.last_match(1) message = Regexp.quote(Regexp.last_match(2)) candidates = Symbol.instance_methods.collect(&:to_s) select_message(path, receiver, message, candidates) when NUMERIC_REGEXP # Numeric receiver = Regexp.last_match(1) message = Regexp.quote(Regexp.last_match(5)) begin # rubocop:disable Security/Eval candidates = eval(receiver, bind).methods.collect(&:to_s) # rubocop:enable Security/Eval rescue Pry::RescuableException candidates = [] end select_message(path, receiver, message, candidates) when HEX_REGEXP # Numeric(0xFFFF) receiver = Regexp.last_match(1) message = Regexp.quote(Regexp.last_match(2)) begin # rubocop:disable Security/Eval candidates = eval(receiver, bind).methods.collect(&:to_s) # rubocop:enable Security/Eval rescue Pry::RescuableException candidates = [] end select_message(path, receiver, message, candidates) when GLOBALVARIABLE_REGEXP # global regmessage = Regexp.new(Regexp.quote(Regexp.last_match(1))) candidates = global_variables.collect(&:to_s).grep(regmessage) when VARIABLE_REGEXP # variable receiver = Regexp.last_match(1) message = Regexp.quote(Regexp.last_match(2)) gv = eval("global_variables", bind, __FILE__, __LINE__).collect(&:to_s) lv = eval("local_variables", bind, __FILE__, __LINE__).collect(&:to_s) cv = eval("self.class.constants", bind, __FILE__, __LINE__).collect(&:to_s) if (gv | lv | cv).include?(receiver) || /^[A-Z]/ =~ receiver && /\./ !~ receiver # foo.func and foo is local var. OR # Foo::Bar.func begin candidates = eval( # rubocop:disable Security/Eval "#{receiver}.methods", bind, __FILE__, __LINE__ ).collect(&:to_s) rescue Pry::RescuableException candidates = [] end else # func1.func2 require 'set' candidates = Set.new to_ignore = ignored_modules ObjectSpace.each_object(Module) do |m| next if begin to_ignore.include?(m) rescue StandardError true end # jruby doesn't always provide #instance_methods() on each # object. if m.respond_to?(:instance_methods) candidates.merge m.instance_methods(false).collect(&:to_s) end end end select_message(path, receiver, message, candidates.sort) when /^\.([^.]*)$/ # Unknown(maybe String) receiver = "" message = Regexp.quote(Regexp.last_match(1)) candidates = String.instance_methods(true).collect(&:to_s) select_message(path, receiver, message, candidates) else candidates = eval( "methods | private_methods | local_variables | " \ "self.class.constants | instance_variables", bind, __FILE__, __LINE__ - 2 ).collect(&:to_s) if eval("respond_to?(:class_variables)", bind, __FILE__, __LINE__) candidates += eval( "class_variables", bind, __FILE__, __LINE__ ).collect(&:to_s) end candidates = (candidates | RESERVED_WORDS | custom_completions) .grep(/^#{Regexp.quote(input)}/) candidates.collect(&path) end rescue Pry::RescuableException [] end end # rubocop:enable Metrics/AbcSize, Metrics/MethodLength def select_message(path, receiver, message, candidates) candidates.grep(/^#{message}/).collect do |e| next unless e =~ /^[a-zA-Z_]/ path.call(receiver + "." + e) end.compact end # build_path seperates the input into two parts: path and input. # input is the partial string that should be completed # path is a proc that takes an input and builds a full path. def build_path(input) # check to see if the input is a regex return proc { |i| i.to_s }, input if input[%r{/\.}] trailing_slash = input.end_with?('/') contexts = input.chomp('/').split(%r{/}) input = contexts[-1] path = proc do |i| p = contexts[0..-2].push(i).join('/') p += '/' if trailing_slash && !i.nil? p end [path, input] end def ignored_modules # We could cache the result, but IRB is not loaded by default. # And this is very fast anyway. # By using this approach, we avoid Module#name calls, which are # relatively slow when there are a lot of anonymous modules defined. s = Set.new scanner = lambda do |m| next if s.include?(m) # IRB::ExtendCommandBundle::EXCB recurses. s << m m.constants(false).each do |c| value = m.const_get(c) scanner.call(value) if value.is_a?(Module) end end # FIXME: Add Pry here as well? [:IRB, :SLex, :RubyLex, :RubyToken].each do |module_name| next unless Object.const_defined?(module_name) scanner.call(Object.const_get(module_name)) end s.delete(IRB::Context) if defined?(IRB::Context) s end end end pry-0.13.1/lib/pry/input_lock.rb000066400000000000000000000116221364454223300164720ustar00rootroot00000000000000# frozen_string_literal: true class Pry # There is one InputLock per input (such as STDIN) as two REPLs on the same # input makes things delirious. InputLock serializes accesses to the input so # that threads to not conflict with each other. The latest thread to request # ownership of the input wins. class InputLock class Interrupt < Exception; end # rubocop:disable Lint/InheritException class << self attr_accessor :input_locks attr_accessor :global_lock end self.input_locks = {} self.global_lock = Mutex.new def self.for(input) # XXX This method leaks memory, as we never unregister an input once we # are done with it. Fortunately, the leak is tiny (or so we hope). In # usual scenarios, we would leak the StringIO that is passed to be # evaluated from the command line. global_lock.synchronize do input_locks[input] ||= Pry::InputLock.new end end def initialize @mutex = Mutex.new @cond = ConditionVariable.new @owners = [] @interruptible = false end # Adds ourselves to the ownership list. The last one in the list may access # the input through interruptible_region(). def __with_ownership @mutex.synchronize do # Three cases: # 1) There are no owners, in this case we are good to go. # 2) The current owner of the input is not reading the input (it might # just be evaluating some ruby that the user typed). # The current owner will figure out that it cannot go back to reading # the input since we are adding ourselves to the @owners list, which # in turns makes us the current owner. # 3) The owner of the input is in the interruptible region, reading from # the input. It's safe to send an Interrupt exception to interrupt # the owner. It will then proceed like in case 2). # We wait until the owner sets the interruptible flag back # to false, meaning that he's out of the interruptible region. # Note that the owner may receive multiple interrupts since, but that # should be okay (and trying to avoid it is futile anyway). while @interruptible @owners.last.raise Interrupt @cond.wait(@mutex) end @owners << Thread.current end yield ensure @mutex.synchronize do # We are releasing any desire to have the input ownership by removing # ourselves from the list. @owners.delete(Thread.current) # We need to wake up the thread at the end of the @owners list, but # sadly Ruby doesn't allow us to choose which one we wake up, so we wake # them all up. @cond.broadcast end end def with_ownership(&block) # If we are in a nested with_ownership() call (nested pry context), we do nothing. nested = @mutex.synchronize { @owners.include?(Thread.current) } nested ? yield : __with_ownership(&block) end def enter_interruptible_region @mutex.synchronize do # We patiently wait until we are the owner. This may happen as another # thread calls with_ownership() because of a binding.pry happening in # another thread. @cond.wait(@mutex) until @owners.last == Thread.current # We are the legitimate owner of the input. We mark ourselves as # interruptible, so other threads can send us an Interrupt exception # while we are blocking from reading the input. @interruptible = true end end def leave_interruptible_region @mutex.synchronize do # We check if we are still the owner, because we could have received an # Interrupt right after the following @cond.broadcast, making us retry. @interruptible = false if @owners.last == Thread.current @cond.broadcast end rescue Interrupt # We need to guard against a spurious interrupt delivered while we are # trying to acquire the lock (the rescue block is no longer in our scope). retry end def interruptible_region enter_interruptible_region # XXX Note that there is a chance that we get the interrupt right after # the readline call succeeded, but we'll never know, and we will retry the # call, discarding that piece of input. yield rescue Interrupt # We were asked to back off. The one requesting the interrupt will be # waiting on the conditional for the interruptible flag to change to false. # Note that there can be some inefficiency, as we could immediately # succeed in enter_interruptible_region(), even before the one requesting # the ownership has the chance to register itself as an owner. # To mitigate the issue, we sleep a little bit. leave_interruptible_region sleep 0.01 retry ensure leave_interruptible_region end end end pry-0.13.1/lib/pry/inspector.rb000066400000000000000000000022121364454223300163240ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Inspector MAP = { "default" => { value: Pry.config.print, description: <<-DESCRIPTION.each_line.map(&:lstrip!) The default Pry inspector. It has paging and color support, and uses pretty_inspect when printing an object. DESCRIPTION }, "simple" => { value: proc do |output, value| begin output.puts value.inspect rescue RescuableException output.puts "unknown" end end, description: <<-DESCRIPTION.each_line.map(&:lstrip) A simple inspector that uses #puts and #inspect when printing an object. It has no pager, color, or pretty_inspect support. DESCRIPTION }, "clipped" => { value: proc do |output, value| output.puts Pry.view_clip(value, id: true) end, description: <<-DESCRIPTION.each_line.map(&:lstrip) The clipped inspector has the same features as the 'simple' inspector but prints large objects as a smaller string. DESCRIPTION } }.freeze end end pry-0.13.1/lib/pry/last_exception.rb000066400000000000000000000026521364454223300173470ustar00rootroot00000000000000# frozen_string_literal: true # # {Pry::LastException} is a proxy class who wraps an Exception object for # {Pry#last_exception}. it extends the exception object with methods that # help pry commands be useful. # # the original exception object is not modified and method calls are forwarded # to the wrapped exception object. # class Pry class LastException < BasicObject attr_accessor :bt_index def initialize(exception) @exception = exception @bt_index = 0 @file, @line = bt_source_location_for(0) end def method_missing(name, *args, &block) if @exception.respond_to?(name) @exception.public_send(name, *args, &block) else super end end def respond_to_missing?(name, include_all = false) @exception.respond_to?(name, include_all) end # # @return [String] # returns the path to a file for the current backtrace. see {#bt_index}. # attr_reader :file # # @return [Fixnum] # returns the line for the current backtrace. see {#bt_index}. # attr_reader :line # @return [Exception] # returns the wrapped exception # def wrapped_exception @exception end def bt_source_location_for(index) backtrace[index] =~ /(.*):(\d+)/ [::Regexp.last_match(1), ::Regexp.last_match(2).to_i] end def inc_bt_index @bt_index = (@bt_index + 1) % backtrace.size end end end pry-0.13.1/lib/pry/method.rb000066400000000000000000000470111364454223300156040ustar00rootroot00000000000000# frozen_string_literal: true require 'method_source' class Pry class << self # If the given object is a `Pry::Method`, return it unaltered. If it's # anything else, return it wrapped in a `Pry::Method` instance. def Method(obj) if obj.is_a? Pry::Method obj else Pry::Method.new(obj) end end end # This class wraps the normal `Method` and `UnboundMethod` classes # to provide extra functionality useful to Pry. class Method # rubocop:disable Metrics/ClassLength extend Helpers::BaseHelpers extend Forwardable include Helpers::BaseHelpers include Helpers::DocumentationHelpers include CodeObject::Helpers class << self # Given a string representing a method name and optionally a binding to # search in, find and return the requested method wrapped in a # `Pry::Method` instance. # # @param [String] name The name of the method to retrieve. # @param [Binding] target The context in which to search for the method. # @param [Hash] options # @option options [Boolean] :instance Look for an instance method if # `name` doesn't contain any context. # @option options [Boolean] :methods Look for a bound/singleton method if # `name` doesn't contain any context. # @return [Pry::Method, nil] A `Pry::Method` instance containing the # requested method, or `nil` if name is `nil` or no method could be # located matching the parameters. def from_str(name, target = TOPLEVEL_BINDING, options = {}) if name.nil? nil elsif name.to_s =~ /(.+)\#(\S+)\Z/ context = Regexp.last_match(1) meth_name = Regexp.last_match(2) from_module(target.eval(context), meth_name, target) elsif name.to_s =~ /(.+)(\[\])\Z/ context = Regexp.last_match(1) meth_name = Regexp.last_match(2) from_obj(target.eval(context), meth_name, target) elsif name.to_s =~ /(.+)(\.|::)(\S+)\Z/ context = Regexp.last_match(1) meth_name = Regexp.last_match(3) from_obj(target.eval(context), meth_name, target) elsif options[:instance] from_module(target.eval("self"), name, target) elsif options[:methods] from_obj(target.eval("self"), name, target) else from_str(name, target, instance: true) || from_str(name, target, methods: true) end rescue Pry::RescuableException nil end # Given a `Binding`, try to extract the `::Method` it originated from and # use it to instantiate a `Pry::Method`. Return `nil` if this isn't # possible. # # @param [Binding] binding # @return [Pry::Method, nil] # def from_binding(binding) meth_name = binding.eval('::Kernel.__method__') if [:__script__, nil].include?(meth_name) nil else method = begin if Object === binding.eval('self') # rubocop:disable Style/CaseEquality new( Kernel.instance_method(:method) .bind(binding.eval("self")) .call(meth_name) ) else str = 'class << self; self; end' \ '.instance_method(::Kernel.__method__).bind(self)' new(binding.eval(str)) end rescue NameError, NoMethodError # rubocop:disable Lint/ShadowedException Disowned.new(binding.eval('self'), meth_name.to_s) end if WeirdMethodLocator.weird_method?(method, binding) WeirdMethodLocator.new(method, binding).find_method || method else method end end end # In order to support 2.0 Refinements we need to look up methods # inside the relevant Binding. # @param [Object] obj The owner/receiver of the method. # @param [Symbol] method_name The name of the method. # @param [Symbol] method_type The type of method: :method or :instance_method # @param [Binding] target The binding where the method is looked up. # @return [Method, UnboundMethod] The 'refined' method object. def lookup_method_via_binding( obj, method_name, method_type, target = TOPLEVEL_BINDING ) Pry.current[:obj] = obj Pry.current[:name] = method_name receiver = obj.is_a?(Module) ? "Module" : "Kernel" target.eval( "::#{receiver}.instance_method(:#{method_type})" \ ".bind(Pry.current[:obj]).call(Pry.current[:name])" ) ensure Pry.current[:obj] = Pry.current[:name] = nil end # Given a `Class` or `Module` and the name of a method, try to # instantiate a `Pry::Method` containing the instance method of # that name. Return `nil` if no such method exists. # # @param [Class, Module] klass # @param [String] name # @param [Binding] target The binding where the method is looked up. # @return [Pry::Method, nil] def from_class(klass, name, target = TOPLEVEL_BINDING) new(lookup_method_via_binding(klass, name, :instance_method, target)) rescue StandardError nil end alias from_module from_class # Given an object and the name of a method, try to instantiate # a `Pry::Method` containing the method of that name bound to # that object. Return `nil` if no such method exists. # # @param [Object] obj # @param [String] name # @param [Binding] target The binding where the method is looked up. # @return [Pry::Method, nil] def from_obj(obj, name, target = TOPLEVEL_BINDING) new(lookup_method_via_binding(obj, name, :method, target)) rescue StandardError nil end # Get all of the instance methods of a `Class` or `Module` # @param [Class,Module] klass # @param [Boolean] include_super Whether to include methods from ancestors. # @return [Array[Pry::Method]] def all_from_class(klass, include_super = true) %w[public protected private].flat_map do |visibility| safe_send( klass, :"#{visibility}_instance_methods", include_super ).map do |method_name| new( safe_send(klass, :instance_method, method_name), visibility: visibility.to_sym ) end end end # # Get all of the methods on an `Object` # # @param [Object] obj # # @param [Boolean] include_super # indicates whether or not to include methods from ancestors. # # @return [Array[Pry::Method]] # def all_from_obj(obj, include_super = true) all_from_class(singleton_class_of(obj), include_super) end # Get every `Class` and `Module`, in order, that will be checked when looking # for an instance method to call on this object. # @param [Object] obj # @return [Array[Class, Module]] def resolution_order(obj) if Class === obj # rubocop:disable Style/CaseEquality singleton_class_resolution_order(obj) + instance_resolution_order(Class) else klass = begin singleton_class_of(obj) rescue StandardError obj.class end instance_resolution_order(klass) end end # Get every `Class` and `Module`, in order, that will be checked when looking # for methods on instances of the given `Class` or `Module`. # This does not treat singleton classes of classes specially. # @param [Class, Module] klass # @return [Array[Class, Module]] def instance_resolution_order(klass) # include klass in case it is a singleton class, ([klass] + Pry::Method.safe_send(klass, :ancestors)).uniq end def method_definition?(name, definition_line) singleton_method_definition?(name, definition_line) || instance_method_definition?(name, definition_line) end def singleton_method_definition?(name, definition_line) regexp = /^define_singleton_method\(?\s*[:\"\']#{Regexp.escape(name)}| ^def\s*self\.#{Regexp.escape(name)}/x regexp =~ definition_line.strip end def instance_method_definition?(name, definition_line) regexp = /^define_method\(?\s*[:\"\']#{Regexp.escape(name)}| ^def\s*#{Regexp.escape(name)}/x regexp =~ definition_line.strip end # Get the singleton classes of superclasses that could define methods on # the given class object, and any modules they include. # If a module is included at multiple points in the ancestry, only # the lowest copy will be returned. def singleton_class_resolution_order(klass) ancestors = Pry::Method.safe_send(klass, :ancestors) resolution_order = ancestors.grep(Class).flat_map do |anc| [singleton_class_of(anc), *singleton_class_of(anc).included_modules] end resolution_order.reverse.uniq.reverse - Class.included_modules end def singleton_class_of(obj) class << obj; self; end rescue TypeError # can't define singleton. Fixnum, Symbol, Float, ... obj.class end end # Workaround for https://github.com/pry/pry/pull/2086 def_delegators :@method, :owner, :parameters, :receiver # A new instance of `Pry::Method` wrapping the given `::Method`, # `UnboundMethod`, or `Proc`. # # @param [::Method, UnboundMethod, Proc] method # @param [Hash] known_info Can be used to pre-cache expensive to compute stuff. # @return [Pry::Method] def initialize(method, known_info = {}) @method = method @visibility = known_info[:visibility] end # Get the name of the method as a String, regardless of the underlying # Method#name type. # # @return [String] def name @method.name.to_s end # Get the owner of the method as a Pry::Module # @return [Pry::Module] def wrapped_owner @wrapped_owner ||= Pry::WrappedModule.new(owner) end # Get underlying object wrapped by this Pry::Method instance # @return [Method, UnboundMethod, Proc] def wrapped @method end # Is the method undefined? (aka `Disowned`) # @return [Boolean] false def undefined? false end # Get the name of the method including the class on which it was defined. # @example # method(:puts).method_name # => "Kernel.puts" # @return [String] def name_with_owner "#{wrapped_owner.method_prefix}#{name}" end # @return [String, nil] The source code of the method, or `nil` if it's unavailable. def source @source ||= case source_type when :c c_source when :ruby ruby_source end end # Update the live copy of the method's source. def redefine(source) Patcher.new(self).patch_in_ram source Pry::Method(owner.instance_method(name)) end # Can we get the source code for this method? # @return [Boolean] def source? !!source rescue MethodSource::SourceNotFoundError false end # @return [String, nil] The documentation for the method, or `nil` if it's # unavailable. def doc @doc ||= case source_type when :c info = pry_doc_info info.docstring if info when :ruby get_comment_content(comment) end end # @return [Symbol] The source type of the method. The options are # `:ruby` for Ruby methods or `:c` for methods written in C. def source_type source_location.nil? ? :c : :ruby end # @return [String, nil] The name of the file the method is defined in, or # `nil` if the filename is unavailable. def source_file if source_location.nil? if source_type == :c info = pry_doc_info info.file if info end else source_location.first end end # @return [Fixnum, nil] The line of code in `source_file` which begins # the method's definition, or `nil` if that information is unavailable. def source_line source_location.nil? ? nil : source_location.last end # @return [Range, nil] The range of lines in `source_file` which contain # the method's definition, or `nil` if that information is unavailable. def source_range source_location.nil? ? nil : (source_line)..(source_line + source.lines.count - 1) end # @return [Symbol] The visibility of the method. May be `:public`, # `:protected`, or `:private`. def visibility @visibility ||= if owner.public_instance_methods.any? { |m| m.to_s == name } :public elsif owner.protected_instance_methods.any? { |m| m.to_s == name } :protected elsif owner.private_instance_methods.any? { |m| m.to_s == name } :private else :none end end # @return [String] A representation of the method's signature, including its # name and parameters. Optional and "rest" parameters are marked with `*` # and block parameters with `&`. Keyword arguments are shown with `:` # If the parameter names are unavailable, they're given numbered names instead. # Paraphrased from `awesome_print` gem. def signature if respond_to?(:parameters) args = parameters.inject([]) do |args_array, (arg_type, name)| name ||= (arg_type == :block ? 'block' : "arg#{args_array.size + 1}") args_array.push( case arg_type when :req then name.to_s when :opt then "#{name}=?" when :rest then "*#{name}" when :block then "&#{name}" when :key then "#{name}:?" when :keyreq then "#{name}:" else '?' end ) end else args = (1..arity.abs).map { |i| "arg#{i}" } args[-1] = "*#{args[-1]}" if arity < 0 end "#{name}(#{args.join(', ')})" end # @return [Pry::Method, nil] The wrapped method that is called when you # use "super" in the body of this method. def super(times = 1) if @method.is_a?(UnboundMethod) sup = super_using_ancestors(Pry::Method.instance_resolution_order(owner), times) else sup = super_using_ancestors(Pry::Method.resolution_order(receiver), times) sup &&= sup.bind(receiver) end Pry::Method.new(sup) if sup end # @return [String, nil] The original name the method was defined under, # before any aliasing, or `nil` if it can't be determined. def original_name return nil if source_type != :ruby method_name_from_first_line(source.lines.first) end # @return [Boolean] Was the method defined outside a source file? def dynamically_defined? !!(source_file && source_file =~ /(\(.*\))|<.*>/) end # @return [Boolean] Whether the method is unbound. def unbound_method? is_a?(::UnboundMethod) end # @return [Boolean] Whether the method is bound. def bound_method? is_a?(::Method) end # @return [Boolean] Whether the method is a singleton method. def singleton_method? wrapped_owner.singleton_class? end # @return [Boolean] Was the method defined within the Pry REPL? def pry_method? source_file == Pry.eval_path end # @return [Array] All known aliases for the method. def aliases owner = @method.owner # Avoid using `to_sym` on {Method#name}, which returns a `String`, because # it won't be garbage collected. name = @method.name all_methods_to_compare = owner.instance_methods | owner.private_instance_methods alias_list = all_methods_to_compare.combination(2).select do |pair| pair.include?(name) && owner.instance_method(pair.first) == owner.instance_method(pair.last) end.flatten alias_list.delete(name) alias_list.map(&:to_s) end # @return [Boolean] Is the method definitely an alias? def alias? name != original_name end # @return [Boolean] def ==(other) return other == @method if other.is_a?(Pry::Method) @method == other end # @param [Class] klass # @return [Boolean] def is_a?(klass) (klass == Pry::Method) || @method.is_a?(klass) end alias kind_of? is_a? # @param [String, Symbol] method_name # @return [Boolean] def respond_to?(method_name, include_all = false) super || @method.respond_to?(method_name, include_all) end # Delegate any unknown calls to the wrapped method. def method_missing(method_name, *args, &block) if @method.respond_to?(method_name) @method.__send__(method_name, *args, &block) else super end end def respond_to_missing?(method_name, include_private = false) @method.respond_to?(method_name) || super end def comment Pry::Code.from_file(source_file).comment_describing(source_line) end private # @return [YARD::CodeObjects::MethodObject] # @raise [CommandError] when the method can't be found or `pry-doc` isn't installed. def pry_doc_info if defined?(PryDoc) Pry::MethodInfo.info_for(@method) || raise( CommandError, "Cannot locate this method: #{name}. (source_location returns nil)" ) else fail_msg = "Cannot locate this method: #{name}." if Helpers::Platform.mri? fail_msg += " Invoke the 'gem-install pry-doc' Pry command to get " \ "access to Ruby Core documentation.\n" end raise CommandError, fail_msg end end # @param [Class, Module] ancestors The ancestors to investigate # @return [Method] The unwrapped super-method def super_using_ancestors(ancestors, times = 1) next_owner = owner times.times do i = ancestors.index(next_owner) + 1 while ancestors[i] && !(ancestors[i].method_defined?(name) || ancestors[i].private_method_defined?(name)) i += 1 end (next_owner = ancestors[i]) || (return nil) end begin safe_send(next_owner, :instance_method, name) rescue StandardError nil end end # @param [String] first_ln The first line of a method definition. # @return [String, nil] def method_name_from_first_line(first_ln) return nil if first_ln.strip !~ /^def / tokens = SyntaxHighlighter.tokenize(first_ln) tokens = tokens.tokens.each_slice(2) if tokens.respond_to?(:tokens) tokens.each_cons(2) do |t1, t2| if t2.last == :method || t2.last == :ident && t1 == [".", :operator] return t2.first end end nil end def c_source info = pry_doc_info strip_comments_from_c_code(info.source) if info && info.source end def ruby_source # Clone of `MethodSource.source_helper` that knows to use our # hacked version of `source_location` for our input buffer for methods # defined in `(pry)`. file, line = *source_location unless file raise SourceNotFoundError, "Could not locate source for #{name_with_owner}!" end begin code = Pry::Code.from_file(file).expression_at(line) rescue SyntaxError => e raise MethodSource::SourceNotFoundError, e.message end strip_leading_whitespace(code) end end end pry-0.13.1/lib/pry/method/000077500000000000000000000000001364454223300152545ustar00rootroot00000000000000pry-0.13.1/lib/pry/method/disowned.rb000066400000000000000000000034061364454223300174200ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Method # A Disowned Method is one that's been removed from the class on which it was defined. # # e.g. # class C # def foo # C.send(:undefine_method, :foo) # Pry::Method.from_binding(binding) # end # end # # In this case we assume that the "owner" is the singleton class of the receiver. # # This occurs mainly in Sinatra applications. class Disowned < Method attr_reader :receiver, :name # Create a new Disowned method. # # @param [Object] receiver # @param [String] method_name def initialize(receiver, method_name) @receiver = receiver @name = method_name @method = nil end # Is the method undefined? (aka `Disowned`) # @return [Boolean] true def undefined? true end # Can we get the source for this method? # @return [Boolean] false def source? false end # Get the hypothesized owner of the method. # # @return [Object] def owner class << receiver; self; end end # Raise a more useful error message instead of trying to forward to nil. # rubocop:disable Style/MethodMissingSuper def method_missing(method_name, *args, &block) if method(:name).respond_to?(method_name) raise "Cannot call '#{method_name}' on an undef'd method." end method = Object.instance_method(:method_missing).bind(self) method.call(method_name, *args, &block) end # rubocop:enable Style/MethodMissingSuper def respond_to_missing?(method_name, include_private = false) !method(:name).respond_to?(method_name) || super end end end end pry-0.13.1/lib/pry/method/patcher.rb000066400000000000000000000104711364454223300172320ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Method class Patcher attr_accessor :method # rubocop:disable Style/ClassVars @@source_cache = {} # rubocop:enable Style/ClassVars def initialize(method) @method = method end def self.code_for(filename) @@source_cache[filename] end # perform the patch def patch_in_ram(source) if method.alias? with_method_transaction do redefine source end else redefine source end end private def redefine(source) @@source_cache[cache_key] = source TOPLEVEL_BINDING.eval wrap(source), cache_key end def cache_key "pry-redefined(0x#{method.owner.object_id.to_s(16)}##{method.name})" end # Run some code ensuring that at the end target#meth_name will not have changed. # # When we're redefining aliased methods we will overwrite the method at the # unaliased name (so that super continues to work). By wrapping that code in a # transation we make that not happen, which means that alias_method_chains, etc. # continue to work. # def with_method_transaction temp_name = "__pry_#{method.original_name}__" method = self.method method.owner.class_eval do alias_method temp_name, method.original_name yield alias_method method.name, method.original_name alias_method method.original_name, temp_name end ensure begin method.send(:remove_method, temp_name) rescue StandardError nil end end # Update the definition line so that it can be eval'd directly on the Method's # owner instead of from the original context. # # In particular this takes `def self.foo` and turns it into `def foo` so that we # don't end up creating the method on the singleton class of the singleton class # by accident. # # This is necessarily done by String manipulation because we can't find out what # syntax is needed for the argument list by ruby-level introspection. # # @param [String] line The original definition line. e.g. def self.foo(bar, baz=1) # @return [String] The new definition line. e.g. def foo(bar, baz=1) def definition_for_owner(line) if line =~ /\Adef (?:.*?\.)?#{Regexp.escape(method.original_name)}(?=[\(\s;]|$)/ "def #{method.original_name}#{$'}" else raise CommandError, "Could not find original `def #{method.original_name}` line " \ "to patch." end end # Apply wrap_for_owner and wrap_for_nesting successively to `source` # @param [String] source # @return [String] The wrapped source. def wrap(source) wrap_for_nesting(wrap_for_owner(source)) end # Update the source code so that when it has the right owner when eval'd. # # This (combined with definition_for_owner) is backup for the case that # wrap_for_nesting fails, to ensure that the method will stil be defined in # the correct place. # # @param [String] source The source to wrap # @return [String] def wrap_for_owner(source) Pry.current[:pry_owner] = method.owner owner_source = definition_for_owner(source) visibility_fix = "#{method.visibility} #{method.name.to_sym.inspect}" "Pry.current[:pry_owner].class_eval do; #{owner_source}\n#{visibility_fix}\nend" end # Update the new source code to have the correct Module.nesting. # # This method uses syntactic analysis of the original source file to determine # the new nesting, so that we can tell the difference between: # # class A; def self.b; end; end # class << A; def b; end; end # # The resulting code should be evaluated in the TOPLEVEL_BINDING. # # @param [String] source The source to wrap. # @return [String] def wrap_for_nesting(source) nesting = Pry::Code.from_file(method.source_file).nesting_at(method.source_line) (nesting + [source] + nesting.map { "end" } + [""]).join(";") rescue Pry::Indent::UnparseableNestingError source end end end end pry-0.13.1/lib/pry/method/weird_method_locator.rb000066400000000000000000000163571364454223300220120ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Method # This class is responsible for locating the *real* `Pry::Method` # object captured by a binding. # # Given a `Binding` from inside a method and a 'seed' Pry::Method object, # there are primarily two situations where the seed method doesn't match # the Binding: # 1. The Pry::Method is from a subclass # 2. The Pry::Method represents a method of the same name while the original # was renamed to something else. For 1. we search vertically up the # inheritance chain, and for 2. we search laterally along the object's # method table. # # When we locate the method that matches the Binding we wrap it in # Pry::Method and return it, or return nil if we fail. class WeirdMethodLocator class << self # Whether the given method object matches the associated binding. # If the method object does not match the binding, then it's # most likely not the method captured by the binding, and we # must commence a search. # # @param [Pry::Method] method # @param [Binding] binding # @return [Boolean] def normal_method?(method, binding) if method && method.source_file && method.source_range if binding.respond_to?(:source_location) binding_file, binding_line = binding.source_location else binding_file = binding.eval('__FILE__') binding_line = binding.eval('__LINE__') end (File.expand_path(method.source_file) == File.expand_path(binding_file)) && method.source_range.include?(binding_line) end rescue StandardError false end def weird_method?(method, binding) !normal_method?(method, binding) end end attr_accessor :method attr_accessor :target # @param [Pry::Method] method The seed method. # @param [Binding] target The Binding that captures the method # we want to locate. def initialize(method, target) @method = method @target = target end # @return [Pry::Method, nil] The Pry::Method that matches the # given binding. def find_method find_method_in_superclass || find_renamed_method end # @return [Boolean] Whether the Pry::Method is unrecoverable # This usually happens when the method captured by the Binding # has been subsequently deleted. def lost_method? !!(find_method.nil? && renamed_method_source_location) end private def skip_superclass_search? target_mod = @target.eval('self').class target_mod.ancestors.take_while { |mod| mod != target_mod }.any? end def normal_method?(method) self.class.normal_method?(method, target) end def target_self target.eval('self') end def target_file file = if target.respond_to?(:source_location) target.source_location.first else target.eval('__FILE__') end pry_file? ? file : File.expand_path(file) end def target_line if target.respond_to?(:source_location) target.source_location.last else target.eval('__LINE__') end end def pry_file? file = if target.respond_to?(:source_location) target.source_location.first else target.eval('__FILE__') end Pry.eval_path == file end # it's possible in some cases that the method we find by this approach is # a sub-method of the one we're currently in, consider: # # class A; def b; binding.pry; end; end # class B < A; def b; super; end; end # # Given that we can normally find the source_range of methods, and that we # know which __FILE__ and __LINE__ the binding is at, we can hope to # disambiguate these cases. # # This obviously won't work if the source is unavaiable for some reason, # or if both methods have the same __FILE__ and __LINE__. # # @return [Pry::Method, nil] The Pry::Method representing the # superclass method. def find_method_in_superclass guess = method return guess if skip_superclass_search? while guess # needs rescue if this is a Disowned method or a C method or something... # TODO: Fix up the exception handling so we don't need a bare rescue return guess if normal_method?(guess) break if guess == guess.super guess = guess.super end # Uhoh... none of the methods in the chain had the right `__FILE__` and # `__LINE__` due to unknown circumstances. # TODO: we should warn the user when this happens. nil end # This is the case where the name of a method has changed # (via alias_method) so we locate the Method object for the # renamed method. # # @return [Pry::Method, nil] The Pry::Method representing the # renamed method def find_renamed_method return unless valid_file?(target_file) alias_name = all_methods_for(target_self).find do |v| location = target_self.method(v).source_location expanded_source_location(location) == renamed_method_source_location end alias_name && Pry::Method(target_self.method(alias_name)) end def expanded_source_location(source_location) return unless source_location if pry_file? source_location else [File.expand_path(source_location.first), source_location.last] end end # Use static analysis to locate the start of the method definition. # We have the `__FILE__` and `__LINE__` from the binding and the # original name of the method so we search up until we find a # def/define_method, etc defining a method of the appropriate name. # # @return [Array] The `source_location` of the # renamed method def renamed_method_source_location if defined?(@original_method_source_location) return @original_method_source_location end source_index = lines_for_file(target_file)[0..(target_line - 1)].rindex do |v| Pry::Method.method_definition?(method.name, v) end @original_method_source_location = source_index && [target_file, index_to_line_number(source_index)] end def index_to_line_number(index) # Pry.line_buffer is 0-indexed pry_file? ? index : index + 1 end def valid_file?(file) (File.exist?(file) && !File.directory?(file)) || Pry.eval_path == file end def lines_for_file(file) @lines_for_file ||= {} @lines_for_file[file] ||= if Pry.eval_path == file Pry.line_buffer else File.readlines(file) end end def all_methods_for(obj) obj.public_methods(false) + obj.private_methods(false) + obj.protected_methods(false) end end end end pry-0.13.1/lib/pry/object_path.rb000066400000000000000000000046411364454223300166100ustar00rootroot00000000000000# frozen_string_literal: true require 'strscan' class Pry # `ObjectPath` implements the resolution of "object paths", which are strings # that are similar to filesystem paths but meant for traversing Ruby objects. # Examples of valid object paths include: # # x # @foo/@bar # "string"/upcase # Pry/Method # # Object paths are mostly relevant in the context of the `cd` command. # @see https://github.com/pry/pry/wiki/State-navigation class ObjectPath SPECIAL_TERMS = ["", "::", ".", ".."].freeze # @param [String] path_string The object path expressed as a string. # @param [Array] current_stack The current state of the binding # stack. def initialize(path_string, current_stack) @path_string = path_string @current_stack = current_stack end # @return [Array] a new stack resulting from applying the given # path to the current stack. def resolve scanner = StringScanner.new(@path_string.strip) stack = @current_stack.dup loop do begin next_segment = "" loop do # Scan for as long as we don't see a slash next_segment += scanner.scan(%r{[^/]*}) if complete?(next_segment) || scanner.eos? scanner.getch # consume the slash break else next_segment += scanner.getch # append the slash end end case next_segment.chomp when "" stack = [stack.first] when "::" stack.push(TOPLEVEL_BINDING) when "." next when ".." stack.pop unless stack.size == 1 else stack.push(Pry.binding_for(stack.last.eval(next_segment))) end rescue RescuableException => e return handle_failure(next_segment, e) end break if scanner.eos? end stack end private def complete?(segment) SPECIAL_TERMS.include?(segment) || Pry::Code.complete_expression?(segment) end def handle_failure(context, err) msg = [ "Bad object path: #{@path_string.inspect}", "Failed trying to resolve: #{context.inspect}", "Exception: #{err.inspect}" ].join("\n") command_error = CommandError.new(msg) command_error.set_backtrace(err.backtrace) raise command_error end end end pry-0.13.1/lib/pry/output.rb000066400000000000000000000067011364454223300156650ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Output # @return [Array] default terminal screen size [rows, cols] DEFAULT_SIZE = [27, 80].freeze attr_reader :pry_instance def initialize(pry_instance) @output = pry_instance.config.output @color = pry_instance.config.color end def puts(*objs) return print "\n" if objs.empty? objs.each do |obj| if (ary = Array.try_convert(obj)) puts(*ary) else print "#{obj.to_s.chomp}\n" end end nil end def print(*objs) objs.each do |obj| @output.print decolorize_maybe(obj.to_s) end nil end alias << print alias write print def tty? @output.respond_to?(:tty?) && @output.tty? end def method_missing(method_name, *args, &block) if @output.respond_to?(method_name) @output.__send__(method_name, *args, &block) else super end end def respond_to_missing?(method_name, include_private = false) @output.respond_to?(method_name, include_private) end def decolorize_maybe(str) return str if @color Pry::Helpers::Text.strip_color(str) end # @return [Array] a pair of [rows, columns] which gives the size of # the window. If the window size cannot be determined, the default value. def size rows, cols = actual_screen_size return [rows.to_i, cols.to_i] if rows.to_i != 0 && cols.to_i != 0 DEFAULT_SIZE end # Return a screen width or the default if that fails. def width size.last end # Return a screen height or the default if that fails. def height size.first end private def actual_screen_size # The best way, if possible (requires non-jruby >=1.9 or io-console gem). io_console_size || # Fall back to the old standby, though it might be stale. env_size || # Fall further back, though this one is also out of date without # something calling Readline.set_screen_size. readline_size || # Windows users can otherwise run ansicon and get a decent answer. ansicon_env_size end def io_console_size return if Pry::Helpers::Platform.jruby? begin require 'io/console' begin @output.winsize if tty? && @output.respond_to?(:winsize) rescue Errno::EOPNOTSUPP # rubocop:disable Lint/HandleExceptions # Output is probably a socket, which doesn't support #winsize. end rescue LoadError # rubocop:disable Lint/HandleExceptions # They probably don't have the io/console stdlib or the io-console gem. # We'll keep trying. end end def env_size size = [Pry::Env['LINES'] || Pry::Env['ROWS'], Pry::Env['COLUMNS']] size if nonzero_column?(size) end def readline_size return unless defined?(Readline) && Readline.respond_to?(:get_screen_size) size = Readline.get_screen_size size if nonzero_column?(size) rescue Java::JavaLang::NullPointerException # This rescue won't happen on jrubies later than: # https://github.com/jruby/jruby/pull/436 nil end def ansicon_env_size return unless Pry::Env['ANSICON'] =~ /\((.*)x(.*)\)/ size = [Regexp.last_match(2), Regexp.last_match(1)] size if nonzero_column?(size) end def nonzero_column?(size) size[1].to_i > 0 end end end pry-0.13.1/lib/pry/pager.rb000066400000000000000000000143231364454223300154220ustar00rootroot00000000000000# frozen_string_literal: true # A pager is an `IO`-like object that accepts text and either prints it # immediately, prints it one page at a time, or streams it to an external # program to print one page at a time. class Pry class Pager class StopPaging < StandardError end attr_reader :pry_instance def initialize(pry_instance) @pry_instance = pry_instance end # Send the given text through the best available pager (if # `Pry.config.pager` is enabled). If you want to send text through in # chunks as you generate it, use `open` to get a writable object # instead. # # @param [String] text # Text to run through a pager. # def page(text) open do |pager| pager << text end end # Yields a pager object (`NullPager`, `SimplePager`, or `SystemPager`). # All pagers accept output with `#puts`, `#print`, `#write`, and `#<<`. def open pager = best_available yield pager rescue StopPaging # rubocop:disable Lint/HandleExceptions ensure pager.close if pager end private def enabled? !!@enabled end attr_reader :output # Return an instance of the "best" available pager class -- # `SystemPager` if possible, `SimplePager` if `SystemPager` isn't # available, and `NullPager` if the user has disabled paging. All # pagers accept output with `#puts`, `#print`, `#write`, and `#<<`. You # must call `#close` when you're done writing output to a pager, and # you must rescue `Pry::Pager::StopPaging`. These requirements can be # avoided by using `.open` instead. def best_available if !pry_instance.config.pager NullPager.new(pry_instance.output) elsif !SystemPager.available? || Helpers::Platform.jruby? SimplePager.new(pry_instance.output) else SystemPager.new(pry_instance.output) end end # `NullPager` is a "pager" that actually just prints all output as it # comes in. Used when `Pry.config.pager` is false. class NullPager def initialize(out) @out = out end def puts(str) print "#{str.chomp}\n" end def print(str) write str end alias << print def write(str) @out.write str end def close; end private def height @height ||= @out.height end def width @width ||= @out.width end end # `SimplePager` is a straightforward pure-Ruby pager. We use it on # JRuby and when we can't find a usable external pager. class SimplePager < NullPager def initialize(*) super @tracker = PageTracker.new(height - 3, width) end def write(str) str.lines.each do |line| @out.print line @tracker.record line next unless @tracker.page? @out.print "\n" @out.print "\e[0m" @out.print " --- Press enter to continue " \ "( q to break ) --- \n" raise StopPaging if Readline.readline("").chomp == "q" @tracker.reset end end end # `SystemPager` buffers output until we're pretty sure it's at least a # page long, then invokes an external pager and starts streaming output # to it. If `#close` is called before then, it just prints out the # buffered content. class SystemPager < NullPager def self.default_pager pager = Pry::Env['PAGER'] || '' # Default to less, and make sure less is being passed the correct # options pager = "less -R -F -X" if pager.strip.empty? || pager =~ /^less\b/ pager end @system_pager = nil def self.available? if @system_pager.nil? @system_pager = begin pager_executable = default_pager.split(' ').first if Helpers::Platform.windows? || Helpers::Platform.windows_ansi? `where /Q #{pager_executable}` else `which #{pager_executable}` end $CHILD_STATUS.success? rescue StandardError false end else @system_pager end end def initialize(*) super @tracker = PageTracker.new(height, width) @buffer = "" @pager = nil end def write(str) if invoked_pager? write_to_pager str else @tracker.record str @buffer += str write_to_pager @buffer if @tracker.page? end rescue Errno::EPIPE raise StopPaging end def close if invoked_pager? pager.close else @out.puts @buffer end end private def write_to_pager(text) pager.write @out.decolorize_maybe(text) end def invoked_pager? @pager end def pager @pager ||= IO.popen(self.class.default_pager, 'w') end end # `PageTracker` tracks output to determine whether it's likely to take # up a whole page. This doesn't need to be super precise, but we can # use it for `SimplePager` and to avoid invoking the system pager # unnecessarily. # # One simplifying assumption is that we don't need `#page?` to return # `true` on the basis of an incomplete line. Long lines should be # counted as multiple lines, but we don't have to transition from # `false` to `true` until we see a newline. class PageTracker def initialize(rows, cols) @rows = rows @cols = cols reset end def record(str) str.lines.each do |line| if line.end_with? "\n" @row += ((@col + line_length(line) - 1) / @cols) + 1 @col = 0 else @col += line_length(line) end end end def page? @row >= @rows end def reset @row = 0 @col = 0 end private # Approximation of the printable length of a given line, without the # newline and without ANSI color codes. def line_length(line) line.chomp.gsub(/\e\[[\d;]*m/, '').length end end end end pry-0.13.1/lib/pry/plugins.rb000066400000000000000000000071751364454223300160140ustar00rootroot00000000000000# frozen_string_literal: true require 'ostruct' class Pry class PluginManager PRY_PLUGIN_PREFIX = /^pry-/.freeze # Placeholder when no associated gem found, displays warning class NoPlugin def initialize(name) @name = name end def method_missing(*) warn "Warning: The plugin '#{@name}' was not found! (no gem found)" super end def respond_to_missing?(*) false end end class Plugin attr_accessor :name, :gem_name, :enabled, :spec, :active def initialize(name, gem_name, spec, enabled) @name = name @gem_name = gem_name @enabled = enabled @spec = spec end # Disable a plugin. (prevents plugin from being loaded, cannot # disable an already activated plugin) def disable! self.enabled = false end # Enable a plugin. (does not load it immediately but puts on # 'white list' to be loaded) def enable! self.enabled = true end # Load the Command line options defined by this plugin (if they exist) def load_cli_options cli_options_file = File.join(spec.full_gem_path, "lib/#{spec.name}/cli.rb") return unless File.exist?(cli_options_file) if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.4.4") cli_options_file = File.realpath(cli_options_file) end require cli_options_file end # Activate the plugin (require the gem - enables/loads the # plugin immediately at point of call, even if plugin is # disabled) # Does not reload plugin if it's already active. def activate! # Create the configuration object for the plugin. Pry.config.send("#{gem_name.tr('-', '_')}=", OpenStruct.new) begin require gem_name unless active? rescue LoadError => e warn "Found plugin #{gem_name}, but could not require '#{gem_name}'" warn e rescue StandardError => e warn "require '#{gem_name}' # Failed, saying: #{e}" end self.active = true self.enabled = true end alias active? active alias enabled? enabled def supported? pry_version = Gem::Version.new(VERSION) spec.dependencies.each do |dependency| if dependency.name == "pry" return dependency.requirement.satisfied_by?(pry_version) end end true end end def initialize @plugins = [] end # Find all installed Pry plugins and store them in an internal array. def locate_plugins gem_list.each do |gem| next if gem.name !~ PRY_PLUGIN_PREFIX plugin_name = gem.name.split('-', 2).last plugin = Plugin.new(plugin_name, gem.name, gem, false) @plugins << plugin.tap(&:enable!) if plugin.supported? && !plugin_located?(plugin) end @plugins end # @return [Hash] A hash with all plugin names (minus the 'pry-') as # keys and Plugin objects as values. def plugins h = Hash.new { |_, key| NoPlugin.new(key) } @plugins.each do |plugin| h[plugin.name] = plugin end h end # Require all enabled plugins, disabled plugins are skipped. def load_plugins @plugins.each do |plugin| plugin.activate! if plugin.enabled? end end private def plugin_located?(plugin) @plugins.any? { |existing| existing.gem_name == plugin.gem_name } end def gem_list Gem.refresh return Gem::Specification if Gem::Specification.respond_to?(:each) Gem.source_index.find_name('') end end end pry-0.13.1/lib/pry/prompt.rb000066400000000000000000000143411364454223300156450ustar00rootroot00000000000000# frozen_string_literal: true class Pry # Prompt represents the Pry prompt, which can be used with Readline-like # libraries. It defines a few default prompts (default prompt, simple prompt, # etc) and also provides an API for adding and implementing custom prompts. # # @example Registering a new Pry prompt # Pry::Prompt.add( # :ipython, # 'IPython-like prompt', [':', '...:'] # ) do |_context, _nesting, pry_instance, sep| # sep == ':' ? "In [#{pry_instance.input_ring.count}]: " : ' ...: ' # end # # # Produces: # # In [3]: def foo # # ...: puts 'foo' # # ...: end # # => :foo # # In [4]: # # @example Manually instantiating the Prompt class # prompt_procs = [ # proc { '#{rand(1)}>" }, # proc { "#{('a'..'z').to_a.sample}*" } # ] # prompt = Pry::Prompt.new( # :random, # 'Random number or letter prompt.', # prompt_procs # ) # prompt.wait_proc.call(...) #=> # prompt.incomplete_proc.call(...) # # @since v0.11.0 # @api public class Prompt # A Hash that holds all prompts. The keys of the Hash are prompt # names, the values are Hash instances of the format {:description, :value}. @prompts = {} class << self # Retrieves a prompt. # # @example # Prompt[:my_prompt] # # @param [Symbol] name The name of the prompt you want to access # @return [Hash{Symbol=>Object}] # @since v0.12.0 def [](name) @prompts[name.to_s] end # @return [Hash{Symbol=>Hash}] the duplicate of the internal prompts hash # @note Use this for read-only operations # @since v0.12.0 def all @prompts.dup end # Adds a new prompt to the prompt hash. # # @param [Symbol] name # @param [String] description # @param [Array] separators The separators to differentiate # between prompt modes (default mode and class/method definition mode). # The Array *must* have a size of 2. # @yield [context, nesting, pry_instance, sep] # @yieldparam context [Object] the context where Pry is currently in # @yieldparam nesting [Integer] whether the context is nested # @yieldparam pry_instance [Pry] the Pry instance # @yieldparam separator [String] separator string # @return [nil] # @raise [ArgumentError] if the size of `separators` is not 2 # @raise [ArgumentError] if `prompt_name` is already occupied # @since v0.12.0 def add(name, description = '', separators = %w[> *]) name = name.to_s unless separators.size == 2 raise ArgumentError, "separators size must be 2, given #{separators.size}" end if @prompts.key?(name) raise ArgumentError, "the '#{name}' prompt was already added" end @prompts[name] = new( name, description, separators.map do |sep| proc do |context, nesting, pry_instance| yield(context, nesting, pry_instance, sep) end end ) nil end end # @return [String] attr_reader :name # @return [String] attr_reader :description # @return [Array] the array of procs that hold # `[wait_proc, incomplete_proc]` attr_reader :prompt_procs # @param [String] name # @param [String] description # @param [Array] prompt_procs def initialize(name, description, prompt_procs) @name = name @description = description @prompt_procs = prompt_procs end # @return [Proc] the proc which builds the wait prompt (`>`) def wait_proc @prompt_procs.first end # @return [Proc] the proc which builds the prompt when in the middle of an # expression such as open method, etc. (`*`) def incomplete_proc @prompt_procs.last end # @deprecated Use a `Pry::Prompt` instance directly def [](key) key = key.to_s if %w[name description].include?(key) Pry::Warning.warn( "`Pry::Prompt[:#{@name}][:#{key}]` is deprecated. " \ "Use `#{self.class}##{key}` instead" ) public_send(key) elsif key.to_s == 'value' Pry::Warning.warn( "`#{self.class}[:#{@name}][:value]` is deprecated. Use " \ "`#{self.class}#prompt_procs` instead or an instance of " \ "`#{self.class}` directly" ) @prompt_procs end end add( :default, "The default Pry prompt. Includes information about the current expression \n" \ "number, evaluation context, and nesting level, plus a reminder that you're \n" \ 'using Pry.' ) do |context, nesting, pry_instance, sep| format( "[%s] %s(%s)%s%s ", in_count: pry_instance.input_ring.count, name: pry_instance.config.prompt_name, context: Pry.view_clip(context), nesting: (nesting > 0 ? ":#{nesting}" : ''), separator: sep ) end add( :simple, "A simple `>>`.", ['>> ', ' | '] ) do |_, _, _, sep| sep end add( :nav, "A prompt that displays the binding stack as a path and includes information \n" \ "about #{Helpers::Text.bold('_in_')} and #{Helpers::Text.bold('_out_')}.", %w[> *] ) do |_context, _nesting, pry_instance, sep| tree = pry_instance.binding_stack.map { |b| Pry.view_clip(b.eval('self')) } format( "[%s] (%s) %s: %s%s ", in_count: pry_instance.input_ring.count, name: pry_instance.config.prompt_name, tree: tree.join(' / '), stack_size: pry_instance.binding_stack.size - 1, separator: sep ) end add( :shell, 'A prompt that displays `$PWD` as you change it.', %w[$ *] ) do |context, _nesting, pry_instance, sep| format( "%s %s:%s %s ", name: pry_instance.config.prompt_name, context: Pry.view_clip(context), pwd: Dir.pwd, separator: sep ) end add( :none, 'Wave goodbye to the Pry prompt.', Array.new(2) ) { '' } end end pry-0.13.1/lib/pry/pry_class.rb000066400000000000000000000270021364454223300163210ustar00rootroot00000000000000# frozen_string_literal: true require 'stringio' require 'pathname' class Pry LOCAL_RC_FILE = "./.pryrc".freeze # @return [Boolean] true if this Ruby supports safe levels and tainting, # to guard against using deprecated or unsupported features HAS_SAFE_LEVEL = ( RUBY_ENGINE == 'ruby' && Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.7') ) class << self extend Pry::Forwardable attr_accessor :custom_completions attr_accessor :current_line attr_accessor :line_buffer attr_accessor :eval_path attr_accessor :cli attr_accessor :quiet attr_accessor :last_internal_error attr_accessor :config def_delegators :@plugin_manager, :plugins, :load_plugins, :locate_plugins def_delegators( :@config, :input, :input=, :output, :output=, :commands, :commands=, :print, :print=, :exception_handler, :exception_handler=, :hooks, :hooks=, :color, :color=, :pager, :pager=, :editor, :editor=, :memory_size, :memory_size=, :extra_sticky_locals, :extra_sticky_locals=, :prompt, :prompt=, :history, :history= ) # # @example # Pry.configure do |config| # config.eager_load! # optional # config.input = # .. # config.foo = 2 # end # # @yield [config] # Yields a block with {Pry.config} as its argument. # def configure yield config end end # # @return [main] # returns the special instance of Object, "main". # def self.main @main ||= TOPLEVEL_BINDING.eval "self" end # # @return [Pry::Config] # Returns a value store for an instance of Pry running on the current thread. # def self.current Thread.current[:__pry__] ||= {} end # Load the given file in the context of `Pry.toplevel_binding` # @param [String] file The unexpanded file path. def self.load_file_at_toplevel(file) toplevel_binding.eval(File.read(file), file) rescue RescuableException => e puts "Error loading #{file}: #{e}\n#{e.backtrace.first}" end # Load RC files if appropriate This method can also be used to reload the # files if they have changed. def self.load_rc_files rc_files_to_load.each do |file| critical_section do load_file_at_toplevel(file) end end end # Load the local RC file (./.pryrc) def self.rc_files_to_load files = [] files << Pry.config.rc_file if Pry.config.should_load_rc files << LOCAL_RC_FILE if Pry.config.should_load_local_rc files.map { |file| real_path_to(file) }.compact.uniq end # Expand a file to its canonical name (following symlinks as appropriate) def self.real_path_to(file) Pathname.new(File.expand_path(file)).realpath.to_s rescue Errno::ENOENT, Errno::EACCES nil end # Load any Ruby files specified with the -r flag on the command line. def self.load_requires Pry.config.requires.each do |file| require file end end # Trap interrupts on jruby, and make them behave like MRI so we can # catch them. def self.load_traps trap('INT') { raise Interrupt } end def self.load_win32console require 'win32console' # The mswin and mingw versions of pry require win32console, so this should # only fail on jruby (where win32console doesn't work). # Instead we'll recommend ansicon, which does. rescue LoadError warn <<-WARNING if Pry.config.windows_console_warning For a better Pry experience on Windows, please use ansicon: https://github.com/adoxa/ansicon If you use an alternative to ansicon and don't want to see this warning again, you can add "Pry.config.windows_console_warning = false" to your pryrc. WARNING end # Do basic setup for initial session including: loading pryrc, plugins, # requires, and history. def self.initial_session_setup return unless initial_session? @initial_session = false # note these have to be loaded here rather than in _pry_ as # we only want them loaded once per entire Pry lifetime. load_rc_files end def self.final_session_setup return if @session_finalized @session_finalized = true load_plugins if Pry.config.should_load_plugins load_requires if Pry.config.should_load_requires load_history if Pry.config.history_load load_traps if Pry.config.should_trap_interrupts load_win32console if Helpers::Platform.windows? && !Helpers::Platform.windows_ansi? end # Start a Pry REPL. # This method also loads `pryrc` as necessary the first time it is invoked. # @param [Object, Binding] target The receiver of the Pry session # @param [Hash] options # @option options (see Pry#initialize) # @example # Pry.start(Object.new, :input => MyInput.new) def self.start(target = nil, options = {}) return if Pry::Env['DISABLE_PRY'] if Pry::Env['FAIL_PRY'] raise 'You have FAIL_PRY set to true, which results in Pry calls failing' end options = options.to_hash if in_critical_section? output.puts "ERROR: Pry started inside Pry." output.puts "This can happen if you have a binding.pry inside a #to_s " \ "or #inspect function." return end options[:target] = Pry.binding_for(target || toplevel_binding) initial_session_setup final_session_setup # Unless we were given a backtrace, save the current one if options[:backtrace].nil? options[:backtrace] = caller # If Pry was started via `binding.pry`, elide that from the backtrace if options[:backtrace].first =~ /pry.*core_extensions.*pry/ options[:backtrace].shift end end driver = options[:driver] || Pry::REPL # Enter the matrix driver.start(options) rescue Pry::TooSafeException puts "ERROR: Pry cannot work with $SAFE > 0" raise end # Execute the file through the REPL loop, non-interactively. # @param [String] file_name File name to load through the REPL. def self.load_file_through_repl(file_name) REPLFileLoader.new(file_name).load end # # An inspector that clips the output to `max_length` chars. # In case of > `max_length` chars the `# notation is used. # # @param [Object] obj # The object to view. # # @param [Hash] options # @option options [Integer] :max_length (60) # The maximum number of chars before clipping occurs. # # @option options [Boolean] :id (false) # Boolean to indicate whether or not a hex reprsentation of the object ID # is attached to the return value when the length of inspect is greater than # value of `:max_length`. # # @return [String] # The string representation of `obj`. # def self.view_clip(obj, options = {}) max = options.fetch :max_length, 60 id = options.fetch :id, false if obj.is_a?(Module) && obj.name.to_s != "" && obj.name.to_s.length <= max obj.name.to_s elsif Pry.main == obj # Special-case to support jruby. Fixed as of: # https://github.com/jruby/jruby/commit/d365ebd309cf9df3dde28f5eb36ea97056e0c039 # we can drop in the future. obj.to_s # rubocop:disable Style/CaseEquality elsif Pry.config.prompt_safe_contexts.any? { |v| v === obj } && obj.inspect.length <= max # rubocop:enable Style/CaseEquality obj.inspect elsif id format("#<#{obj.class}:0x%x>", id: obj.object_id << 1) else "#<#{obj.class}>" end rescue RescuableException "unknown" end # Load Readline history if required. def self.load_history Pry.history.load end # @return [Boolean] Whether this is the first time a Pry session has # been started since loading the Pry class. def self.initial_session? @initial_session end # Run a Pry command from outside a session. The commands available are # those referenced by `Pry.config.commands` (the default command set). # @param [String] command_string The Pry command (including arguments, # if any). # @param [Hash] options Optional named parameters. # @return [nil] # @option options [Object, Binding] :target The object to run the # command under. Defaults to `TOPLEVEL_BINDING` (main). # @option options [Boolean] :show_output Whether to show command # output. Defaults to true. # @example Run at top-level with no output. # Pry.run_command "ls" # @example Run under Pry class, returning only public methods. # Pry.run_command "ls -m", :target => Pry # @example Display command output. # Pry.run_command "ls -av", :show_output => true def self.run_command(command_string, options = {}) options = { target: TOPLEVEL_BINDING, show_output: true, output: Pry.config.output, commands: Pry.config.commands }.merge!(options) # :context for compatibility with <= 0.9.11.4 target = options[:context] || options[:target] output = options[:show_output] ? options[:output] : StringIO.new pry = Pry.new(output: output, target: target, commands: options[:commands]) pry.eval command_string nil end def self.auto_resize! Pry.config.input # by default, load Readline if !defined?(Readline) || Pry.config.input != Readline warn "Sorry, you must be using Readline for Pry.auto_resize! to work." return end if Readline::VERSION =~ /edit/i warn(<<-WARN) Readline version #{Readline::VERSION} detected - will not auto_resize! correctly. For the fix, use GNU Readline instead: https://github.com/guard/guard/wiki/Add-Readline-support-to-Ruby-on-Mac-OS-X WARN return end trap :WINCH do begin Readline.set_screen_size(*output.size) rescue StandardError => e warn "\nPry.auto_resize!'s Readline.set_screen_size failed: #{e}" end begin Readline.refresh_line rescue StandardError => e warn "\nPry.auto_resize!'s Readline.refresh_line failed: #{e}" end end end # Set all the configurable options back to their default values def self.reset_defaults @initial_session = true @session_finalized = nil self.config = Pry::Config.new self.cli = false self.current_line = 1 self.line_buffer = [""] self.eval_path = "(pry)" end # Basic initialization. def self.init @plugin_manager ||= PluginManager.new reset_defaults locate_plugins end # Return a `Binding` object for `target` or return `target` if it is # already a `Binding`. # In the case where `target` is top-level then return `TOPLEVEL_BINDING` # @param [Object] target The object to get a `Binding` object for. # @return [Binding] The `Binding` object. def self.binding_for(target) return target if Binding === target # rubocop:disable Style/CaseEquality return TOPLEVEL_BINDING if Pry.main == target target.__binding__ end def self.toplevel_binding unless defined?(@toplevel_binding) && @toplevel_binding # Grab a copy of the TOPLEVEL_BINDING without any local variables. # This binding has a default definee of Object, and new methods are # private (just as in TOPLEVEL_BINDING). TOPLEVEL_BINDING.eval <<-RUBY def self.__pry__ binding end Pry.toplevel_binding = __pry__ class << self; undef __pry__; end RUBY end @toplevel_binding.eval('private') @toplevel_binding end class << self attr_writer :toplevel_binding end def self.in_critical_section? Thread.current[:pry_critical_section] ||= 0 Thread.current[:pry_critical_section] > 0 end def self.critical_section Thread.current[:pry_critical_section] ||= 0 Thread.current[:pry_critical_section] += 1 yield ensure Thread.current[:pry_critical_section] -= 1 end end Pry.init pry-0.13.1/lib/pry/pry_instance.rb000066400000000000000000000521641364454223300170270ustar00rootroot00000000000000# frozen_string_literal: true require 'method_source' require 'ostruct' ## # Pry is a powerful alternative to the standard IRB shell for Ruby. It # features syntax highlighting, a flexible plugin architecture, runtime # invocation and source and documentation browsing. # # Pry can be started similar to other command line utilities by simply running # the following command: # # pry # # Once inside Pry you can invoke the help message: # # help # # This will show a list of available commands and their usage. For more # information about Pry you can refer to the following resources: # # * http://pryrepl.org/ # * https://github.com/pry/pry # * the IRC channel, which is #pry on the Freenode network # # rubocop:disable Metrics/ClassLength class Pry extend Pry::Forwardable attr_accessor :binding_stack attr_accessor :custom_completions attr_accessor :eval_string attr_accessor :backtrace attr_accessor :suppress_output attr_accessor :last_result attr_accessor :last_file attr_accessor :last_dir attr_reader :last_exception attr_reader :exit_value # @since v0.12.0 attr_reader :input_ring # @since v0.12.0 attr_reader :output_ring attr_reader :config def_delegators( :@config, :input, :input=, :output, :output=, :commands, :commands=, :print, :print=, :exception_handler, :exception_handler=, :hooks, :hooks=, :color, :color=, :pager, :pager=, :editor, :editor=, :memory_size, :memory_size=, :extra_sticky_locals, :extra_sticky_locals= ) EMPTY_COMPLETIONS = [].freeze # Create a new {Pry} instance. # @param [Hash] options # @option options [#readline] :input # The object to use for input. # @option options [#puts] :output # The object to use for output. # @option options [Pry::CommandBase] :commands # The object to use for commands. # @option options [Hash] :hooks # The defined hook Procs. # @option options [Pry::Prompt] :prompt # The array of Procs to use for prompts. # @option options [Proc] :print # The Proc to use for printing return values. # @option options [Boolean] :quiet # Omit the `whereami` banner when starting. # @option options [Array] :backtrace # The backtrace of the session's `binding.pry` line, if applicable. # @option options [Object] :target # The initial context for this session. def initialize(options = {}) @binding_stack = [] @indent = Pry::Indent.new(self) @eval_string = ''.dup @backtrace = options.delete(:backtrace) || caller target = options.delete(:target) @config = self.class.config.merge(options) push_prompt(config.prompt) @input_ring = Pry::Ring.new(config.memory_size) @output_ring = Pry::Ring.new(config.memory_size) @custom_completions = config.command_completions set_last_result nil @input_ring << nil push_initial_binding(target) exec_hook(:when_started, target, options, self) @prompt_warn = false end # This is the prompt at the top of the prompt stack. # @return [Pry::Prompt] the current prompt def prompt prompt_stack.last end # Sets the Pry prompt. # @param [Pry::Prompt] new_prompt # @return [void] def prompt=(new_prompt) if prompt_stack.empty? push_prompt new_prompt else prompt_stack[-1] = new_prompt end end # Initialize this instance by pushing its initial context into the binding # stack. If no target is given, start at the top level. def push_initial_binding(target = nil) push_binding(target || Pry.toplevel_binding) end # The currently active `Binding`. # @return [Binding] The currently active `Binding` for the session. def current_binding binding_stack.last end alias current_context current_binding # support previous API # Push a binding for the given object onto the stack. If this instance is # currently stopped, mark it as usable again. def push_binding(object) @stopped = false binding_stack << Pry.binding_for(object) end # # Generate completions. # # @param [String] str # What the user has typed so far # # @return [Array] # Possible completions # def complete(str) return EMPTY_COMPLETIONS unless config.completer Pry.critical_section do completer = config.completer.new(config.input, self) completer.call( str, target: current_binding, custom_completions: custom_completions.call.push(*sticky_locals.keys) ) end end # # Injects a local variable into the provided binding. # # @param [String] name # The name of the local to inject. # # @param [Object] value # The value to set the local to. # # @param [Binding] binding # The binding to set the local on. # # @return [Object] # The value the local was set to. # def inject_local(name, value, binding) value = value.is_a?(Proc) ? value.call : value if binding.respond_to?(:local_variable_set) binding.local_variable_set name, value else # < 2.1 begin Pry.current[:pry_local] = value binding.eval "#{name} = ::Pry.current[:pry_local]" ensure Pry.current[:pry_local] = nil end end end undef :memory_size if method_defined? :memory_size # @return [Integer] The maximum amount of objects remembered by the inp and # out arrays. Defaults to 100. def memory_size @output_ring.max_size end undef :memory_size= if method_defined? :memory_size= def memory_size=(size) @input_ring = Pry::Ring.new(size) @output_ring = Pry::Ring.new(size) end # Inject all the sticky locals into the current binding. def inject_sticky_locals! sticky_locals.each_pair do |name, value| inject_local(name, value, current_binding) end end # Add a sticky local to this Pry instance. # A sticky local is a local that persists between all bindings in a session. # @param [Symbol] name The name of the sticky local. # @yield The block that defines the content of the local. The local # will be refreshed at each tick of the repl loop. def add_sticky_local(name, &block) config.extra_sticky_locals[name] = block end def sticky_locals { _in_: input_ring, _out_: output_ring, pry_instance: self, _ex_: last_exception && last_exception.wrapped_exception, _file_: last_file, _dir_: last_dir, _: proc { last_result }, __: proc { output_ring[-2] } }.merge(config.extra_sticky_locals) end # Reset the current eval string. If the user has entered part of a multiline # expression, this discards that input. def reset_eval_string @eval_string = ''.dup end # Pass a line of input to Pry. # # This is the equivalent of `Binding#eval` but with extra Pry! # # In particular: # 1. Pry commands will be executed immediately if the line matches. # 2. Partial lines of input will be queued up until a complete expression has # been accepted. # 3. Output is written to `#output` in pretty colours, not returned. # # Once this method has raised an exception or returned false, this instance # is no longer usable. {#exit_value} will return the session's breakout # value if applicable. # # @param [String?] line The line of input; `nil` if the user types `` # @option options [Boolean] :generated Whether this line was generated automatically. # Generated lines are not stored in history. # @return [Boolean] Is Pry ready to accept more input? # @raise [Exception] If the user uses the `raise-up` command, this method # will raise that exception. def eval(line, options = {}) return false if @stopped exit_value = nil exception = catch(:raise_up) do exit_value = catch(:breakout) do handle_line(line, options) # We use 'return !@stopped' here instead of 'return true' so that if # handle_line has stopped this pry instance (e.g. by opening pry_instance.repl and # then popping all the bindings) we still exit immediately. return !@stopped end exception = false end @stopped = true @exit_value = exit_value # TODO: make this configurable? raise exception if exception false end # Potentially deprecated. Use `Pry::REPL.new(pry, :target => target).start` # (If nested sessions are going to exist, this method is fine, but a goal is # to come up with an alternative to nested sessions altogether.) def repl(target = nil) Pry::REPL.new(self, target: target).start end def evaluate_ruby(code) inject_sticky_locals! exec_hook :before_eval, code, self result = current_binding.eval(code, Pry.eval_path, Pry.current_line) set_last_result(result, code) ensure update_input_history(code) exec_hook :after_eval, result, self end # Output the result or pass to an exception handler (if result is an exception). def show_result(result) if last_result_is_exception? exception_handler.call(output, result, self) elsif should_print? print.call(output, result, self) end rescue RescuableException => e # Being uber-paranoid here, given that this exception arose because we couldn't # serialize something in the user's program, let's not assume we can serialize # the exception either. begin output.puts "(pry) output error: #{e.inspect}\n#{e.backtrace.join("\n")}" rescue RescuableException if last_result_is_exception? output.puts "(pry) output error: failed to show exception" else output.puts "(pry) output error: failed to show result" end end ensure output.flush if output.respond_to?(:flush) end # If the given line is a valid command, process it in the context of the # current `eval_string` and binding. # @param [String] val The line to process. # @return [Boolean] `true` if `val` is a command, `false` otherwise def process_command(val) val = val.lstrip if /^\s\S/ !~ val val = val.chomp result = commands.process_line( val, target: current_binding, output: output, eval_string: @eval_string, pry_instance: self, hooks: hooks ) # set a temporary (just so we can inject the value we want into eval_string) Pry.current[:pry_cmd_result] = result # note that `result` wraps the result of command processing; if a # command was matched and invoked then `result.command?` returns true, # otherwise it returns false. if result.command? unless result.void_command? # the command that was invoked was non-void (had a return value) and so we make # the value of the current expression equal to the return value # of the command. @eval_string = "::Pry.current[:pry_cmd_result].retval\n" end true else false end end # Same as process_command, but outputs exceptions to `#output` instead of # raising. # @param [String] val The line to process. # @return [Boolean] `true` if `val` is a command, `false` otherwise def process_command_safely(val) process_command(val) rescue CommandError, Pry::Slop::InvalidOptionError, MethodSource::SourceNotFoundError => e Pry.last_internal_error = e output.puts "Error: #{e.message}" true end # Run the specified command. # @param [String] val The command (and its params) to execute. # @return [Pry::Command::VOID_VALUE] # @example # pry_instance.run_command("ls -m") def run_command(val) commands.process_line( val, eval_string: @eval_string, target: current_binding, pry_instance: self, output: output ) Pry::Command::VOID_VALUE end # Execute the specified hook. # @param [Symbol] name The hook name to execute # @param [*Object] args The arguments to pass to the hook # @return [Object, Exception] The return value of the hook or the exception raised # # If executing a hook raises an exception, we log that and then continue sucessfully. # To debug such errors, use the global variable $pry_hook_error, which is set as a # result. def exec_hook(name, *args, &block) e_before = hooks.errors.size hooks.exec_hook(name, *args, &block).tap do hooks.errors[e_before..-1].each do |e| output.puts "#{name} hook failed: #{e.class}: #{e.message}" output.puts e.backtrace.first.to_s output.puts "(see pry_instance.hooks.errors to debug)" end end end # Set the last result of an eval. # This method should not need to be invoked directly. # @param [Object] result The result. # @param [String] code The code that was run. def set_last_result(result, code = "") @last_result_is_exception = false @output_ring << result self.last_result = result unless code =~ /\A\s*\z/ end # Set the last exception for a session. # @param [Exception] exception The last exception. def last_exception=(exception) @last_result_is_exception = true last_exception = Pry::LastException.new(exception) @output_ring << last_exception @last_exception = last_exception end # Update Pry's internal state after evalling code. # This method should not need to be invoked directly. # @param [String] code The code we just eval'd def update_input_history(code) # Always push to the @input_ring as the @output_ring is always pushed to. @input_ring << code return unless code Pry.line_buffer.push(*code.each_line) Pry.current_line += code.lines.count end # @return [Boolean] True if the last result is an exception that was raised, # as opposed to simply an instance of Exception (like the result of # Exception.new) def last_result_is_exception? @last_result_is_exception end # Whether the print proc should be invoked. # Currently only invoked if the output is not suppressed. # @return [Boolean] Whether the print proc should be invoked. def should_print? !@suppress_output end # Returns the appropriate prompt to use. # @return [String] The prompt. def select_prompt object = current_binding.eval('self') open_token = @indent.open_delimiters.last || @indent.stack.last c = OpenStruct.new( object: object, nesting_level: binding_stack.size - 1, open_token: open_token, session_line: Pry.history.session_line_count + 1, history_line: Pry.history.history_line_count + 1, expr_number: input_ring.count, pry_instance: self, binding_stack: binding_stack, input_ring: input_ring, eval_string: @eval_string, cont: !@eval_string.empty? ) Pry.critical_section do # If input buffer is empty, then use normal prompt. Otherwise use the wait # prompt (indicating multi-line expression). if prompt.is_a?(Pry::Prompt) prompt_proc = eval_string.empty? ? prompt.wait_proc : prompt.incomplete_proc return prompt_proc.call(c.object, c.nesting_level, c.pry_instance) end unless @prompt_warn @prompt_warn = true Kernel.warn( "warning: setting prompt with help of " \ "`Pry.config.prompt = [proc {}, proc {}]` is deprecated. " \ "Use Pry::Prompt API instead" ) end # If input buffer is empty then use normal prompt if eval_string.empty? generate_prompt(Array(prompt).first, c) # Otherwise use the wait prompt (indicating multi-line expression) else generate_prompt(Array(prompt).last, c) end end end # Pushes the current prompt onto a stack that it can be restored from later. # Use this if you wish to temporarily change the prompt. # # @example # push_prompt(Pry::Prompt[:my_prompt]) # # @param [Pry::Prompt] new_prompt # @return [Pry::Prompt] new_prompt def push_prompt(new_prompt) prompt_stack.push new_prompt end # Pops the current prompt off of the prompt stack. If the prompt you are # popping is the last prompt, it will not be popped. Use this to restore the # previous prompt. # # @example # pry = Pry.new(prompt: Pry::Prompt[:my_prompt1]) # pry.push_prompt(Pry::Prompt[:my_prompt2]) # pry.pop_prompt # => prompt2 # pry.pop_prompt # => prompt1 # pry.pop_prompt # => prompt1 # # @return [Pry::Prompt] the prompt being popped def pop_prompt prompt_stack.size > 1 ? prompt_stack.pop : prompt end undef :pager if method_defined? :pager # Returns the currently configured pager # @example # pry_instance.pager.page text def pager Pry::Pager.new(self) end undef :output if method_defined? :output # Returns an output device # @example # pry_instance.output.puts "ohai!" def output Pry::Output.new(self) end # Raise an exception out of Pry. # # See Kernel#raise for documentation of parameters. # See rb_make_exception for the inbuilt implementation. # # This is necessary so that the raise-up command can tell the # difference between an exception the user has decided to raise, # and a mistake in specifying that exception. # # (i.e. raise-up RunThymeError.new should not be the same as # raise-up NameError, "unititialized constant RunThymeError") # def raise_up_common(force, *args) exception = if args == [] last_exception || RuntimeError.new elsif args.length == 1 && args.first.is_a?(String) RuntimeError.new(args.first) elsif args.length > 3 raise ArgumentError, "wrong number of arguments" elsif !args.first.respond_to?(:exception) raise TypeError, "exception class/object expected" elsif args.size == 1 args.first.exception else args.first.exception(args[1]) end raise TypeError, "exception object expected" unless exception.is_a? Exception exception.set_backtrace(args.size == 3 ? args[2] : caller(1)) if force || binding_stack.one? binding_stack.clear throw :raise_up, exception else binding_stack.pop raise exception end end def raise_up(*args) raise_up_common(false, *args) end def raise_up!(*args) raise_up_common(true, *args) end # Convenience accessor for the `quiet` config key. # @return [Boolean] def quiet? config.quiet end private def handle_line(line, options) if line.nil? config.control_d_handler.call(self) return end ensure_correct_encoding!(line) Pry.history << line unless options[:generated] @suppress_output = false inject_sticky_locals! begin unless process_command_safely(line) @eval_string += "#{line.chomp}\n" if !line.empty? || !@eval_string.empty? end rescue RescuableException => e self.last_exception = e result = e Pry.critical_section do show_result(result) end return end # This hook is supposed to be executed after each line of ruby code # has been read (regardless of whether eval_string is yet a complete expression) exec_hook :after_read, eval_string, self begin complete_expr = Pry::Code.complete_expression?(@eval_string) rescue SyntaxError => e output.puts e.message.gsub(/^.*syntax error, */, "SyntaxError: ") reset_eval_string end if complete_expr if @eval_string =~ /;\Z/ || @eval_string.empty? || @eval_string =~ /\A *#.*\n\z/ @suppress_output = true end # A bug in jruby makes java.lang.Exception not rescued by # `rescue Pry::RescuableException` clause. # # * https://github.com/pry/pry/issues/854 # * https://jira.codehaus.org/browse/JRUBY-7100 # # Until that gets fixed upstream, treat java.lang.Exception # as an additional exception to be rescued explicitly. # # This workaround has a side effect: java exceptions specified # in `Pry.config.unrescued_exceptions` are ignored. jruby_exceptions = [] jruby_exceptions << Java::JavaLang::Exception if Helpers::Platform.jruby? begin # Reset eval string, in case we're evaluating Ruby that does something # like open a nested REPL on this instance. eval_string = @eval_string reset_eval_string result = evaluate_ruby(eval_string) rescue RescuableException, *jruby_exceptions => e # Eliminate following warning: # warning: singleton on non-persistent Java type X # (http://wiki.jruby.org/Persistence) if Helpers::Platform.jruby? && e.class.respond_to?('__persistent__') e.class.__persistent__ = true end self.last_exception = e result = e end Pry.critical_section do show_result(result) end end throw(:breakout) if current_binding.nil? end # Force `eval_string` into the encoding of `val`. [Issue #284] def ensure_correct_encoding!(val) if @eval_string.empty? && val.respond_to?(:encoding) && val.encoding != @eval_string.encoding @eval_string.force_encoding(val.encoding) end end def generate_prompt(prompt_proc, conf) if prompt_proc.arity == 1 prompt_proc.call(conf) else prompt_proc.call(conf.object, conf.nesting_level, conf.pry_instance) end end # the array that the prompt stack is stored in def prompt_stack @prompt_stack ||= [] end end # rubocop:enable Metrics/ClassLength pry-0.13.1/lib/pry/repl.rb000066400000000000000000000177221364454223300152740ustar00rootroot00000000000000# frozen_string_literal: true class Pry class REPL extend Pry::Forwardable def_delegators :@pry, :input, :output # @return [Pry] The instance of {Pry} that the user is controlling. attr_accessor :pry # Instantiate a new {Pry} instance with the given options, then start a # {REPL} instance wrapping it. # @option options See {Pry#initialize} def self.start(options) new(Pry.new(options)).start end # Create an instance of {REPL} wrapping the given {Pry}. # @param [Pry] pry The instance of {Pry} that this {REPL} will control. # @param [Hash] options Options for this {REPL} instance. # @option options [Object] :target The initial target of the session. def initialize(pry, options = {}) @pry = pry @indent = Pry::Indent.new(pry) @readline_output = nil @pry.push_binding options[:target] if options[:target] end # Start the read-eval-print loop. # @return [Object?] If the session throws `:breakout`, return the value # thrown with it. # @raise [Exception] If the session throws `:raise_up`, raise the exception # thrown with it. def start prologue Pry::InputLock.for(:all).with_ownership { repl } ensure epilogue end private # Set up the repl session. # @return [void] def prologue pry.exec_hook :before_session, pry.output, pry.current_binding, pry return unless pry.config.correct_indent # Clear the line before starting Pry. This fixes issue #566. output.print(Helpers::Platform.windows_ansi? ? "\e[0F" : "\e[0G") end # The actual read-eval-print loop. # # The {REPL} instance is responsible for reading and looping, whereas the # {Pry} instance is responsible for evaluating user input and printing # return values and command output. # # @return [Object?] If the session throws `:breakout`, return the value # thrown with it. # @raise [Exception] If the session throws `:raise_up`, raise the exception # thrown with it. def repl loop do case val = read when :control_c output.puts "" pry.reset_eval_string when :no_more_input output.puts "" if output.tty? break else output.puts "" if val.nil? && output.tty? return pry.exit_value unless pry.eval(val) end end end # Clean up after the repl session. # @return [void] def epilogue pry.exec_hook :after_session, pry.output, pry.current_binding, pry end # Read a line of input from the user. # @return [String] The line entered by the user. # @return [nil] On ``. # @return [:control_c] On ``. # @return [:no_more_input] On EOF. def read @indent.reset if pry.eval_string.empty? current_prompt = pry.select_prompt indentation = pry.config.auto_indent ? @indent.current_prefix : '' val = read_line("#{current_prompt}#{indentation}") # Return nil for EOF, :no_more_input for error, or :control_c for return val unless val.is_a?(String) if pry.config.auto_indent original_val = "#{indentation}#{val}" indented_val = @indent.indent(val) if output.tty? && pry.config.correct_indent && Pry::Helpers::BaseHelpers.use_ansi_codes? output.print @indent.correct_indentation( current_prompt, indented_val, calculate_overhang(current_prompt, original_val, indented_val) ) output.flush end else indented_val = val end indented_val end # Manage switching of input objects on encountering `EOFError`s. # @return [Object] Whatever the given block returns. # @return [:no_more_input] Indicates that no more input can be read. def handle_read_errors should_retry = true exception_count = 0 begin yield rescue EOFError pry.config.input = Pry.config.input unless should_retry output.puts "Error: Pry ran out of things to read from! " \ "Attempting to break out of REPL." return :no_more_input end should_retry = false retry # Handle like Bash: empty the current input buffer, but don't # quit. This is only for MRI 1.9; other versions of Ruby don't let you # send Interrupt from within Readline. rescue Interrupt return :control_c # If we get a random error when trying to read a line we don't want to # automatically retry, as the user will see a lot of error messages # scroll past and be unable to do anything about it. rescue RescuableException => e puts "Error: #{e.message}" output.puts e.backtrace exception_count += 1 retry if exception_count < 5 puts "FATAL: Pry failed to get user input using `#{input}`." puts "To fix this you may be able to pass input and output file " \ "descriptors to pry directly. e.g." puts " Pry.config.input = STDIN" puts " Pry.config.output = STDOUT" puts " binding.pry" return :no_more_input end end # Returns the next line of input to be sent to the {Pry} instance. # @param [String] current_prompt The prompt to use for input. # @return [String?] The next line of input, or `nil` on . def read_line(current_prompt) handle_read_errors do if coolline_available? input.completion_proc = proc do |cool| completions = @pry.complete cool.completed_word completions.compact end elsif input.respond_to? :completion_proc= input.completion_proc = proc do |inp| @pry.complete inp end end if readline_available? set_readline_output input_readline(current_prompt, false) # false since we'll add it manually elsif coolline_available? input_readline(current_prompt) elsif input.method(:readline).arity == 1 input_readline(current_prompt) else input_readline end end end def input_readline(*args) Pry::InputLock.for(:all).interruptible_region do input.readline(*args) end end def readline_available? defined?(Readline) && input == Readline end def coolline_available? defined?(Coolline) && input.is_a?(Coolline) end # If `$stdout` is not a tty, it's probably a pipe. # @example # # `piping?` returns `false` # % pry # [1] pry(main) # # # `piping?` returns `true` # % pry | tee log def piping? return false unless $stdout.respond_to?(:tty?) !$stdout.tty? && $stdin.tty? && !Helpers::Platform.windows? end # @return [void] def set_readline_output return if @readline_output @readline_output = (Readline.output = Pry.config.output) if piping? end # Calculates correct overhang for current line. Supports vi Readline # mode and its indicators such as "(ins)" or "(cmd)". # # @return [Integer] # @note This doesn't calculate overhang for Readline's emacs mode with an # indicator because emacs is the default mode and it doesn't use # indicators in 99% of cases. def calculate_overhang(current_prompt, original_val, indented_val) overhang = original_val.length - indented_val.length if readline_available? && Readline.respond_to?(:vi_editing_mode?) begin # rb-readline doesn't support this method: # https://github.com/ConnorAtherton/rb-readline/issues/152 if Readline.vi_editing_mode? overhang = output.width - current_prompt.size - indented_val.size end rescue NotImplementedError # VI editing mode is unsupported on JRuby. # https://github.com/pry/pry/issues/1840 nil end end [0, overhang].max end end end pry-0.13.1/lib/pry/repl_file_loader.rb000066400000000000000000000055201364454223300176120ustar00rootroot00000000000000# frozen_string_literal: true class Pry # A class to manage the loading of files through the REPL loop. # This is an interesting trick as it processes your file as if it # was user input in an interactive session. As a result, all Pry # commands are available, and they are executed non-interactively. Furthermore # the session becomes interactive when the repl loop processes a # 'make-interactive' command in the file. The session also becomes # interactive when an exception is encountered, enabling you to fix # the error before returning to non-interactive processing with the # 'make-non-interactive' command. class REPLFileLoader def initialize(file_name) full_name = File.expand_path(file_name) raise "No such file: #{full_name}" unless File.exist?(full_name) define_additional_commands @content = File.read(full_name) end # Switch to interactive mode, i.e take input from the user # and use the regular print and exception handlers. # @param [Pry] pry_instance the Pry instance to make interactive. def interactive_mode(pry_instance) pry_instance.config.input = Pry.config.input pry_instance.config.print = Pry.config.print pry_instance.config.exception_handler = Pry.config.exception_handler Pry::REPL.new(pry_instance).start end # Switch to non-interactive mode. Essentially # this means there is no result output # and that the session becomes interactive when an exception is encountered. # @param [Pry] pry_instance the Pry instance to make non-interactive. def non_interactive_mode(pry_instance, content) pry_instance.print = proc {} pry_instance.exception_handler = proc do |o, _e, p| p.run_command "cat --ex" o.puts "...exception encountered, going interactive!" interactive_mode(pry_instance) end content.lines.each do |line| break unless pry_instance.eval line, generated: true end return if pry_instance.eval_string.empty? pry_instance.output.puts( "#{pry_instance.eval_string}...exception encountered, going interactive!" ) interactive_mode(pry_instance) end # Define a few extra commands useful for flipping back & forth # between interactive/non-interactive modes def define_additional_commands s = self Pry::Commands.command "make-interactive", "Make the session interactive" do s.interactive_mode(pry_instance) end Pry::Commands.command( "load-file", "Load another file through the repl" ) do |file_name| s.non_interactive_mode(pry_instance, File.read(File.expand_path(file_name))) end end # Actually load the file through the REPL by setting file content # as the REPL input stream. def load non_interactive_mode(Pry.new, @content) end end end pry-0.13.1/lib/pry/ring.rb000066400000000000000000000042331364454223300152620ustar00rootroot00000000000000# frozen_string_literal: true class Pry # A ring is a thread-safe fixed-capacity array to which you can only add # elements. Older entries are overwritten as you add new elements, so that the # ring can never contain more than `max_size` elemens. # # @example # ring = Pry::Ring.new(3) # ring << 1 << 2 << 3 # ring.to_a #=> [1, 2, 3] # ring << 4 # ring.to_a #=> [2, 3, 4] # # ring[0] #=> 2 # ring[-1] #=> 4 # ring.clear # ring[0] #=> nil # # @api public # @since v0.12.0 class Ring # @return [Integer] maximum buffer size attr_reader :max_size # @return [Integer] how many objects were added during the lifetime of the # ring attr_reader :count alias size count # @param [Integer] max_size Maximum buffer size. The buffer will start # overwriting elements once its reaches its maximum capacity def initialize(max_size) @max_size = max_size @mutex = Mutex.new clear end # Push `value` to the current index. # # @param [Object] value # @return [self] def <<(value) @mutex.synchronize do @buffer[count % max_size] = value @count += 1 self end end # Read the value stored at `index`. # # @param [Integer, Range] index The element (if Integer) or elements # (if Range) associated with `index` # @return [Object, Array, nil] element(s) at `index`, `nil` if none # exist def [](index) @mutex.synchronize do return @buffer[(count + index) % max_size] if index.is_a?(Integer) return @buffer[index] if count <= max_size transpose_buffer_tail[index] end end # @return [Array] the buffer as unwinded array def to_a return @buffer.dup if count <= max_size transpose_buffer_tail end # Clear the buffer and reset count. # @return [void] def clear @mutex.synchronize do @buffer = [] @count = 0 end end private def transpose_buffer_tail tail = @buffer.slice(count % max_size, @buffer.size) tail.concat @buffer.slice(0, count % max_size) end end end pry-0.13.1/lib/pry/slop.rb000066400000000000000000000475321364454223300153110ustar00rootroot00000000000000# frozen_string_literal: true class Pry # rubocop:disable Metrics/ClassLength class Slop require_relative 'slop/option' require_relative 'slop/commands' include Enumerable VERSION = '3.4.0'.freeze # The main Error class, all Exception classes inherit from this class. class Error < StandardError; end # Raised when an option argument is expected but none are given. class MissingArgumentError < Error; end # Raised when an option is expected/required but not present. class MissingOptionError < Error; end # Raised when an argument does not match its intended match constraint. class InvalidArgumentError < Error; end # Raised when an invalid option is found and the strict flag is enabled. class InvalidOptionError < Error; end # Raised when an invalid command is found and the strict flag is enabled. class InvalidCommandError < Error; end # Returns a default Hash of configuration options this Slop instance uses. DEFAULT_OPTIONS = { strict: false, help: false, banner: nil, ignore_case: false, autocreate: false, arguments: false, optional_arguments: false, multiple_switches: true, longest_flag: 0 }.freeze class << self # items - The Array of items to extract options from (default: ARGV). # config - The Hash of configuration options to send to Slop.new(). # block - An optional block used to add options. # # Examples: # # Slop.parse(ARGV, :help => true) do # on '-n', '--name', 'Your username', :argument => true # end # # Returns a new instance of Slop. def parse(items = ARGV, config = {}, &block) parse! items.dup, config, &block end # items - The Array of items to extract options from (default: ARGV). # config - The Hash of configuration options to send to Slop.new(). # block - An optional block used to add options. # # Returns a new instance of Slop. def parse!(items = ARGV, config = {}, &block) if items.is_a?(Hash) && config.empty? config = items items = ARGV end slop = Pry::Slop.new config, &block slop.parse! items slop end # Build a Slop object from a option specification. # # This allows you to design your options via a simple String rather # than programatically. Do note though that with this method, you're # unable to pass any advanced options to the on() method when creating # options. # # string - The optspec String # config - A Hash of configuration options to pass to Slop.new # # Examples: # # opts = Slop.optspec(<<-SPEC) # ruby foo.rb [options] # --- # n,name= Your name # a,age= Your age # A,auth Sign in with auth # p,passcode= Your secret pass code # SPEC # # opts.fetch_option(:name).description #=> "Your name" # # Returns a new instance of Slop. def optspec(string, config = {}) config[:banner], optspec = string.split(/^--+$/, 2) if string[/^--+$/] lines = optspec.split("\n").reject(&:empty?) opts = Slop.new(config) lines.each do |line| opt, description = line.split(' ', 2) short, long = opt.split(',').map { |s| s.sub(/\A--?/, '') } opt = opts.on(short, long, description) if long && long.end_with?('=') long.sub!(/\=$/, '') opt.config[:argument] = true end end opts end end # The Hash of configuration options for this Slop instance. attr_reader :config # The Array of Slop::Option objects tied to this Slop instance. attr_reader :options # Create a new instance of Slop and optionally build options via a block. # # config - A Hash of configuration options. # block - An optional block used to specify options. def initialize(config = {}, &block) @config = DEFAULT_OPTIONS.merge(config) @options = [] @commands = {} @trash = [] @triggered_options = [] @unknown_options = [] @callbacks = {} @separators = {} @runner = nil if block_given? block.arity == 1 ? yield(self) : instance_eval(&block) end return unless config[:help] on('-h', '--help', 'Display this help message.', tail: true) do warn help end end # Is strict mode enabled? # # Returns true if strict mode is enabled, false otherwise. def strict? config[:strict] end # Set the banner. # # banner - The String to set the banner. def banner=(banner) config[:banner] = banner end # Get or set the banner. # # banner - The String to set the banner. # # Returns the banner String. def banner(banner = nil) config[:banner] = banner if banner config[:banner] end # Set the description (used for commands). # # desc - The String to set the description. def description=(desc) config[:description] = desc end # Get or set the description (used for commands). # # desc - The String to set the description. # # Returns the description String. def description(desc = nil) config[:description] = desc if desc config[:description] end # Add a new command. # # command - The Symbol or String used to identify this command. # options - A Hash of configuration options (see Slop::new) # # Returns a new instance of Slop mapped to this command. def command(command, options = {}, &block) @commands[command.to_s] = Pry::Slop.new(options, &block) end # Parse a list of items, executing and gathering options along the way. # # items - The Array of items to extract options from (default: ARGV). # block - An optional block which when used will yield non options. # # Returns an Array of original items. def parse(items = ARGV, &block) parse! items.dup, &block items end # Parse a list of items, executing and gathering options along the way. # unlike parse() this method will remove any options and option arguments # from the original Array. # # items - The Array of items to extract options from (default: ARGV). # block - An optional block which when used will yield non options. # # Returns an Array of original items with options removed. def parse!(items = ARGV, &block) if items.empty? && @callbacks[:empty] @callbacks[:empty].each { |cb| cb.call(self) } return items end if (cmd = @commands[items[0]]) return cmd.parse! items[1..-1] end items.each_with_index do |item, index| @trash << index && break if item == '--' autocreate(items, index) if config[:autocreate] process_item(items, index, &block) unless @trash.include?(index) end items.reject!.with_index { |_item, index| @trash.include?(index) } missing_options = options.select { |opt| opt.required? && opt.count < 1 } if missing_options.any? raise MissingOptionError, "Missing required option(s): #{missing_options.map(&:key).join(', ')}" end if @unknown_options.any? raise InvalidOptionError, "Unknown options #{@unknown_options.join(', ')}" end if @triggered_options.empty? && @callbacks[:no_options] @callbacks[:no_options].each { |cb| cb.call(self) } end @runner.call(self, items) if @runner.respond_to?(:call) items end # Add an Option. # # objects - An Array with an optional Hash as the last element. # # Examples: # # on '-u', '--username=', 'Your username' # on :v, :verbose, 'Enable verbose mode' # # Returns the created instance of Slop::Option. def on(*objects, &block) option = build_option(objects, &block) options << option option end alias option on alias opt on # Fetch an options argument value. # # key - The Symbol or String option short or long flag. # # Returns the Object value for this option, or nil. def [](key) option = fetch_option(key) option.value if option end alias get [] # Returns a new Hash with option flags as keys and option values as values. # # include_commands - If true, merge options from all sub-commands. def to_hash(include_commands = false) hash = Hash[options.map { |opt| [opt.key.to_sym, opt.value] }] if include_commands @commands.each { |cmd, opts| hash.merge!(cmd.to_sym => opts.to_hash) } end hash end alias to_h to_hash # Enumerable interface. Yields each Slop::Option. def each(&block) options.each(&block) end # Specify code to be executed when these options are parsed. # # callable - An object responding to a call method. # # yields - The instance of Slop parsing these options # An Array of unparsed arguments # # Example: # # Slop.parse do # on :v, :verbose # # run do |opts, args| # puts "Arguments: #{args.inspect}" if opts.verbose? # end # end def run(callable = nil, &block) @runner = callable || block return if @runner.respond_to?(:call) raise ArgumentError, "You must specify a callable object or a block to #run" end # Check for an options presence. # # Examples: # # opts.parse %w( --foo ) # opts.present?(:foo) #=> true # opts.present?(:bar) #=> false # # Returns true if all of the keys are present in the parsed arguments. def present?(*keys) keys.all? { |key| (opt = fetch_option(key)) && opt.count > 0 } end # Override this method so we can check if an option? method exists. # # Returns true if this option key exists in our list of options. def respond_to_missing?(method_name, include_all = false) options.any? { |o| o.key == method_name.to_s.chop } || super end # Fetch a list of options which were missing from the parsed list. # # Examples: # # opts = Slop.new do # on :n, :name= # on :p, :password= # end # # opts.parse %w[ --name Lee ] # opts.missing #=> ['password'] # # Returns an Array of Strings representing missing options. def missing (options - @triggered_options).map(&:key) end # Fetch a Slop::Option object. # # key - The Symbol or String option key. # # Examples: # # opts.on(:foo, 'Something fooey', :argument => :optional) # opt = opts.fetch_option(:foo) # opt.class #=> Slop::Option # opt.accepts_optional_argument? #=> true # # Returns an Option or nil if none were found. def fetch_option(key) options.find { |option| [option.long, option.short].include?(clean(key)) } end # Fetch a Slop object associated with this command. # # command - The String or Symbol name of the command. # # Examples: # # opts.command :foo do # on :v, :verbose, 'Enable verbose mode' # end # # # ruby run.rb foo -v # opts.fetch_command(:foo).verbose? #=> true def fetch_command(command) @commands[command.to_s] end # Add a callback. # # label - The Symbol identifier to attach this callback. # # Returns nothing. def add_callback(label, &block) (@callbacks[label] ||= []) << block end # Add string separators between options. # # text - The String text to print. def separator(text) if @separators[options.size] @separators[options.size] << "\n#{text}" else @separators[options.size] = text end end # Print a handy Slop help string. # # Returns the banner followed by available option help strings. def to_s heads = options.reject(&:tail?) tails = (options - heads) opts = (heads + tails).select(&:help).map(&:to_s) optstr = opts.each_with_index.map do |o, i| (str = @separators[i + 1]) ? [o, str].join("\n") : o end.join("\n") if @commands.any? optstr << "\n" unless optstr.empty? optstr << "\nAvailable commands:\n\n" optstr << commands_to_help optstr << "\n\nSee ` --help` for more information on a specific command." end banner = config[:banner] banner ||= "Usage: #{File.basename($PROGRAM_NAME, '.*')}" \ "#{' [command]' if @commands.any?} [options]" if banner "#{banner}\n#{@separators[0] ? "#{@separators[0]}\n" : ''}#{optstr}" else optstr end end alias help to_s private # Convenience method for present?(:option). # # Examples: # # opts.parse %( --verbose ) # opts.verbose? #=> true # opts.other? #=> false # # Returns true if this option is present. If this method does not end # with a ? character it will instead call super(). def method_missing(method, *args, &block) meth = method.to_s if meth.end_with?('?') meth = meth.chop present?(meth) || present?(meth.tr('_', '-')) else super end end # Process a list item, figure out if it's an option, execute any # callbacks, assign any option arguments, and do some sanity checks. # # items - The Array of items to process. # index - The current Integer index of the item we want to process. # block - An optional block which when passed will yield non options. # # Returns nothing. def process_item(items, index, &block) return unless (item = items[index]) option, argument = extract_option(item) if item.start_with?('-') if option option.count += 1 unless item.start_with?('--no-') option.count += 1 if option.key[0, 3] == "no-" @trash << index @triggered_options << option if option.expects_argument? argument ||= items.at(index + 1) if !argument || argument =~ /\A--?[a-zA-Z][a-zA-Z0-9_-]*\z/ raise MissingArgumentError, "#{option.key} expects an argument" end execute_option(option, argument, index, item) elsif option.accepts_optional_argument? argument ||= items.at(index + 1) if argument && argument =~ /\A([^\-?]|-\d)+/ execute_option(option, argument, index, item) else option.call(nil) end elsif config[:multiple_switches] && argument execute_multiple_switches(option, argument, index) else option.value = option.count > 0 option.call(nil) end else @unknown_options << item if strict? && item =~ /\A--?/ yield(item) if block && !@trash.include?(index) end end # Execute an option, firing off callbacks and assigning arguments. # # option - The Slop::Option object found by #process_item. # argument - The argument Object to assign to this option. # index - The current Integer index of the object we're processing. # item - The optional String item we're processing. # # Returns nothing. def execute_option(option, argument, index, item = nil) unless option if config[:multiple_switches] && strict? raise InvalidOptionError, "Unknown option -#{item}" end return end if argument unless item && item.end_with?("=#{argument}") @trash << index + 1 unless option.argument_in_value end option.value = argument else option.value = option.count > 0 end if option.match? && !argument.match(option.config[:match]) raise InvalidArgumentError, "#{argument} is an invalid argument" end option.call(option.value) end # Execute a `-abc` type option where a, b and c are all options. This # method is only executed if the multiple_switches argument is true. # # option - The first Option object. # argument - The argument to this option. (Split into multiple Options). # index - The index of the current item being processed. # # Returns nothing. def execute_multiple_switches(option, argument, index) execute_option(option, nil, index) argument.split('').each do |key| next unless (opt = fetch_option(key)) opt.count += 1 execute_option(opt, nil, index, key) end end # Extract an option from a flag. # # flag - The flag key used to extract an option. # # Returns an Array of [option, argument]. def extract_option(flag) option = fetch_option(flag) option ||= fetch_option(flag.downcase) if config[:ignore_case] option ||= fetch_option(flag.gsub(/([^-])-/, '\1_')) unless option case flag when /\A--?([^=]+)=(.+)\z/, /\A-([a-zA-Z])(.+)\z/, /\A--no-(.+)\z/ option = fetch_option(Regexp.last_match(1)) argument = Regexp.last_match(2) || false option.argument_in_value = true if option end end [option, argument] end # Autocreate an option on the fly. See the :autocreate Slop config option. # # items - The Array of items we're parsing. # index - The current Integer index for the item we're processing. # # Returns nothing. def autocreate(items, index) flag = items[index] return if fetch_option(flag) || @trash.include?(index) option = build_option(Array(flag)) argument = items[index + 1] option.config[:argument] = (argument && argument !~ /\A--?/) option.config[:autocreated] = true options << option end # Build an option from a list of objects. # # objects - An Array of objects used to build this option. # # Returns a new instance of Slop::Option. def build_option(objects, &block) config = {} config[:argument] = true if @config[:arguments] config[:optional_argument] = true if @config[:optional_arguments] if objects.last.is_a?(Hash) config.merge!(objects.last) objects.pop end short = extract_short_flag(objects, config) long = extract_long_flag(objects, config) desc = objects[0].respond_to?(:to_str) ? objects.shift : nil Option.new(self, short, long, desc, config, &block) end # Extract the short flag from an item. # # objects - The Array of objects passed from #build_option. # config - The Hash of configuration options built in #build_option. def extract_short_flag(objects, config) flag = clean(objects.first) if flag.size == 2 && flag.end_with?('=') config[:argument] ||= true flag.chop! end return unless flag.size == 1 objects.shift flag end # Extract the long flag from an item. # # objects - The Array of objects passed from #build_option. # config - The Hash of configuration options built in #build_option. def extract_long_flag(objects, config) flag = objects.first.to_s return unless flag =~ /\A(?:--?)?[a-zA-Z][a-zA-Z0-9_-]+\=?\??\z/ config[:argument] ||= true if flag.end_with?('=') config[:optional_argument] = true if flag.end_with?('=?') objects.shift clean(flag).sub(/\=\??\z/, '') end # Remove any leading -- characters from a string. # # object - The Object we want to cast to a String and clean. # # Returns the newly cleaned String with leading -- characters removed. def clean(object) object.to_s.sub(/\A--?/, '') end def commands_to_help padding = 0 @commands.each { |c, _| padding = c.size if c.size > padding } @commands.map do |cmd, opts| " #{cmd}#{' ' * (padding - cmd.size)} #{opts.description}" end.join("\n") end end # rubocop:enable Metrics/ClassLength end pry-0.13.1/lib/pry/slop/000077500000000000000000000000001364454223300147515ustar00rootroot00000000000000pry-0.13.1/lib/pry/slop/LICENSE000066400000000000000000000020361364454223300157570ustar00rootroot00000000000000Copyright (c) 2012 Lee Jarvis Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. pry-0.13.1/lib/pry/slop/commands.rb000066400000000000000000000132371364454223300171050ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Slop class Commands include Enumerable attr_reader :config, :commands, :arguments attr_writer :banner # Create a new instance of Slop::Commands and optionally build # Slop instances via a block. Any configuration options used in # this method will be the default configuration options sent to # each Slop object created. # # config - An optional configuration Hash. # block - Optional block used to define commands. # # Examples: # # commands = Slop::Commands.new do # on :new do # on '-o', '--outdir=', 'The output directory' # on '-v', '--verbose', 'Enable verbose mode' # end # # on :generate do # on '--assets', 'Generate assets', :default => true # end # # global do # on '-D', '--debug', 'Enable debug mode', :default => false # end # end # # commands[:new].class #=> Slop # commands.parse # def initialize(config = {}, &block) @config = config @commands = {} @banner = nil @triggered_command = nil warn "[DEPRECATED] Slop::Commands is deprecated and will be removed in "\ "Slop version 4. Check out http://injekt.github.com/slop/#commands for "\ "a new implementation of commands." return unless block_given? block.arity == 1 ? yield(self) : instance_eval(&block) end # Optionally set the banner for this command help output. # # banner - The String text to set the banner. # # Returns the String banner if one is set. def banner(banner = nil) @banner = banner if banner @banner end # Add a Slop instance for a specific command. # # command - A String or Symbol key used to identify this command. # config - A Hash of configuration options to pass to Slop. # block - An optional block used to pass options to Slop. # # Returns the newly created Slop instance mapped to command. def on(command, config = {}, &block) commands[command.to_s] = Slop.new(@config.merge(config), &block) end # Add a Slop instance used when no other commands exist. # # config - A Hash of configuration options to pass to Slop. # block - An optional block used to pass options to Slop. # # Returns the newly created Slop instance mapped to default. def default(config = {}, &block) on('default', config, &block) end # Add a global Slop instance. # # config - A Hash of configuration options to pass to Slop. # block - An optional block used to pass options to Slop. # # Returns the newly created Slop instance mapped to global. def global(config = {}, &block) on('global', config, &block) end # Fetch the instance of Slop tied to a command. # # key - The String or Symbol key used to locate this command. # # Returns the Slop instance if this key is found, nil otherwise. def [](key) commands[key.to_s] end alias get [] # Check for a command presence. # # Examples: # # cmds.parse %w( foo ) # cmds.present?(:foo) #=> true # cmds.present?(:bar) #=> false # # Returns true if the given key is present in the parsed arguments. def present?(key) key.to_s == @triggered_command end # Enumerable interface. def each(&block) @commands.each(&block) end # Parse a list of items. # # items - The Array of items to parse. # # Returns the original Array of items. def parse(items = ARGV) parse! items.dup items end # Parse a list of items, removing any options or option arguments found. # # items - The Array of items to parse. # # Returns the original Array of items with options removed. def parse!(items = ARGV) if (opts = commands[items[0].to_s]) @triggered_command = items.shift execute_arguments! items opts.parse! items elsif (opts = commands['default']) opts.parse! items elsif config[:strict] && items[0] raise InvalidCommandError, "Unknown command `#{items[0]}`" end execute_global_opts! items items end # Returns a nested Hash with Slop options and values. See Slop#to_hash. def to_hash Hash[commands.map { |k, v| [k.to_sym, v.to_hash] }] end # Returns the help String. def to_s defaults = commands.delete('default') globals = commands.delete('global') helps = commands.reject { |_, v| v.options.none? } helps['Global options'] = globals.to_s if globals && globals.options.any? helps['Other options'] = defaults.to_s if defaults && defaults.options.any? banner = @banner ? "#{@banner}\n" : "" banner + helps.map { |key, opts| " #{key}\n#{opts}" }.join("\n\n") end alias help to_s # Returns the inspection String. def inspect "#" end private # Returns nothing. def execute_arguments!(items) @arguments = items.take_while { |arg| !arg.start_with?('-') } items.shift @arguments.size end # Returns nothing. def execute_global_opts!(items) return unless (global_opts = commands['global']) global_opts.parse!(items) end end end end pry-0.13.1/lib/pry/slop/option.rb000066400000000000000000000143601364454223300166120ustar00rootroot00000000000000# frozen_string_literal: true class Pry class Slop class Option # The default Hash of configuration options this class uses. DEFAULT_OPTIONS = { argument: false, optional_argument: false, tail: false, default: nil, callback: nil, delimiter: ',', limit: 0, match: nil, optional: true, required: false, as: String, autocreated: false }.freeze attr_reader :short, :long, :description, :config, :types attr_accessor :count, :argument_in_value # Incapsulate internal option information, mainly used to store # option specific configuration data, most of the meat of this # class is found in the #value method. # # slop - The instance of Slop tied to this Option. # short - The String or Symbol short flag. # long - The String or Symbol long flag. # description - The String description text. # config - A Hash of configuration options. # block - An optional block used as a callback. def initialize(slop, short, long, description, config = {}, &block) @slop = slop @short = short @long = long @description = description @config = DEFAULT_OPTIONS.merge(config) @count = 0 @callback = block_given? ? block : config[:callback] @value = nil @types = { string: proc { |v| v.to_s }, symbol: proc { |v| v.to_sym }, integer: proc { |v| value_to_integer(v) }, float: proc { |v| value_to_float(v) }, range: proc { |v| value_to_range(v) }, count: proc { @count } } if long && long.size > @slop.config[:longest_flag] @slop.config[:longest_flag] = long.size end @config.each_key do |key| predicate = :"#{key}?" unless self.class.method_defined?(predicate) self.class.__send__(:define_method, predicate) { !@config.key?(key) } end end end # Returns true if this option expects an argument. def expects_argument? config[:argument] && config[:argument] != :optional end # Returns true if this option accepts an optional argument. def accepts_optional_argument? config[:optional_argument] || config[:argument] == :optional end # Returns the String flag of this option. Preferring the long flag. def key long || short end # Call this options callback if one exists, and it responds to call(). # # Returns nothing. def call(*objects) @callback.call(*objects) if @callback.respond_to?(:call) end # Set the new argument value for this option. # # We use this setter method to handle concatenating lists. That is, # when an array type is specified and used more than once, values from # both options will be grouped together and flattened into a single array. def value=(new_value) if config[:as].to_s.casecmp('array') == 0 @value ||= [] if new_value.respond_to?(:split) @value.concat new_value.split(config[:delimiter], config[:limit]) end else @value = new_value end end # Fetch the argument value for this option. # # Returns the Object once any type conversions have taken place. def value value = @value.nil? ? config[:default] : @value return value if [true, false, nil].include?(value) && config[:as].to_s != 'count' type = config[:as] if type.respond_to?(:call) type.call(value) elsif (callable = types[type.to_s.downcase.to_sym]) callable.call(value) else value end end # Returns the help String for this option. def to_s return config[:help] if config[:help].respond_to?(:to_str) out = " #{short ? "-#{short}, " : ' ' * 4}" if long out += "--#{long}" size = long.size diff = @slop.config[:longest_flag] - size out += (' ' * (diff + 6)) else out += (' ' * (@slop.config[:longest_flag] + 8)) end "#{out}#{description}" end alias help to_s # Returns the String inspection text. def inspect "# exception @pry.output.puts( "SyntaxError: #{exception.message.sub(/.*syntax error, */m, '')}" ) end @pry.evaluate_ruby(str) if complete_expr end end result end def push(*lines) Array(lines).flatten.each do |line| @pry.eval(line) end end def push_binding(context) @pry.push_binding context end def last_output @out.string if @out end def process_command(command_str) @pry.process_command(command_str) || raise("Not a valid command") last_command_result_or_output end def last_command_result result = Pry.current[:pry_cmd_result] result.retval if result end protected def last_command_result_or_output result = last_command_result if result != Pry::Command::VOID_VALUE result else last_output end end def reset_output @out = StringIO.new @pry.output = @out end end end end pry-0.13.1/lib/pry/testable/utility.rb000066400000000000000000000012471364454223300176330ustar00rootroot00000000000000# frozen_string_literal: true require 'tempfile' class Pry module Testable module Utility # # Creates a Tempfile then unlinks it after the block has yielded. # # @yieldparam [String] file # The path of the temp file # # @return [void] # def temp_file(ext = '.rb') file = Tempfile.open(['pry', ext]) yield file ensure file.close(true) if file end def unindent(*args) Pry::Helpers::CommandHelpers.unindent(*args) end def inner_scope catch(:inner_scope) do yield -> { throw(:inner_scope, self) } end end end end end pry-0.13.1/lib/pry/testable/variables.rb000066400000000000000000000024071364454223300200770ustar00rootroot00000000000000# frozen_string_literal: true class Pry module Testable module Variables # # @example # temporary_constants(:Foo, :Bar) do # Foo = Class.new(RuntimeError) # Bar = Class.new(RuntimeError) # end # Foo # => NameError # Bar # => NameError # # @param [Array] names # An array of constant names that be defined by a block, # and removed by this method afterwards. # # @return [void] # def temporary_constants(*names) names.each do |name| Object.remove_const name if Object.const_defined?(name) end yield ensure names.each do |name| Object.remove_const name if Object.const_defined?(name) end end # # @param [String] name # The name of a variable. # # @param [String] value # Its value. # # @param [Binding] binding # The binding object to insert a variable into. # # @return [void] # def insert_variable(name, value, binding) Pry.current[:pry_local] = value binding.eval("#{name} = ::Pry.current[:pry_local]") ensure Pry.current[:pry_local] = nil end end end end pry-0.13.1/lib/pry/version.rb000066400000000000000000000001111364454223300157770ustar00rootroot00000000000000# frozen_string_literal: true class Pry VERSION = '0.13.1'.freeze end pry-0.13.1/lib/pry/warning.rb000066400000000000000000000013021364454223300157620ustar00rootroot00000000000000# frozen_string_literal: true class Pry # @api private # @since v0.13.0 module Warning # Prints a warning message with exact file and line location, similar to how # Ruby's -W prints warnings. # # @param [String] message # @return [void] def self.warn(message) if Kernel.respond_to?(:caller_locations) location = caller_locations(2..2).first path = location.path lineno = location.lineno else # Ruby 1.9.3 support. frame = caller[1].split(':') # rubocop:disable Performance/Caller path = frame.first lineno = frame[1] end Kernel.warn("#{path}:#{lineno}: warning: #{message}") end end end pry-0.13.1/lib/pry/wrapped_module.rb000066400000000000000000000310771364454223300173400ustar00rootroot00000000000000# frozen_string_literal: true class Pry class << self # If the given object is a `Pry::WrappedModule`, return it unaltered. If it's # anything else, return it wrapped in a `Pry::WrappedModule` instance. def WrappedModule(obj) if obj.is_a? Pry::WrappedModule obj else Pry::WrappedModule.new(obj) end end end class WrappedModule include Helpers::BaseHelpers include CodeObject::Helpers attr_reader :wrapped # Convert a string to a module. # # @param [String] mod_name # @param [Binding] target The binding where the lookup takes place. # @return [Module, nil] The module or `nil` (if conversion failed). # @example # Pry::WrappedModule.from_str("Pry::Code") def self.from_str(mod_name, target = TOPLEVEL_BINDING) Pry::WrappedModule.new(target.eval(mod_name)) if safe_to_evaluate?(mod_name, target) rescue RescuableException nil end class << self private # We use this method to decide whether code is safe to eval. Method's are # generally not, but everything else is. # TODO: is just checking != "method" enough?? # TODO: see duplication of this method in Pry::CodeObject # @param [String] str The string to lookup. # @param [Binding] target Where the lookup takes place. # @return [Boolean] def safe_to_evaluate?(str, target) return true if str.strip == "self" return false if str =~ /%/ kind = target.eval("defined?(#{str})") kind =~ /variable|constant/ end end # @raise [ArgumentError] if the argument is not a `Module` # @param [Module] mod def initialize(mod) unless mod.is_a?(Module) raise ArgumentError, "Tried to initialize a WrappedModule with a " \ "non-module #{mod.inspect}" end @wrapped = mod @memoized_candidates = [] @host_file_lines = nil @source = nil @source_location = nil @doc = nil @all_source_locations_by_popularity = nil end # Returns an array of the names of the constants accessible in the wrapped # module. This avoids the problem of accidentally calling the singleton # method `Module.constants`. # @param [Boolean] inherit Include the names of constants from included # modules? def constants(inherit = true) Module.instance_method(:constants).bind(@wrapped).call(inherit) end # The prefix that would appear before methods defined on this class. # # i.e. the "String." or "String#" in String.new and String#initialize. # # @return String def method_prefix if singleton_class? if Module === singleton_instance # rubocop:disable Style/CaseEquality "#{WrappedModule.new(singleton_instance).nonblank_name}." else "self." end else "#{nonblank_name}#" end end # The name of the Module if it has one, otherwise #. # # @return [String] def nonblank_name if name.to_s == "" wrapped.inspect else name end end # Is this a singleton class? # @return [Boolean] def singleton_class? if Pry::Method.safe_send(wrapped, :respond_to?, :singleton_class?) Pry::Method.safe_send(wrapped, :singleton_class?) else wrapped != Pry::Method.safe_send(wrapped, :ancestors).first end end # Is this strictly a module? (does not match classes) # @return [Boolean] def module? wrapped.instance_of?(Module) end # Is this strictly a class? # @return [Boolean] def class? wrapped.instance_of?(Class) end # Get the instance associated with this singleton class. # # @raise ArgumentError: tried to get instance of non singleton class # # @return [Object] def singleton_instance unless singleton_class? raise ArgumentError, "tried to get instance of non singleton class" end if Helpers::Platform.jruby? wrapped.to_java.attached else @singleton_instance ||= ObjectSpace.each_object(wrapped).detect do |x| (class << x; self; end) == wrapped end end end # Forward method invocations to the wrapped module def method_missing(method_name, *args, &block) if wrapped.respond_to?(method_name) wrapped.send(method_name, *args, &block) else super end end def respond_to_missing?(method_name, include_private = false) wrapped.respond_to?(method_name, include_private) || super end # Retrieve the source location of a module. Return value is in same # format as Method#source_location. If the source location # cannot be found this method returns `nil`. # # @return [Array, nil] The source location of the # module (or class), or `nil` if no source location found. def source_location @source_location ||= primary_candidate.source_location rescue Pry::RescuableException nil end # @return [String, nil] The associated file for the module (i.e # the primary candidate: highest ranked monkeypatch). def file Array(source_location).first end alias source_file file # @return [Fixnum, nil] The associated line for the module (i.e # the primary candidate: highest ranked monkeypatch). def line Array(source_location).last end alias source_line line # Returns documentation for the module. # This documentation is for the primary candidate, if # you would like documentation for other candidates use # `WrappedModule#candidate` to select the candidate you're # interested in. # @raise [Pry::CommandError] If documentation cannot be found. # @return [String] The documentation for the module. def doc @doc ||= primary_candidate.doc end # Returns the source for the module. # This source is for the primary candidate, if # you would like source for other candidates use # `WrappedModule#candidate` to select the candidate you're # interested in. # @raise [Pry::CommandError] If source cannot be found. # @return [String] The source for the module. def source @source ||= primary_candidate.source end # @return [String] Return the associated file for the # module from YARD, if one exists. def yard_file YARD::Registry.at(name).file if yard_docs? end # @return [Fixnum] Return the associated line for the # module from YARD, if one exists. def yard_line YARD::Registry.at(name).line if yard_docs? end # @return [String] Return the YARD docs for this module. def yard_doc YARD::Registry.at(name).docstring.to_s if yard_docs? end # Return a candidate for this module of specified rank. A `rank` # of 0 is equivalent to the 'primary candidate', which is the # module definition with the highest number of methods. A `rank` # of 1 is the module definition with the second highest number of # methods, and so on. Module candidates are necessary as modules # can be reopened multiple times and in multiple places in Ruby, # the candidate API gives you access to the module definition # representing each of those reopenings. # @raise [Pry::CommandError] If the `rank` is out of range. That # is greater than `number_of_candidates - 1`. # @param [Fixnum] rank # @return [Pry::WrappedModule::Candidate] def candidate(rank) @memoized_candidates[rank] ||= WrappedModule::Candidate.new(self, rank) end # @return [Fixnum] The number of candidate definitions for the # current module. def number_of_candidates method_candidates.count end # @note On JRuby 1.9 and higher, in certain conditions, this method chucks # away its ability to be quick (when there are lots of monkey patches, # like in Rails). However, it should be efficient enough on other rubies. # @see https://github.com/jruby/jruby/issues/525 # @return [Enumerator, Array] on JRuby 1.9 and higher returns Array, on # other rubies returns Enumerator def candidates enum = Enumerator.new do |y| (0...number_of_candidates).each do |num| y.yield candidate(num) end end Helpers::Platform.jruby_19? ? enum.to_a : enum end # @return [Boolean] Whether YARD docs are available for this module. def yard_docs? !!(defined?(YARD) && YARD::Registry.at(name)) end # @param [Fixnum] times How far to travel up the ancestor chain. # @return [Pry::WrappedModule, nil] The wrapped module that is the # superclass. # When `self` is a `Module` then return the # nth ancestor, otherwise (in the case of classes) return the # nth ancestor that is a class. def super(times = 1) return self if times.zero? sup = if wrapped.is_a?(Class) ancestors.select { |v| v.is_a?(Class) }[times] else ancestors[times] end Pry::WrappedModule(sup) if sup end private # @return [Pry::WrappedModule::Candidate] The candidate with the # highest rank, that is the 'monkey patch' of this module with the # highest number of methods, which contains a source code line that # defines the module. It is considered the 'canonical' definition # for the module. In the absense of a suitable candidate, the # candidate of rank 0 will be returned, or a CommandError raised if # there are no candidates at all. def primary_candidate @primary_candidate ||= candidates.find(&:file) || candidate(0) end # @return [Array>] The array of `Pry::Method` objects, # there are two associated with each candidate. The first is the 'base # method' for a candidate and it serves as the start point for # the search in uncovering the module definition. The second is # the last method defined for that candidate and it is used to # speed up source code extraction. def method_candidates @method_candidates ||= all_source_locations_by_popularity.map do |group| methods_sorted_by_source_line = group.last.sort_by(&:source_line) [methods_sorted_by_source_line.first, methods_sorted_by_source_line.last] end end # A helper method. def all_source_locations_by_popularity return @all_source_locations_by_popularity if @all_source_locations_by_popularity ims = all_relevant_methods_for(wrapped).group_by do |v| Array(v.source_location).first end @all_source_locations_by_popularity = ims.sort_by do |path, methods| expanded = File.expand_path(path) load_order = $LOADED_FEATURES.index { |file| expanded.end_with?(file) } [-methods.size, load_order || (1.0 / 0.0)] end end # We only want methods that have a non-nil `source_location`. We also # skip some spooky internal methods. # # @return [Array] def all_relevant_methods_for(mod) methods = all_methods_for(mod).select(&:source_location) .reject { |x| method_defined_by_forwardable_module?(x) } return methods unless methods.empty? safe_send(mod, :constants).flat_map do |const_name| if (const = nested_module?(mod, const_name)) all_relevant_methods_for(const) else [] end end end # Return all methods (instance methods and class methods) for a # given module. # @return [Array] def all_methods_for(mod) Pry::Method.all_from_obj(mod, false) + Pry::Method.all_from_class(mod, false) end def nested_module?(parent, name) return if safe_send(parent, :autoload?, name) child = safe_send(parent, :const_get, name) return unless child.is_a?(Module) return unless safe_send(child, :name) == "#{safe_send(parent, :name)}::#{name}" child end # Detect methods that are defined with `def_delegator` from the Forwardable # module. We want to reject these methods as they screw up module # extraction since the `source_location` for such methods points at forwardable.rb # TODO: make this more robust as valid user-defined files called # forwardable.rb are also skipped. def method_defined_by_forwardable_module?(method) method.source_location.first =~ /forwardable\.rb/ end # memoized lines for file def lines_for_file(file) @lines_for_file ||= {} @lines_for_file[file] ||= if file == Pry.eval_path Pry.line_buffer.drop(1) else File.readlines(file) end end end end pry-0.13.1/lib/pry/wrapped_module/000077500000000000000000000000001364454223300170035ustar00rootroot00000000000000pry-0.13.1/lib/pry/wrapped_module/candidate.rb000066400000000000000000000124001364454223300212410ustar00rootroot00000000000000# frozen_string_literal: true class Pry class WrappedModule # This class represents a single candidate for a module/class definition. # It provides access to the source, documentation, line and file # for a monkeypatch (reopening) of a class/module. class Candidate include Pry::Helpers::DocumentationHelpers include Pry::CodeObject::Helpers extend Pry::Forwardable # @return [String] The file where the module definition is located. attr_reader :file alias source_file file # @return [Fixnum] The line where the module definition is located. attr_reader :line alias source_line line # Methods to delegate to associated `Pry::WrappedModule # instance`. private_delegates = [:lines_for_file, :method_candidates, :yard_docs?, :name] public_delegates = [:wrapped, :module?, :class?, :nonblank_name, :number_of_candidates] def_delegators :@wrapper, *public_delegates def_private_delegators :@wrapper, *private_delegates # @raise [Pry::CommandError] If `rank` is out of bounds. # @param [Pry::WrappedModule] wrapper The associated # `Pry::WrappedModule` instance that owns the candidates. # @param [Fixnum] rank The rank of the candidate to # retrieve. Passing 0 returns 'primary candidate' (the candidate with largest # number of methods), passing 1 retrieves candidate with # second largest number of methods, and so on, up to # `Pry::WrappedModule#number_of_candidates() - 1` def initialize(wrapper, rank) @wrapper = wrapper if number_of_candidates <= 0 raise CommandError, "Cannot find a definition for #{name} module!" end if rank > (number_of_candidates - 1) raise CommandError, "No such module candidate. Allowed candidates range is " \ "from 0 to #{number_of_candidates - 1}" end @source = @source_location = nil @rank = rank @file, @line = source_location end # @raise [Pry::CommandError] If source code cannot be found. # @return [String] The source for the candidate, i.e the # complete module/class definition. def source return nil if file.nil? return @source if @source @source ||= strip_leading_whitespace( Pry::Code.from_file(file).expression_at(line, number_of_lines_in_first_chunk) ) end # @raise [Pry::CommandError] If documentation cannot be found. # @return [String] The documentation for the candidate. def doc return nil if file.nil? @doc ||= get_comment_content(Pry::Code.from_file(file).comment_describing(line)) end # @return [Array, nil] A `[String, Fixnum]` pair representing the # source location (file and line) for the candidate or `nil` # if no source location found. def source_location return @source_location if @source_location file, line = first_method_source_location return nil unless file.is_a?(String) @source_location = [file, first_line_of_module_definition(file, line)] rescue Pry::RescuableException nil end private # Locate the first line of the module definition. # @param [String] file The file that contains the module # definition (somewhere). # @param [Fixnum] line The module definition should appear # before this line (if it exists). # @return [Fixnum] The line where the module is defined. This # line number is one-indexed. def first_line_of_module_definition(file, line) searchable_lines = lines_for_file(file)[0..(line - 2)] searchable_lines.rindex { |v| class_regexes.any? { |r| r =~ v } } + 1 end def class_regexes mod_type_string = wrapped.class.to_s.downcase [/(^|=)\s*#{mod_type_string}\s+(?:(?:\w*)::)*?#{wrapped.name.split(/::/).last}/, /^\s*(::)?#{wrapped.name.split(/::/).last}\s*?=\s*?#{wrapped.class}/, /^\s*(::)?#{wrapped.name.split(/::/).last}\.(class|instance)_eval/] end # This method is used by `Candidate#source_location` as a # starting point for the search for the candidate's definition. # @return [Array] The source location of the base method used to # calculate the source location of the candidate. def first_method_source_location @first_method_source_location ||= method_candidates[@rank].first.source_location end # @return [Array] The source location of the last method in this # candidate's module definition. def last_method_source_location @last_method_source_location ||= method_candidates[@rank].last.source_location end # Return the number of lines between the start of the class definition and # the start of the last method. We use this value so we can quickly grab # these lines from the file (without having to check each intervening line # for validity, which is expensive) speeding up source extraction. # # @return [Integer] number of lines. def number_of_lines_in_first_chunk end_method_line = last_method_source_location.last end_method_line - line end end end end pry-0.13.1/multi_test_inside_docker.sh000077500000000000000000000006461364454223300200340ustar00rootroot00000000000000#!/bin/bash -e export ORIGINAL_PATH=$PATH function test { version=$1 export PATH=$ORIGINAL_PATH export GEM_HOME=/tmp/prytmp/$version export PATH=/opt/rubies/$version/bin:$GEM_HOME/bin:$PATH export RUBY_ROOT=/opt/rubies/$version if [ ! -f $GEM_HOME/bin/bundle ]; then gem install bundler --no-document fi bundle install --quiet rake test } for ruby in `ls /opt/rubies` do test $ruby || : done pry-0.13.1/pry.gemspec000066400000000000000000000024331364454223300145750ustar00rootroot00000000000000# frozen_string_literal: true require File.expand_path('../lib/pry/version', __FILE__) Gem::Specification.new do |s| s.name = "pry" s.version = Pry::VERSION s.required_ruby_version = '>= 1.9.3' s.authors = [ 'John Mair (banisterfiend)', 'Conrad Irwin', 'Ryan Fitzgerald', 'Kyrylo Silin' ] s.email = [ 'jrmair@gmail.com', 'conrad.irwin@gmail.com', 'rwfitzge@gmail.com', 'silin@kyrylo.org' ] s.summary = 'A runtime developer console and IRB alternative with powerful ' \ 'introspection capabilities.' s.description = < 1.1' s.add_dependency 'method_source', '~> 1.0' s.metadata['changelog_uri'] = 'https://github.com/pry/pry/blob/master/CHANGELOG.md' s.metadata['source_code_uri'] = 'https://github.com/pry/pry' s.metadata['bug_tracker_uri'] = 'https://github.com/pry/pry/issues' end pry-0.13.1/spec/000077500000000000000000000000001364454223300133465ustar00rootroot00000000000000pry-0.13.1/spec/block_command_spec.rb000066400000000000000000000031131364454223300174730ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe Pry::BlockCommand do subject { Class.new(described_class).new } describe "#call" do context "when #process accepts no arguments" do let(:block) do def process; end method(:process) end before { subject.class.block = block } it "calls the block despite passed arguments" do expect { subject.call(1, 2) }.not_to raise_error end end context "when #process accepts some arguments" do let(:block) do def process(arg, other); end method(:process) end before { subject.class.block = block } it "calls the block even if there's not enough arguments" do expect { subject.call(1) }.not_to raise_error end it "calls the block even if there are more arguments than needed" do expect { subject.call(1, 2, 3) }.not_to raise_error end end context "when passed a variable-length array" do let(:block) do def process(*args); end method(:process) end before { subject.class.block = block } it "calls the block without arguments" do expect { subject.call }.not_to raise_error end it "calls the block with some arguments" do expect { subject.call(1, 2, 3) }.not_to raise_error end end end describe "#help" do before do subject.class.description = 'desc' subject.class.command_options(listing: 'listing') end it "returns help output" do expect(subject.help).to eq('listing desc') end end end pry-0.13.1/spec/class_command_spec.rb000066400000000000000000000146341364454223300175200ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe Pry::ClassCommand do describe ".inherited" do context "when match is defined" do subject do Class.new(described_class) do match('match') end end it "sets match on the subclass" do subclass = Class.new(subject) expect(subclass.match).to eq('match') end end context "when description is defined" do subject do Class.new(described_class) do description('description') end end it "sets description on the subclass" do subclass = Class.new(subject) expect(subclass.description).to eq('description') end end context "when command_options is defined" do subject do Class.new(described_class) do command_options(listing: 'listing') end end it "sets command_options on the subclass" do subclass = Class.new(subject) expect(subclass.command_options) .to match(hash_including(listing: 'listing')) end end end describe ".source" do subject { Class.new(described_class) } it "returns source code for the process method" do expect(subject.source).to match(/\Adef process\n.+\nend\n\z/) end end describe ".doc" do subject do Class.new(described_class) { banner('banner') } end it "returns source code for the process method" do expect(subject.doc).to eq("banner\n -h, --help Show this message.") end end describe ".source_location" do subject { Class.new(described_class) } it "returns source location" do expect(subject.source_location) .to match([/class_command.rb/, be_kind_of(Integer)]) end end describe ".source_file" do subject { Class.new(described_class) } it "returns source file" do expect(subject.source_file).to match(/class_command.rb/) end end describe ".source_line" do subject { Class.new(described_class) } it "returns source file" do expect(subject.source_line).to be_kind_of(Integer) end end describe "#call" do subject do command = Class.new(described_class) do def process; end end command.new end before { subject.class.banner('banner') } it "invokes setup" do expect(subject).to receive(:setup) expect(subject.call) end it "sets command's opts" do expect { subject.call }.to change { subject.opts } .from(nil).to(an_instance_of(Pry::Slop)) end it "sets command's args" do expect { subject.call('foo', 'bar') }.to change { subject.args } .from(nil).to(%w[foo bar]) end context "when help is invoked" do let(:output) { StringIO.new } before { subject.output = output } it "outputs help info" do subject.call('--help') expect(subject.output.string) .to eq("banner\n -h, --help Show this message.\n") end it "returns void value" do expect(subject.call('--help')).to eql(Pry::Command::VOID_VALUE) end end context "when help is not invloved" do context "when #process accepts no arguments" do subject do command = Class.new(described_class) do def process; end end command.new end it "calls the command despite passed arguments" do expect { subject.call('foo') }.not_to raise_error end end context "when #process accepts some arguments" do subject do command = Class.new(described_class) do def process(arg, other); end end command.new end it "calls the command even if there's not enough arguments" do expect { subject.call('foo') }.not_to raise_error end it "calls the command even if there are more arguments than needed" do expect { subject.call('1', '2', '3') }.not_to raise_error end end context "when passed a variable-length array" do subject do command = Class.new(described_class) do def process(arg, other); end end command.new end it "calls the command without arguments" do expect { subject.call }.not_to raise_error end it "calls the command with some arguments" do expect { subject.call('1', '2', '3') }.not_to raise_error end end end end describe "#help" do subject { Class.new(described_class).new } before { subject.class.banner('banner') } it "returns help output" do expect(subject.help) .to eq("banner\n -h, --help Show this message.") end end describe "#slop" do subject { Class.new(described_class).new } before { subject.class.banner(' banner') } it "returns a Slop instance" do expect(subject.slop).to be_a(Pry::Slop) end it "makes Slop's banner unindented" do slop = subject.slop expect(slop.banner).to eq('banner') end it "defines the help option" do expect(subject.slop.fetch_option(:help)).not_to be_nil end context "when there are subcommands" do subject do command = Class.new(described_class) do def subcommands(cmd) cmd.command(:download) end end command.new end it "adds subcommands to Slop" do expect(subject.slop.fetch_command(:download)).not_to be_nil end end context "when there are options" do subject do command = Class.new(described_class) do def options(opt) opt.on(:test) end end command.new end it "adds subcommands to Slop" do expect(subject.slop.fetch_option(:test)).not_to be_nil end end end describe "#complete" do subject do command = Class.new(described_class) do def options(opt) opt.on(:d, :download) opt.on(:u, :upload) opt.on(:x) end end command.new end before { subject.class.banner('') } it "generates option completions" do expect(subject.complete('')) .to match(array_including('--download ', '--upload ', '-x')) end end describe "#process" do it "raises CommandError" do expect { subject.process } .to raise_error(Pry::CommandError, /not implemented/) end end end pry-0.13.1/spec/cli_spec.rb000066400000000000000000000160371364454223300154630ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe Pry::CLI do before { described_class.reset } describe ".add_options" do it "returns self" do expect(described_class.add_options).to eq(described_class) end context "when options is nil and a block is provided" do before { described_class.options = nil } it "sets the block as options" do block = proc {} described_class.add_options(&block) expect(described_class.options).to eql(block) end end context "when options were previously set" do it "overwrites the options proc that executes the provided block" do described_class.options = proc {} executed = false described_class.add_options { executed = true } described_class.options.call expect(executed).to be_truthy end it "overwrites the options proc that executes original options" do original_executed = false described_class.options = proc { original_executed = true } described_class.add_options {} described_class.options.call expect(original_executed).to be_truthy end end end describe ".add_plugin_options" do it "returns self" do expect(described_class.add_plugin_options).to eq(described_class) end it "loads cli options of plugins" do plugin_mock = double expect(plugin_mock).to receive(:load_cli_options) plugins = { 'pry-testplugin' => plugin_mock } expect(Pry).to receive(:plugins).and_return(plugins) described_class.add_plugin_options end end describe ".add_option_processor" do it "returns self" do expect(described_class.add_option_processor {}).to eq(described_class) end it "adds an option processor" do option_processor = proc {} described_class.add_option_processor(&option_processor) expect(described_class.option_processors).to eql([option_processor]) end end describe ".parse_options" do context "when option exists" do before { described_class.options = proc { on(:v, 'test') } } it "removes the existing option from ARGV" do argv = %w[filename -v] described_class.parse_options(argv) expect(argv).not_to include('-v') end it "initializes session setup" do expect(Pry).to receive(:initial_session_setup) described_class.parse_options(%w[-v]) end it "finalizes session setup" do expect(Pry).to receive(:final_session_setup) described_class.parse_options(%w[-v]) end end context "when multiple options exist" do it "processes only called options" do processor_a_called = false processor_b_called = false processor_c_called = false described_class.options = proc do on('option-a', 'test a') { processor_a_called = true } on('option-b', 'test b') { processor_b_called = true } on('option-c', 'test c') { processor_c_called = true } end described_class.parse_options(%w[--option-a --option-b]) expect(processor_a_called).to be_truthy expect(processor_b_called).to be_truthy expect(processor_c_called).to be_falsey end end context "when option doesn't exist" do it "raises error" do expect { described_class.parse_options(['--nothing']) } .to raise_error(Pry::CLI::NoOptionsError) end end context "when argv is passed with a dash (-)" do before { described_class.options = proc {} } it "sets everything after the dash as input args" do argv = %w[filename - foo bar] described_class.parse_options(argv) expect(described_class.input_args).to eq(%w[foo bar]) end end context "when argv is passed with a double dash (--)" do before { described_class.options = proc {} } it "sets everything after the double dash as input args" do argv = %w[filename -- foo bar] described_class.parse_options(argv) expect(described_class.input_args).to eq(%w[foo bar]) end end context "when invalid option is provided" do before { described_class.options = proc { on(:valid, 'valid') } } it "exits program" do expect(Kernel).to receive(:exit) expect(STDOUT).to receive(:puts) described_class.parse_options(%w[--invalid]) end end end describe ".start" do before do # Don't start Pry session in the middle of tests. allow(Pry).to receive(:start) described_class.options = proc {} end it "sets Pry.cli to true" do opts = described_class.parse_options(%w[]) described_class.start(opts) expect(Pry.cli).to be_truthy end context "when the help option is provided" do before { described_class.options = proc { on(:help, 'help') } } it "exits" do expect(Kernel).to receive(:exit) opts = described_class.parse_options(%w[--help]) described_class.start(opts) end end context "when the context option is provided" do before { described_class.options = proc { on(:context=, 'context') } } it "initializes session setup" do expect(Pry).to receive(:initial_session_setup).twice opts = described_class.parse_options(%w[--context=Object]) described_class.start(opts) end it "finalizes session setup" do expect(Pry).to receive(:final_session_setup).twice opts = described_class.parse_options(%w[--context=Object]) described_class.start(opts) end it "starts Pry in the provided context" do expect(Pry).to receive(:start).with( instance_of(Binding), input: instance_of(StringIO) ) do |binding, _opts| expect(binding.eval('self')).to be_an(Object) end opts = described_class.parse_options(%w[--context=Object]) described_class.start(opts) end end context "when the context option is not provided" do before { described_class.options = proc {} } it "starts Pry in the top level" do expect(Pry).to receive(:start).with( instance_of(Binding), input: instance_of(StringIO) ) do |binding, _opts| expect(binding.eval('self')).to eq(Pry.main) end opts = described_class.parse_options(%w[]) described_class.start(opts) end end context "when there are some input args" do before { described_class.options = proc {} } it "loads files through repl and exits" do expect(Pry).to receive(:load_file_through_repl).with(match(%r{pry/foo})) expect(Kernel).to receive(:exit) opts = described_class.parse_options(%w[foo]) described_class.start(opts) end end context "when 'pry' is passed as an input arg" do before { described_class.options = proc {} } it "does not load files through repl" do expect(Pry).not_to receive(:load_file_through_repl) opts = described_class.parse_options(%w[pry]) described_class.start(opts) end end end end pry-0.13.1/spec/code_object_spec.rb000066400000000000000000000245151364454223300171540ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe Pry::CodeObject do let(:pry) do Pry.new.tap { |p| p.binding_stack = [binding] } end describe ".lookup" do context "when looking up method" do let(:pry) do obj = Class.new.new def obj.foo_method; end Pry.new.tap { |p| p.binding_stack = [binding] } end it "finds methods defined on objects" do code_object = described_class.lookup('obj.foo_method', pry) expect(code_object).to be_a(Pry::Method) expect(code_object.name).to eq('foo_method') end end context "when looking up modules" do module FindMeModule; end after { Object.remove_const(:FindMeModule) } it "finds modules" do code_object = described_class.lookup('FindMeModule', pry) expect(code_object).to be_a(Pry::WrappedModule) end end context "when looking up classes" do class FindMeClass; end after { Object.remove_const(:FindMeClass) } it "finds classes" do code_object = described_class.lookup('FindMeClass', pry) expect(code_object).to be_a(Pry::WrappedModule) end end context "when looking up procs" do let(:test_proc) { proc { :hello } } it "finds classes" do code_object = described_class.lookup('test_proc', pry) expect(code_object).to be_a(Pry::Method) expect(code_object.wrapped.call).to eql(test_proc) end end context "when looking up Pry::BlockCommand" do let(:pry) do pry = Pry.new pry.commands.command('test-block-command') {} pry.binding_stack = [binding] pry end it "finds Pry:BlockCommand" do code_object = described_class.lookup('test-block-command', pry) expect(code_object.command_name).to eq('test-block-command') end end context "when looking up Pry::ClassCommand" do class TestClassCommand < Pry::ClassCommand match 'test-class-command' end let(:pry) do pry = Pry.new pry.commands.add_command(TestClassCommand) pry.binding_stack = [binding] pry end after { Object.remove_const(:TestClassCommand) } it "finds Pry:BlockCommand" do code_object = described_class.lookup('test-class-command', pry) expect(code_object.command_name).to eq('test-class-command') end end context "when looking up Pry commands by class" do class TestCommand < Pry::ClassCommand match 'test-command' end let(:pry) do pry = Pry.new pry.commands.add_command(TestCommand) pry.binding_stack = [binding] pry end after { Object.remove_const(:TestCommand) } it "finds Pry::WrappedModule" do code_object = described_class.lookup('TestCommand', pry) expect(code_object).to be_a(Pry::WrappedModule) end end context "when looking up Pry commands by listing" do let(:pry) do pry = Pry.new pry.commands.command('test-command', listing: 'test-listing') {} pry.binding_stack = [binding] pry end it "finds Pry::WrappedModule" do code_object = described_class.lookup('test-listing', pry) expect(code_object.command_name).to eq('test-listing') end end context "when looking up 'nil'" do it "returns nil" do pry = Pry.new pry.binding_stack = [binding] code_object = described_class.lookup(nil, pry) expect(code_object).to be_nil end end context "when looking up 'nil' while being inside a module" do let(:pry) do Pry.new.tap { |p| p.binding_stack = [Pry.binding_for(Module)] } end it "infers the module" do code_object = described_class.lookup(nil, pry) expect(code_object).to be_a(Pry::WrappedModule) end end context "when looking up empty string while being inside a module" do let(:pry) do Pry.new.tap { |p| p.binding_stack = [Pry.binding_for(Module)] } end it "infers the module" do code_object = described_class.lookup('', pry) expect(code_object).to be_a(Pry::WrappedModule) end end context "when looking up 'nil' while being inside a class instance" do let(:pry) do Pry.new.tap { |p| p.binding_stack = [Pry.binding_for(Module.new)] } end it "infers the module" do code_object = described_class.lookup(nil, pry) expect(code_object).to be_a(Pry::WrappedModule) end end context "when looking up empty string while being inside a class instance" do let(:pry) do Pry.new.tap { |p| p.binding_stack = [Pry.binding_for(Module.new)] } end it "infers the module" do code_object = described_class.lookup('', pry) expect(code_object).to be_a(Pry::WrappedModule) end end context "when looking up 'nil' while being inside a method" do let(:pry) do klass = Class.new do def test_binding binding end end Pry.new.tap { |p| p.binding_stack = [klass.new.test_binding] } end it "infers the method" do code_object = described_class.lookup(nil, pry) expect(code_object).to be_a(Pry::Method) end end context "when looking up empty string while being inside a method" do let(:pry) do klass = Class.new do def test_binding binding end end Pry.new.tap { |p| p.binding_stack = [klass.new.test_binding] } end it "infers the method" do code_object = described_class.lookup('', pry) expect(code_object).to be_a(Pry::Method) end end context "when looking up instance methods of a class" do let(:pry) do instance = Class.new do def instance_method; end end Pry.new.tap { |p| p.binding_stack = [binding] } end it "finds instance methods" do code_object = described_class.lookup('instance#instance_method', pry) expect(code_object).to be_a(Pry::Method) end end context "when looking up instance methods" do let(:pry) do instance = Class.new do def instance_method; end end Pry.new.tap { |p| p.binding_stack = [binding] } end it "finds instance methods via the # notation" do code_object = described_class.lookup('instance#instance_method', pry) expect(code_object).to be_a(Pry::Method) end it "finds instance methods via the . notation" do code_object = described_class.lookup('instance.instance_method', pry) expect(code_object).to be_a(Pry::Method) end end context "when looking up anonymous class methods" do let(:pry) do klass = Class.new do def self.class_method; end end Pry.new.tap { |p| p.binding_stack = [binding] } end it "finds instance methods via the # notation" do code_object = described_class.lookup('klass.class_method', pry) expect(code_object).to be_a(Pry::Method) end end context "when looking up class methods of a named class" do before do class TestClass def self.class_method; end end end after { Object.remove_const(:TestClass) } it "finds instance methods via the # notation" do code_object = described_class.lookup('TestClass.class_method', pry) expect(code_object).to be_a(Pry::Method) end end context "when looking up classes by names of variables" do let(:pry) do klass = Class.new Pry.new.tap { |p| p.binding_stack = [binding] } end it "finds instance methods via the # notation" do code_object = described_class.lookup('klass', pry) expect(code_object).to be_a(Pry::WrappedModule) end end context "when looking up classes with 'super: 0'" do let(:pry) do class ParentClass; end class ChildClass < ParentClass; end Pry.new.tap { |p| p.binding_stack = [binding] } end after do Object.remove_const(:ChildClass) Object.remove_const(:ParentClass) end it "finds the child class" do code_object = described_class.lookup('ChildClass', pry, super: 0) expect(code_object).to be_a(Pry::WrappedModule) expect(code_object.wrapped).to eq(ChildClass) end end context "when looking up classes with 'super: 1'" do let(:pry) do class ParentClass; end class ChildClass < ParentClass; end Pry.new.tap { |p| p.binding_stack = [binding] } end after do Object.remove_const(:ChildClass) Object.remove_const(:ParentClass) end it "finds the parent class" do code_object = described_class.lookup('ChildClass', pry, super: 1) expect(code_object).to be_a(Pry::WrappedModule) expect(code_object.wrapped).to eq(ParentClass) end end context "when looking up commands with the super option" do let(:pry) do pry = Pry.new pry.commands.command('test-command') {} pry.binding_stack = [binding] pry end it "finds the command ignoring the super option" do code_object = described_class.lookup('test-command', pry, super: 1) expect(code_object.command_name).to eq('test-command') end end context "when there is a class and a method who is a namesake" do let(:pry) do class TestClass class InnerTestClass; end end def TestClass; end Pry.new.tap { |p| p.binding_stack = [binding] } end after { Object.remove_const(:TestClass) } it "finds the class before the method" do code_object = described_class.lookup('TestClass', pry) expect(code_object).to be_a(Pry::WrappedModule) end it "finds the method when the look up ends with ()" do code_object = described_class.lookup('TestClass()', pry) expect(code_object).to be_a(Pry::Method) end it "finds the class before the method when it's namespaced" do code_object = described_class.lookup('TestClass::InnerTestClass', pry) expect(code_object).to be_a(Pry::WrappedModule) expect(code_object.wrapped).to eq(TestClass::InnerTestClass) end end end end pry-0.13.1/spec/code_spec.rb000066400000000000000000000352561364454223300156320ustar00rootroot00000000000000# frozen_string_literal: true require 'method_source' require 'tempfile' RSpec.describe Pry::Code do describe "Pry::Code()" do context "when given a Code object" do it "returns the passed parameter unchanged" do code = described_class.new expect(Pry::Code(code)).to eql(code) end end context "when given a Method" do def bound_method :test end it "reads lines from bound method" do expect(Pry::Code(method(:bound_method)).to_s).to eq( "def bound_method\n :test\nend\n" ) end end context "when given an UnboundMethod" do def unbound_method :test end it "reads lines from unbound methods" do unbound_method = method(:unbound_method).unbind expect(Pry::Code(unbound_method).to_s).to eq( "def unbound_method\n :test\nend\n" ) end end context "when given a Proc" do it "reads lines from proc" do proc = proc { :proc } expect(Pry::Code(proc).to_s).to eq("proc = proc { :proc }\n") end end context "when given a Pry::Method" do def bound_method :test end it "reads lines from Pry::Method" do method = Pry::Method(method(:bound_method)) expect(Pry::Code(method).to_s).to eq("def bound_method\n :test\nend\n") end end context "when given an Array" do it "reads lines from the array" do expect(Pry::Code(%w[1 2 3]).length).to eq(3) end end end describe ".from_file" do it "reads lines from a file on disk" do expect(described_class.from_file(__FILE__).length).to be > 0 end it "sets code type according to the file" do expect(described_class.from_file(__FILE__).code_type).to eq(:ruby) end it "raises error when file doesn't exist" do expect { Pry::Code.from_file('abcd') } .to raise_error(MethodSource::SourceNotFoundError) end it "reads lines from a file relative to origin pwd" do filename = 'spec/' + File.basename(__FILE__) Dir.chdir('spec') do expect(described_class.from_file(filename).length).to be > 0 end end it "reads lines from a file relative to origin pwd with '.rb' omitted" do filename = 'spec/' + File.basename(__FILE__, '.*') Dir.chdir('spec') do expect(described_class.from_file(filename).code_type).to eq(:ruby) end end it "reads lines from a file relative to current pwd" do filename = File.basename(__FILE__) Dir.chdir('spec') do expect(described_class.from_file(filename).length).to be > 0 end end context "when readling lines from Pry's line buffer" do it "reads entered lines" do pry_eval ':hello' expect(described_class.from_file('(pry)').to_s).to eq(":hello\n") end it "can specify file type manually" do expect(described_class.from_file('(pry)', :c).code_type).to eq(:c) end end context "when reading lines from a file without an extension" do it "sets code type to :unknown" do temp_file('') do |f| expect(described_class.from_file(f.path).code_type).to eq(:unknown) end end end context "when reading files from $LOAD_PATH" do before { $LOAD_PATH << 'spec/fixtures' } after { $LOAD_PATH.delete('spec/fixtures') } it "finds files with '.rb' extensions" do expect(described_class.from_file('slinky.rb').code_type).to eq(:ruby) end it "finds Ruby files with omitted '.rb' extension" do expect(described_class.from_file('slinky').code_type).to eq(:ruby) end it "finds files in a relative directory with '.rb' extension" do expect(described_class.from_file('../spec_helper.rb').code_type).to eq(:ruby) end it "finds files in a relative directory with '.rb' omitted" do expect(described_class.from_file('../spec_helper').code_type).to eq(:ruby) end it "doesn't confuse files with the same name, but without an extension" do expect(described_class.from_file('cat_load_path').code_type).to eq(:unknown) end it "doesn't confuse files with the same name, but with an extension" do expect(described_class.from_file('cat_load_path.rb').code_type).to eq(:ruby) end it "recognizes Gemfile as a Ruby file" do expect(described_class.from_file('Gemfile').code_type).to eq(:ruby) end end end describe ".from_method" do it "reads lines from a method's definition" do method = Pry::Method.from_obj(described_class, :from_method) expect(described_class.from_method(method).length).to be > 0 end end describe ".from_module" do it "reads line from a class" do expect(described_class.from_module(described_class).length).to be > 0 end it "sets code type to :ruby" do expect(described_class.from_module(described_class).code_type).to eq(:ruby) end end describe "#push" do it "is an alias of #<<" do expect(subject.method(:push)).to eq(subject.method(:<<)) end it "appends lines to the code" do subject.push('1') subject.push('1') expect(subject.length).to eq(2) end end describe "#select" do it "returns a code object" do expect(subject.select {}).to be_a(described_class) end it "selects lines matching a condition" do subject.push('matching-foo') subject.push('nonmatching-bar') subject.push('matching-baz') selected = subject.select do |line_of_code| line_of_code.line.start_with?('matching') end expect(selected.lines).to eq(["matching-foo\n", "matching-baz\n"]) end end describe "#reject" do it "returns a code object" do expect(subject.reject {}).to be_a(described_class) end it "rejects lines matching a condition" do subject.push('matching-foo') subject.push('nonmatching-bar') subject.push('matching-baz') selected = subject.reject do |line_of_code| line_of_code.line.start_with?('matching') end expect(selected.lines).to eq(["nonmatching-bar\n"]) end end describe "#between" do before { 4.times { |i| subject.push((i + 1).to_s) } } context "when start_line is nil" do it "returns self" do expect(subject.between(nil)).to eql(subject) end end context "when both start_line and end_line are specified" do it "returns a code object" do expect(subject.between(1)).to be_a(described_class) end it "removes all lines that aren't in the given range" do expect(subject.between(2, 3).lines).to eq(%W[2\n 3\n]) end end context "when only start_line is specified" do it "returns a code object" do expect(subject.between(1)).to be_a(described_class) end it "removes leaves only the specified line" do expect(subject.between(2).lines).to eq(%W[2\n]) end end context "when a negative start_line is specified" do it "returns a line from the end" do expect(subject.between(-1).lines).to eq(%W[4\n]) end end context "when a negative end_line is specified" do it "returns a range of lines from the end" do expect(subject.between(2, -2).lines).to eq(%W[2\n 3\n]) end end context "when start_line is a Range" do it "returns a range fo lines corresponding to the given Range" do expect(subject.between(2..3).lines).to eq(%W[2\n 3\n]) end end end describe "#take_lines" do before { 4.times { |i| subject.push((i + 1).to_s) } } it "takes N lines from start_line" do expect(subject.take_lines(3, 2).lines).to eq(%W[3\n 4\n]) end end describe "#before" do before { 4.times { |i| subject.push((i + 1).to_s) } } context "when line number is nil" do it "returns self" do expect(subject.before(nil)).to eql(subject) end end context "when line number is an integer" do it "selects one line before the specified line number" do expect(subject.before(4).lines).to eql(%W[3\n]) end context "and we specify how many lines to select" do it "selects more than 1 line before" do expect(subject.before(4, 2).lines).to eql(%W[2\n 3\n]) end end end end describe "#around" do before { 10.times { |i| subject.push((i + 1).to_s) } } context "when line number is nil" do it "returns self" do expect(subject.around(nil)).to eql(subject) end end context "when line number is an integer" do it "selects one line around the specified line number" do expect(subject.around(2).lines).to eql(%W[1\n 2\n 3\n]) end context "and we specify how many lines to select" do it "selects more than 1 line around" do expect(subject.around(4, 2).lines).to eql(%W[2\n 3\n 4\n 5\n 6\n]) end end end end describe "#after" do before { 4.times { |i| subject.push((i + 1).to_s) } } context "when line number is nil" do it "returns self" do expect(subject.after(nil)).to eql(subject) end end context "when line number is an integer" do it "selects one line around the specified line number" do expect(subject.after(2).lines).to eql(%W[3\n]) end context "and we specify how many lines to select" do it "selects more than 1 line around" do expect(subject.after(2, 2).lines).to eql(%W[3\n 4\n]) end end end end describe "#grep" do context "when pattern is nil" do it "returns self" do expect(subject.grep(nil)).to eql(subject) end end context "when pattern is specified" do subject do described_class.new(%w[matching-line nonmatching-line matching-line]) end it "returns lines matching the pattern" do matching_code = subject.grep(/\Amatching/) expect(matching_code.lines).to eq(["matching-line\n", "matching-line\n"]) end end end describe "#with_line_numbers" do subject { described_class.new(%w[1 2]) } it "appends line numbers to code" do code = subject.with_line_numbers(true) expect(code.lines).to eq(["1: 1\n", "2: 2\n"]) end end describe "#with_marker" do subject { described_class.new(%w[1 2]) } it "shows a marker in the right place" do code = subject.with_marker(2) expect(code.lines).to eq([" 1\n", " => 2\n"]) end end describe "#with_indentation" do subject { described_class.new(%w[1]) } it "indents lines" do code = subject.with_indentation(3) expect(code.lines).to eq([" 1\n"]) end end describe "#max_lineno_width" do context "when there are less than 10 lines" do before { 9.times { |i| subject.push((i + 1).to_s) } } it "returns 1" do expect(subject.max_lineno_width).to eq(1) end end context "when there are less than 100 lines" do before { 99.times { |i| subject.push((i + 1).to_s) } } it "returns 2" do expect(subject.max_lineno_width).to eq(2) end end context "when there are less than 1000 lines" do before { 999.times { |i| subject.push((i + 1).to_s) } } it "returns 3" do expect(subject.max_lineno_width).to eq(3) end end end describe "#to_s" do subject { described_class.new(%w[1 2 3]) } it "returns a string representation of code" do expect(subject.to_s).to eq("1\n2\n3\n") end end describe "#highlighted" do subject { described_class.new(%w[1]) } it "returns a highlighted for terminal string representation of code" do expect(subject.highlighted).to eq("\e[1;34m1\e[0m\n") end end describe "#comment_describing" do subject { described_class.new(['# foo', '1']) } it "returns a comment describing expression" do expect(subject.comment_describing(2)).to eq("# foo\n") end end describe "#expression_at" do subject { described_class.new(['def foo', ' :test', 'end']) } it "returns a multiline expressiong starting on the given line number" do expect(subject.expression_at(1)).to eq("def foo\n :test\nend\n") end end describe "#nesting_at" do subject do described_class.new( [ 'module TestModule', ' class TestClass', ' def foo', ' :test', ' end', ' end', 'end' ] ) end it "returns an Array of open modules" do expect(subject.nesting_at(5)).to eq(['module TestModule', 'class TestClass']) end end describe "#raw" do context "when code has a marker" do subject { described_class.new([':test']).with_marker } it "returns an unformatted String of all lines" do expect(subject.raw).to eq(":test\n") end end end describe "#length" do it "returns how many lines the code object has" do expect(subject.length).to be_zero end end describe "#==" do context "when an empty code is compared with another empty code" do it "returns true" do other_code = described_class.new expect(subject).to eq(other_code) end end context "when a code is compared with another code with identical lines" do subject { described_class.new(%w[line1 line2 baz]) } it "returns true" do other_code = described_class.new(%w[line1 line2 baz]) expect(subject).to eq(other_code) end end context "when a code is compared with another code with different lines" do subject { described_class.new(%w[foo bar baz]) } it "returns true" do other_code = described_class.new(%w[bingo bango bongo]) expect(subject).not_to eq(other_code) end end end describe "#method_missing" do context "when a String responds to the given method" do it "forwards the method to a String instance" do expect(subject.upcase).to eq('') end end context "when a String does not respond to the given method" do it "raises NoMethodError" do expect { subject.abcdefg } .to raise_error(NoMethodError, /undefined method `abcdefg'/) end end end describe "#respond_to_missing?" do context "when a String responds to the given method" do it "finds the method that is not defined on self" do expect(subject).to respond_to(:upcase) expect(subject.method(:upcase)).to be_a(Method) end end context "when a String does not respond to the given method" do it "doesn't find the method" do expect(subject).not_to respond_to(:abcdefg) expect { subject.method(:abcdefg) }.to raise_error(NameError) end end end end pry-0.13.1/spec/color_printer_spec.rb000066400000000000000000000057261364454223300176000ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe Pry::ColorPrinter do let(:output) { StringIO.new } describe ".default" do let(:output) { StringIO.new } let(:pry_instance) { Pry.new(output: output) } it "prints output prefix with value" do described_class.default(StringIO.new, 'foo', pry_instance) expect(output.string).to eq("=> \"foo\"\n") end end describe ".pp" do context "when no exception is raised in #inspect" do let(:healthy_class) do Class.new do def inspect 'string' end end end it "prints a string with a newline" do described_class.pp(healthy_class.new, output) expect(output.string).to eq("string\n") end end context "when an exception is raised in #inspect" do let(:broken_class) do Class.new do def inspect raise end end end it "still prints a string" do described_class.pp(broken_class.new, output) expect(output.string) .to match(/\A\e\[32m#<#:0x.+>\e\[0m\e\[0m\n\z/) end end context "when printing a BasicObject" do it "prints a string" do described_class.pp(BasicObject.new, output) expect(output.string) .to match(/\A\e\[32m#\e\[0m\e\[0m\n\z/) end end context "when #inspect returns an object literal" do let(:klass) do Class.new do def inspect '#' end end end it "prints the object inspect" do described_class.pp(klass.new, output) expect(output.string).to eq("\e[32m#\e[0m\n") end context "and when SyntaxHighlighter returns a token starting with '\e'" do before do expect(Pry::SyntaxHighlighter).to receive(:keyword_token_color) .and_return("\e[32m") end it "prints the object as is" do described_class.pp(klass.new, output) expect(output.string).to eq("\e[32m#\e[0m\n") end end context "and when SyntaxHighlighter returns a token that doesn't start with '\e'" do before do expect(Pry::SyntaxHighlighter).to receive(:keyword_token_color) .and_return('token') end it "prints the object with escape characters" do described_class.pp(klass.new, output) expect(output.string) .to eq("\e[0m\e[0;tokenm#\e[0m\n") end end end context "when #inspect raises Pry::Pager::StopPaging" do let(:klass) do Class.new do def inspect raise Pry::Pager::StopPaging end end end it "propagates the error" do expect { described_class.pp(klass.new, output) } .to raise_error(Pry::Pager::StopPaging) end end end end pry-0.13.1/spec/command_integration_spec.rb000066400000000000000000000400431364454223300207270ustar00rootroot00000000000000# frozen_string_literal: true describe "commands" do before do @str_output = StringIO.new @o = Object.new # Shortcuts. They save a lot of typing. @bs1 = "Pad.bs1 = pry_instance.binding_stack.dup" @bs2 = "Pad.bs2 = pry_instance.binding_stack.dup" @bs3 = "Pad.bs3 = pry_instance.binding_stack.dup" @self = "Pad.self = self" @command_tester = Pry::CommandSet.new do command "command1", "command 1 test" do output.puts "command1" end command "command2", "command 2 test" do |arg| output.puts arg end end Pad.bong = "bong" end after do Pad.clear Pry.reset_defaults end describe "alias_command" do it 'should make an aliasd command behave like its original' do set = Pry::CommandSet.new do command "test-command" do output.puts "testing 1, 2, 3" end alias_command "test-alias", "test-command" end pry_tester(commands: set).tap do |t| expect(t.eval('test-command')).to eq t.eval('test-alias') end end it 'should pass on arguments to original' do set = Pry::CommandSet.new do command "test-command" do |*args| output.puts "testing #{args.join(' ')}" end alias_command "test-alias", "test-command" end t = pry_tester(commands: set) t.process_command "test-alias hello baby duck" expect(t.last_output).to match(/testing hello baby duck/) end it 'should pass option arguments to original' do set = Pry::CommandSet.new do import Pry::Commands alias_command "test-alias", "ls" end obj = Class.new { @x = 10 } t = pry_tester(obj, commands: set) t.process_command "test-alias -i" expect(t.last_output).to match(/@x/) end it 'should pass option arguments to original with additional parameters' do set = Pry::CommandSet.new do import Pry::Commands alias_command "test-alias", "ls -M" end obj = Class.new { @x = Class.new { define_method(:plymouth) {} } } t = pry_tester(obj, commands: set) t.process_command "test-alias @x" expect(t.last_output).to match(/plymouth/) end it 'should be able to alias a regex command' do set = Pry::CommandSet.new do command(/du.k/) do output.puts "ducky" end alias_command "test-alias", "duck" end t = pry_tester(commands: set) t.process_command "test-alias" expect(t.last_output).to match(/ducky/) end it 'should be able to make the alias a regex' do set = Pry::CommandSet.new do command(/du.k/) do output.puts "ducky" end alias_command(/test-ali.s/, "duck") end redirect_pry_io(InputTester.new("test-alias"), out1 = StringIO.new) do Pry.start self, commands: set end expect(out1.string).to match(/ducky/) end end describe "Pry::Command#run" do it 'should allow running of commands with following whitespace' do set = Pry::CommandSet.new do import Pry::Commands command "test-run" do run "cd / " end end redirect_pry_io(InputTester.new("cd 1/2/3/4/5/6", @bs1, "test-run", @self, @bs2, "exit-all")) do Pry.start(@o, commands: set) end expect(Pad.bs1.size).to eq 7 expect(Pad.self).to eq @o expect(Pad.bs2.size).to eq 1 end it 'should allow running of cd command when contained in a single string' do set = Pry::CommandSet.new do import Pry::Commands command "test-run" do run "cd /" end end redirect_pry_io(InputTester.new("cd 1/2/3/4/5/6", @bs1, "test-run", @self, @bs2, "exit-all")) do Pry.start(@o, commands: set) end expect(Pad.bs1.size).to eq 7 expect(Pad.self).to eq @o expect(Pad.bs2.size).to eq 1 end it 'should allow running of cd command when split into array' do set = Pry::CommandSet.new do import Pry::Commands command "test-run" do run "cd", "/" end end redirect_pry_io(InputTester.new("cd 1/2/3/4/5/6", @bs1, "test-run", @self, @bs2, "exit-all")) do Pry.start(@o, commands: set) end expect(Pad.bs1.size).to eq 7 expect(Pad.self).to eq @o expect(Pad.bs2.size).to eq 1 end it 'should run a command from within a command' do klass = Pry::CommandSet.new do command "v" do output.puts "v command" end command "run_v" do run "v" end end expect(pry_tester(commands: klass).eval('run_v')).to match(/v command/) end it 'should run a regex command from within a command' do klass = Pry::CommandSet.new do command(/v(.*)?/) do |arg| output.puts "v #{arg}" end command "run_v" do run "vbaby" end end expect(pry_tester(commands: klass).eval('run_v')).to match(/v baby/) end it 'should run a command from within a command with arguments' do klass = Pry::CommandSet.new do command(/v(\w+)/) do |arg1, arg2| output.puts "v #{arg1} #{arg2}" end command "run_v_explicit_parameter" do run "vbaby", "param" end command "run_v_embedded_parameter" do run "vbaby param" end end %w[run_v_explicit_parameter run_v_embedded_parameter].each do |cmd| expect(pry_tester(commands: klass).eval(cmd)).to match(/v baby param/) end end end describe "Pry#run_command" do it 'should run a command that modifies the passed in eval_string' do p = Pry.new(output: @str_output) p.eval "def hello\npeter pan\n" p.run_command "amend-line !" expect(p.eval_string).to match(/def hello/) expect(p.eval_string).not_to match(/peter pan/) end it 'should run a command in the context of a session' do pry_tester(Object.new).tap do |t| t.eval "@session_ivar = 10", "pry_instance.run_command('ls')" expect(t.last_output).to match(/@session_ivar/) end end end it 'should interpolate ruby code into commands' do set = Pry::CommandSet.new do command "hello", "", keep_retval: true do |arg| arg end end # rubocop:disable Lint/InterpolationCheck expect(pry_tester(commands: set).eval('hello #{Pad.bong}')).to match(/bong/) # rubocop:enable Lint/InterpolationCheck end # bug fix for https://github.com/pry/pry/issues/170 it( "doesn't choke on complex string interpolation when checking if ruby code " \ "is a command" ) do redirect_pry_io( # rubocop:disable Lint/InterpolationCheck InputTester.new('/#{Regexp.escape(File.expand_path("."))}/'), # rubocop:enable Lint/InterpolationCheck @str_output ) do pry end expect(@str_output.string).not_to match(/SyntaxError/) end it 'should NOT interpolate ruby code into commands if :interpolate => false' do set = Pry::CommandSet.new do command "hello", "", keep_retval: true, interpolate: false do |arg| arg end end # rubocop:disable Lint/InterpolationCheck expect(pry_tester(commands: set).eval('hello #{Pad.bong}')) .to match(/Pad\.bong/) # rubocop:enable Lint/InterpolationCheck end it 'should NOT try to interpolate pure ruby code (no commands) ' do # rubocop:disable Lint/InterpolationCheck # These should raise RuntimeError instead of NameError expect { pry_eval 'raise \'#{aggy}\'' }.to raise_error RuntimeError expect { pry_eval 'raise #{aggy}' }.to raise_error RuntimeError expect(pry_eval('format \'#{my_var}\'')).to eq "\#{my_var}" # rubocop:enable Lint/InterpolationCheck end it 'should create a command with a space in its name zzz' do set = Pry::CommandSet.new do command "hello baby", "" do output.puts "hello baby command" end end expect(pry_tester(commands: set).eval('hello baby')) .to match(/hello baby command/) end it 'should create a command with a space in its name and pass an argument' do set = Pry::CommandSet.new do command "hello baby", "" do |arg| output.puts "hello baby command #{arg}" end end expect(pry_tester(commands: set).eval('hello baby john')) .to match(/hello baby command john/) end it 'should create a regex command and be able to invoke it' do set = Pry::CommandSet.new do command(/hello(.)/, "") do c = captures.first output.puts "hello#{c}" end end expect(pry_tester(commands: set).eval('hello1')).to match(/hello1/) end it( 'creates a regex command and passes captures into the args list ' \ 'before regular arguments' ) do set = Pry::CommandSet.new do command(/hello(.)/, "") do |c1, a1| output.puts "hello #{c1} #{a1}" end end expect(pry_tester(commands: set).eval('hello1 baby')).to match(/hello 1 baby/) end it 'should create a regex command and interpolate the captures' do set = Pry::CommandSet.new do command(/hello (.*)/, "") do |c1| output.puts "hello #{c1}" end end bong = "bong" expect(pry_tester(binding, commands: set).eval("hello #{bong}")) .to match(/hello #{bong}/) end it 'should create a regex command and arg_string should be interpolated' do set = Pry::CommandSet.new do command(/hello(\w+)/, "") do |c1, a1, a2, a3| output.puts "hello #{c1} #{a1} #{a2} #{a3}" end end bing = 'bing' bong = 'bong' bang = 'bang' expect(pry_tester(binding, commands: set) .eval("hellojohn #{bing} #{bong} #{bang}")) .to match(/hello john #{bing} #{bong} #{bang}/) end it 'if a regex capture is missing it should be nil' do set = Pry::CommandSet.new do command(/hello(.)?/, "") do |c1, a1| output.puts "hello #{c1.inspect} #{a1}" end end expect(pry_tester(commands: set).eval('hello baby')).to match(/hello nil baby/) end it( 'creates a command in a nested context and that command should ' \ 'be accessible from the parent' ) do tester = pry_tester(Object.new) expect(tester.eval(*<<-RUBY.split("\n"))).to match(/instance variables:\s+@x/m) @x = nil cd 7 pry_instance.commands.instance_eval { command('bing') { |arg| run arg } } cd .. bing ls RUBY end it 'should define a command that keeps its return value' do klass = Pry::CommandSet.new do command "hello", "", keep_retval: true do :kept_hello end end t = pry_tester(commands: klass) t.eval("hello\n") expect(t.last_command_result).to eq :kept_hello end it 'should define a command that does NOT keep its return value' do klass = Pry::CommandSet.new do command "hello", "", keep_retval: false do :kept_hello end end t = pry_tester(commands: klass) expect(t.eval("hello\n")).to eq '' expect(t.last_command_result).to eq Pry::Command::VOID_VALUE end it 'should define a command that keeps its return value even when nil' do klass = Pry::CommandSet.new do command "hello", "", keep_retval: true do nil end end t = pry_tester(commands: klass) t.eval("hello\n") expect(t.last_command_result).to eq nil end it( 'should define a command that keeps its return value but does not ' \ 'return when value is void' ) do klass = Pry::CommandSet.new do command "hello", "", keep_retval: true do void end end expect(pry_tester(commands: klass).eval("hello\n").empty?).to eq true end it( "a command (with :keep_retval => false) that replaces eval_string with a " \ "valid expression doesn't have the expression value suppressed" ) do klass = Pry::CommandSet.new do command "hello", "" do eval_string.replace("6") end end redirect_pry_io(InputTester.new('def yo', 'hello'), @str_output) do Pry.start self, commands: klass end expect(@str_output.string).to match(/6/) end it( 'a command (with :keep_retval => true) that replaces eval_string with ' \ 'a valid expression overwrites the eval_string with the return value' ) do klass = Pry::CommandSet.new do command "hello", "", keep_retval: true do 7 end end expect(pry_tester(commands: klass).eval("def yo", "hello")).to eq 7 end it( 'a command that return a value in a multi-line expression clears ' \ 'the expression and return the value' ) do klass = Pry::CommandSet.new do command "hello", "", keep_retval: true do 5 end end expect(pry_tester(commands: klass).eval("def yo", "hello\n")).to eq 5 end it 'should set the commands default, and the default should be overridable' do klass = Pry::CommandSet.new do command "hello" do output.puts "hello world" end end other_klass = Pry::CommandSet.new do command "goodbye", "" do output.puts "goodbye world" end end Pry.config.commands = klass expect(pry_tester.eval("hello")).to eq "hello world\n" expect(pry_tester(commands: other_klass).eval("goodbye")).to eq "goodbye world\n" end it 'should inherit commands from Pry::Commands' do klass = Pry::CommandSet.new Pry::Commands do command "v" do end end expect(klass.to_hash.include?("nesting")).to eq true expect(klass.to_hash.include?("jump-to")).to eq true expect(klass.to_hash.include?("cd")).to eq true expect(klass.to_hash.include?("v")).to eq true end it 'should change description of a command using desc' do klass = Pry::CommandSet.new do import Pry::Commands end orig = klass["help"].description klass.instance_eval do desc "help", "blah" end commands = klass.to_hash expect(commands["help"].description).not_to eq orig expect(commands["help"].description).to eq "blah" end it( 'enables an inherited method to access opts, output and target, ' \ 'due to instance_exec' ) do klass = Pry::CommandSet.new do command "v" do output.puts target.eval('self').to_s end end child_klass = Pry::CommandSet.new klass do end mock_pry(Pry.binding_for('john'), "v", print: proc {}, commands: child_klass, output: @str_output) expect(@str_output.string).to eq "john\n" end it 'should import commands from another command object' do klass = Pry::CommandSet.new do import_from Pry::Commands, "ls", "jump-to" end expect(klass.to_hash.include?("ls")).to eq true expect(klass.to_hash.include?("jump-to")).to eq true end it 'should delete some inherited commands when using delete method' do klass = Pry::CommandSet.new Pry::Commands do command "v" do end delete "show-doc", "show-method" delete "ls" end commands = klass.to_hash expect(commands.include?("nesting")).to eq true expect(commands.include?("jump-to")).to eq true expect(commands.include?("cd")).to eq true expect(commands.include?("v")).to eq true expect(commands.include?("show-doc")).to eq false expect(commands.include?("show-method")).to eq false expect(commands.include?("ls")).to eq false end it 'should override some inherited commands' do klass = Pry::CommandSet.new Pry::Commands do command "jump-to" do output.puts "jump-to the music" end command "help" do output.puts "help to the music" end end t = pry_tester(commands: klass) expect(t.eval('jump-to')).to eq "jump-to the music\n" expect(t.eval('help')).to eq "help to the music\n" end it 'should run a command with no parameter' do expect(pry_tester(commands: @command_tester).eval('command1')) .to eq "command1\n" end it 'should run a command with one parameter' do expect(pry_tester(commands: @command_tester).eval('command2 horsey')) .to eq "horsey\n" end end pry-0.13.1/spec/command_set_spec.rb000066400000000000000000000275301364454223300172050ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe Pry::CommandSet do let(:set) do Pry::CommandSet.new { import(Pry::Commands) } end describe "#new" do it "merges other set with itself" do other_set = described_class.new other_set.command('test') {} expect(described_class.new(other_set).count).to eq(1) end context "when block given" do it "instance evals the block" do other = described_class.new do command('test') {} end expect(described_class.new(other).count).to eq(1) end end end describe "#block_command" do it "defines a new command" do subject.block_command('test') expect(subject.count).to eq(1) end it "assings default description" do command = subject.block_command('test') expect(command.description).to eq('No description.') end it "can overwrite default description" do command = subject.block_command('test', 'test description') expect(command.description).to eq('test description') end it "configures command options" do command = subject.block_command( 'test', 'test description', some_option: 'some value' ) expect(command.options).to include(some_option: 'some value') end context "when description is a hash" do it "treats description as options" do command = subject.block_command('test', some_option: 'some value') expect(command.options).to include(some_option: 'some value') end end end describe "#create_command" do it "defines a new class command" do subject.create_command('test') {} expect(subject.count).to eq(1) end it "assings default description" do command = subject.create_command('test') {} expect(command.description).to eq('No description.') end it "can overwrite default description" do command = subject.create_command('test', 'test description') {} expect(command.description).to eq('test description') end it "configures command options" do command = subject.create_command( 'test', 'test description', some_option: 'some value' ) {} expect(command.options).to include(some_option: 'some value') end it "class_evals the given block in the command context" do command = subject.create_command('test') do description('class eval description') end expect(command.description).to eq('class eval description') end context "when description is a hash" do it "treats description as options" do command = subject.create_command('test', some_option: 'some value') {} expect(command.options).to include(some_option: 'some value') end end end describe "#each" do it "iterates over commands" do subject.command('test') expect(subject.each.first.first).to eq('test') end end describe "#delete" do it "deletes given commands" do subject.command('peach') subject.command('kiwi') subject.command('apple') subject.delete('kiwi', 'apple') expect(subject.count).to eq(1) end end describe "#import" do let(:first_set) { described_class.new.tap { |set| set.command('first') } } let(:second_set) { described_class.new.tap { |set| set.command('second') } } it "imports commands from given sets" do subject.import(first_set, second_set) expect(subject.count).to eq(2) end it "returns self" do expect(subject.import(first_set)).to eql(subject) end it "includes given sets' helper modules" do subject.import(first_set, second_set) expect(subject.helper_module.ancestors.size).to eq(3) end end describe "#import_from" do let(:other_set) do set = described_class.new set.command('kiwi') set.command('peach') set.command('plum') set end it "imports matching command from a set" do subject.import_from(other_set, 'kiwi', 'peach') expect(subject.count).to eq(2) end it "returns self" do expect(subject.import_from(other_set)).to eql(subject) end it "includes other set's helper module" do subject.import_from(other_set) expect(subject.helper_module.ancestors.size).to eq(2) end end describe "#find_command_by_match_or_listing" do it "returns a matching by name command" do subject.command('test') command = subject.find_command_by_match_or_listing('test') expect(command.command_name).to eq('test') end it "returns a matching by listing command" do subject.command('test', listing: 'wtf') command = subject.find_command_by_match_or_listing('wtf') expect(command.command_name).to eq('wtf') end it "raises ArgumentError on non-matching command" do expect { subject.find_command_by_match_or_listing('test') } .to raise_error(ArgumentError, "cannot find a command: 'test'") end end describe "#alias_command" do before { subject.command('test') } it "returns the aliased command" do new_command = subject.alias_command('new-test', 'test') expect(new_command.command_name).to eq('new-test') end it "sets description for the aliased command automatically" do new_command = subject.alias_command('new-test', 'test') expect(new_command.description).to eq('Alias for `test`') end it "sets aliased command's listing for string alias" do new_command = subject.alias_command('new-test', 'test') expect(new_command.options).to include(listing: 'new-test') end it "sets aliased command's listing for regex alias" do new_command = subject.alias_command(/test[!?]+/, 'test') expect(new_command.options[:listing].to_s).to eq('/test[!?]+/') end it "sets group for the aliased command automatically" do new_command = subject.alias_command('new-test', 'test') expect(new_command.group).to eq('Aliases') end context "when string description is provided" do it "uses the given description for the aliased command" do new_command = subject.alias_command('new-test', 'test', desc: 'description') expect(new_command.description).to eq('description') end end context "when non-string description is provided" do it "uses the string representation of the given object" do new_command = subject.alias_command('new-test', 'test', desc: Object.new) expect(new_command.description).to match(/# respond_to(:command_name)) end it "doesn't mutate original commands" do hash = subject.to_hash hash['foo'] = 'bar' expect(subject.to_hash).not_to include('foo') end end describe "#[]" do context "when there's an unambiguous command" do before { subject.command('test') } it "selects the command according to the given pattern" do expect(subject['test']).to respond_to(:command_name) end end context "when there's an ambiguous command" do before do subject.command(/\.(.*)/) subject.command(/\.*(.*)/) end it "prefers a command with a higher score" do expect(subject['.foo'].command_name).to eq("/\\.(.*)/") expect(subject['..foo'].command_name).to eq("/\\.*(.*)/") end end end describe "#[]=" do before { subject.command('test') } it "rebinds the command with key" do subject['test-1'] = subject['test'] expect(subject['test-1'].match).to eq('test-1') end context "when given command is nil" do it "deletes the command matching the pattern" do subject['test'] = nil expect(subject.count).to be_zero end end context "when given command is not a subclass of Pry::Command" do it "raises TypeError" do expect { subject['test'] = 1 } .to raise_error(TypeError, 'command is not a subclass of Pry::Command') end end end describe "#add_command" do it "adds a command" do subject.add_command(Class.new(Pry::Command)) expect(subject.count).to eq(1) end end describe "#find_command_for_help" do before { subject.command('test') } context "when the command can be found" do it "returns the command" do expect(subject.find_command_for_help('test')).to respond_to(:command_name) end end context "when the command cannot be found" do it "returns nil" do expect(subject.find_command_for_help('foo')).to be_nil end end end describe "#valid_command?" do before { subject.command('test') } context "when command can be found" do it "returns true" do expect(subject.valid_command?('test')).to be_truthy end end context "when command cannot be found" do it "returns false" do expect(subject.valid_command?('foo')).to be_falsey end end end describe "#process_line" do before { subject.command('test') {} } context "when the given line is a command" do it "returns a command" do expect(subject.process_line('test')).to be_command end it "returns a non-void command" do expect(subject.process_line('test')).to be_void_command end context "and context is provided" do before { subject.command('test') { output.puts('kiwi') } } it "passes the context to the command" do output = StringIO.new subject.process_line('test', output: output) expect(output.string).to eq("kiwi\n") end end end context "when the given line is not a command" do it "returns not a command" do expect(subject.process_line('abcdefg')).not_to be_command end it "returns a void result" do expect(subject.process_line('test')).to be_void_command end end end # TODO: rewrite this block. if defined?(Bond) describe "#complete" do it "should list all command names" do set.create_command('susan') {} expect(set.complete('sus')).to.include 'susan ' end it "should delegate to commands" do set.create_command('susan') do def complete(_search) ['--foo'] end end expect(set.complete('susan ')).to eq ['--foo'] end end end end pry-0.13.1/spec/command_spec.rb000066400000000000000000000441051364454223300163270ustar00rootroot00000000000000# frozen_string_literal: true require 'stringio' RSpec.describe Pry::Command do subject do Class.new(described_class) do def process; end end end let(:default_options) do { argument_required: false, interpolate: true, keep_retval: false, shellwords: true, takes_block: false, use_prefix: true, listing: 'nil' } end describe ".match" do context "when no argument is given" do context "and when match was defined previously" do before { subject.match('old-match') } it "doesn't overwrite match" do expect(subject.match).to eq('old-match') end end context "and when match was not defined previously" do it "sets match to nil" do subject.match expect(subject.match).to be_nil end end end context "when given an argument" do context "and when match is a string" do it "sets command options with listing as match" do subject.match('match') # rubocop:disable Performance/RedundantMatch expect(subject.command_options).to include(listing: 'match') end end context "and when match is an object" do let(:object) do obj = Object.new def obj.inspect 'inspect' end obj end it "sets command options with listing as object's inspect" do subject.match(object) expect(subject.command_options).to include(listing: 'inspect') end end end end describe ".description" do context "and when description was defined previously" do before { subject.description('old description') } it "doesn't overwrite match" do subject.description expect(subject.description).to eq('old description') end end context "and when description was not defined previously" do it "sets description to nil" do expect(subject.description).to be_nil end end context "when given an argument" do it "sets description" do subject.description('description') expect(subject.description).to eq('description') end end end describe ".command_options" do context "when no argument is given" do context "and when command options were defined previously" do before { subject.command_options(foo: :bar) } it "returns memoized command options" do expect(subject.command_options).to eq(default_options.merge(foo: :bar)) end end context "and when command options were not defined previously" do it "sets command options to default options" do subject.command_options expect(subject.command_options).to eq(default_options) end end end context "when given an argument" do let(:new_option) { { new_option: 'value' } } it "merges the argument with command options" do expect(subject.command_options(new_option)) .to eq(default_options.merge(new_option)) end end end describe ".banner" do context "when no argument is given" do context "and when banner was defined previously" do before { subject.banner('banner') } it "returns the memoized banner" do expect(subject.banner).to eq('banner') end end context "and when banner was not defined previously" do it "return nil" do subject.banner expect(subject.banner).to be_nil end end end context "when given an argument" do it "merges the argument with command options" do expect(subject.banner('banner')).to eq('banner') end end end describe ".block" do context "when block exists" do let(:block) { proc {} } it "returns the block" do subject.block = block expect(subject.block).to eql(block) end end context "when block doesn't exist" do it "uses #process method" do expect(subject.block.name).to eq(:process) end end end describe ".source" do it "returns source code of the method" do expect(subject.source).to eq("def process; end\n") end end describe ".doc" do subject do Class.new(described_class) do def help 'help' end end end it "returns help output" do expect(subject.doc).to eq('help') end end describe ".source_file" do it "returns source file" do expect(subject.source_file).to match(__FILE__) end end describe ".source_line" do it "returns source line" do expect(subject.source_line).to be_kind_of(Integer) end end describe ".default_options" do context "when given a String argument" do it "returns default options with string listing" do expect(subject.default_options('listing')) .to eq(default_options.merge(listing: 'listing')) end end context "when given an Object argument" do let(:object) do obj = Object.new def obj.inspect 'inspect' end obj end it "returns default options with object's inspect as listing" do expect(subject.default_options(object)) .to eq(default_options.merge(listing: 'inspect')) end end end describe ".name" do it "returns the name of the command" do expect(subject.name).to eq('#') end context "when super command name exists" do subject do parent = Class.new(described_class) do def name 'parent name' end end Class.new(parent) end it "returns the name of the parent command" do expect(subject.name).to eq('#') end end end describe ".inspect" do subject do Class.new(described_class) do def self.name 'name' end end end it "returns command name" do expect(subject.inspect).to eq('name') end end describe ".command_name" do before { subject.match('foo') } it "returns listing" do expect(subject.command_name).to eq('foo') end end describe ".subclass" do it "returns a new class" do klass = subject.subclass('match', 'desc', {}, Module.new) expect(klass).to be_a(Class) expect(klass).not_to eql(subject) end it "includes helpers to the new class" do mod = Module.new { def foo; end } klass = subject.subclass('match', 'desc', {}, mod) expect(klass.new).to respond_to(:foo) end it "sets match on the new class" do klass = subject.subclass('match', 'desc', {}, Module.new) expect(klass.match).to eq('match') end it "sets description on the new class" do klass = subject.subclass('match', 'desc', {}, Module.new) expect(klass.description).to eq('desc') end it "sets command options on the new class" do klass = subject.subclass('match', 'desc', { foo: :bar }, Module.new) expect(klass.command_options).to include(foo: :bar) end it "sets block on the new class" do block = proc {} klass = subject.subclass('match', 'desc', { foo: :bar }, Module.new, &block) expect(klass.block).to eql(block) end end describe ".matches?" do context "when given value matches command regex" do before { subject.match('test-command') } it "returns true" do expect(subject.matches?('test-command')).to be_truthy end end context "when given value doesn't match command regex" do it "returns false" do expect(subject.matches?('test-command')).to be_falsey end end end describe ".match_score" do context "when command regex matches given value" do context "and when the size of last match is more than 1" do before { subject.match(/\.(.*)/) } it "returns the length of the first match" do expect(subject.match_score('.||')).to eq(1) end end context "and when the size of last match is 1 or 0" do before { subject.match('hi') } it "returns the length of the last match" do expect(subject.match_score('hi there')).to eq(2) end end end context "when command regex doesn't match given value" do it "returns -1" do expect(subject.match_score('test')).to eq(-1) end end end describe ".command_regex" do before { subject.match('test-command') } context "when use_prefix is true" do before { subject.command_options(use_prefix: true) } it "returns a Regexp without a prefix" do expect(subject.command_regex).to eq(/\Atest\-command(?!\S)/) end end context "when use_prefix is false" do before { subject.command_options(use_prefix: false) } it "returns a Regexp with a prefix" do expect(subject.command_regex).to eq(/\A(?:)?test\-command(?!\S)/) end end end describe ".convert_to_regex" do context "when given object is a String" do it "escapes the string as a Regexp" do expect(subject.convert_to_regex('foo.+')).to eq('foo\\.\\+') end end context "when given object is an Object" do let(:obj) { Object.new } it "returns the given object" do expect(subject.convert_to_regex(obj)).to eql(obj) end end end describe ".group" do context "when name is given" do it "sets group to that name" do expect(subject.group('Test Group')).to eq('Test Group') end end context "when source file matches a pry command" do before do expect_any_instance_of(Pry::Method).to receive(:source_file) .and_return('/pry/test_commands/test_command.rb') end it "sets group name to command name" do expect(subject.group).to eq('Test command') end end context "when source file matches a pry plugin" do before do expect_any_instance_of(Pry::Method).to receive(:source_file) .and_return('pry-test-1.2.3') end it "sets group name to plugin name" do expect(subject.group).to eq('pry-test (v1.2.3)') end end context "when source file matches 'pryrc'" do before do expect_any_instance_of(Pry::Method).to receive(:source_file) .and_return('pryrc') end it "sets group name to pryrc" do expect(subject.group).to eq('pryrc') end end context "when source file doesn't match anything" do it "returns '(other)'" do expect(subject.group).to eq('(other)') end end end describe ".state" do it "returns a command state" do expect(described_class.state).to be_an(OpenStruct) end end describe "#run" do let(:command_set) do set = Pry::CommandSet.new set.command('test') {} set end subject do command = Class.new(described_class) command.new(command_set: command_set, pry_instance: Pry.new) end it "runs a command from another command" do result = subject.run('test') expect(result).to be_command end end describe "#commands" do let(:command_set) do set = Pry::CommandSet.new set.command('test') do def process; end end set end subject do command = Class.new(described_class) command.new(command_set: command_set, pry_instance: Pry.new) end it "returns command set as a hash" do expect(subject.commands).to eq('test' => command_set['test']) end end describe "#void" do it "returns void value" do expect(subject.new.void).to eq(Pry::Command::VOID_VALUE) end end describe "#target_self" do let(:target) { binding } subject { Class.new(described_class).new(target: target) } it "returns the value of self inside the target binding" do expect(subject.target_self).to eq(target.eval('self')) end end describe "#state" do let(:target) { binding } subject { Class.new(described_class).new(pry_instance: Pry.new) } it "returns a state object" do expect(subject.state).to be_an(OpenStruct) end it "remembers the state" do subject.state.foo = :bar expect(subject.state.foo).to eq(:bar) end end describe "#interpolate_string" do context "when given string contains \#{" do let(:target) do foo = 'bar' binding end subject { Class.new(described_class).new(target: target) } it "returns the result of eval within target" do # rubocop:disable Lint/InterpolationCheck expect(subject.interpolate_string('#{foo}')).to eq('bar') # rubocop:enable Lint/InterpolationCheck end end context "when given string doesn't contain \#{" do it "returns the given string" do expect(subject.new.interpolate_string('foo')).to eq('foo') end end end describe "#check_for_command_collision" do let(:command_set) do set = Pry::CommandSet.new set.command('test') do def process; end end set end let(:output) { StringIO.new } subject do command = Class.new(described_class) command.new(command_set: command_set, target: target, output: output) end context "when a command collides with a local variable" do let(:target) do test = 'foo' binding end it "displays a warning" do subject.check_for_command_collision('test', '') expect(output.string) .to match("'test', which conflicts with a local-variable") end end context "when a command collides with a method" do let(:target) do def test; end binding end it "displays a warning" do subject.check_for_command_collision('test', '') expect(output.string).to match("'test', which conflicts with a method") end end context "when a command doesn't collide" do let(:target) do def test; end binding end it "doesn't display a warning" do subject.check_for_command_collision('nothing', '') expect(output.string).to be_empty end end end describe "#tokenize" do let(:target) { binding } let(:klass) { Class.new(described_class) } let(:target) { binding } subject { klass.new(target: target) } before { klass.match('test') } context "when given string uses interpolation" do let(:target) do foo = 4 binding end before { klass.command_options(interpolate: true) } it "interpolates the string in the target's context" do # rubocop:disable Lint/InterpolationCheck expect(subject.tokenize('test #{1 + 2} #{3 + foo}')) .to eq(['test', '3 7', [], %w[3 7]]) # rubocop:enable Lint/InterpolationCheck end context "and when interpolation is disabled" do before { klass.command_options(interpolate: false) } it "doesn't interpolate the string" do # rubocop:disable Lint/InterpolationCheck expect(subject.tokenize('test #{3 + foo}')) .to eq(['test', '#{3 + foo}', [], %w[#{3 + foo}]]) # rubocop:enable Lint/InterpolationCheck end end end context "when given string doesn't match a command" do it "raises CommandError" do expect { subject.tokenize('boom') } .to raise_error(Pry::CommandError, /command which didn't match/) end end context "when target is not set" do subject { klass.new } it "still returns tokens" do expect(subject.tokenize('test --help')) .to eq(['test', '--help', [], ['--help']]) end end context "when shellwords is enabled" do before { klass.command_options(shellwords: true) } it "strips quotes from the arguments" do expect(subject.tokenize(%(test "foo" 'bar' 1))) .to eq(['test', %("foo" 'bar' 1), [], %w[foo bar 1]]) end end context "when shellwords is disabled" do before { klass.command_options(shellwords: false) } it "doesn't split quotes from the arguments" do # rubocop:disable Lint/PercentStringArray expect(subject.tokenize(%(test "foo" 'bar' 1))) .to eq(['test', %("foo" 'bar' 1), [], %w["foo" 'bar' 1]]) # rubocop:enable Lint/PercentStringArray end end context "when command regex has captures" do before { klass.match(/perfectly (normal)( beast)/i) } it "returns the captures" do expect(subject.tokenize('Perfectly Normal Beast (honest!)')).to eq( [ 'Perfectly Normal Beast', '(honest!)', ['Normal', ' Beast'], ['(honest!)'] ] ) end end end describe "#process_line" do let(:klass) do Class.new(described_class) do def call(*args); end end end let(:target) do test = 4 binding end let(:output) { StringIO.new } subject { klass.new(target: target, output: output) } before { klass.match(/test(y)?/) } it "sets arg_string" do subject.process_line('test -v') expect(subject.arg_string).to eq('-v') end it "sets captures" do subject.process_line('testy') expect(subject.captures).to eq(['y']) end describe "collision warnings" do context "when collision warnings are configured" do before do expect(Pry.config).to receive(:collision_warning).and_return(true) end it "prints a warning when there's a collision" do subject.process_line('test') expect(output.string).to match(/conflicts with a local-variable/) end end context "when collision warnings are not set" do before do expect(Pry.config).to receive(:collision_warning).and_return(false) end it "prints a warning when there's a collision" do subject.process_line('test') expect(output.string).to be_empty end end end end describe "#complete" do it "returns empty array" do expect(subject.new.complete('')).to eq([]) end end end pry-0.13.1/spec/command_state_spec.rb000066400000000000000000000026651364454223300175340ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe Pry::CommandState do describe ".default" do it "returns the default command state" do expect(described_class.default).to be_a(described_class) end context "when called multiple times" do it "returns the same command state" do first_state = described_class.default second_state = described_class.default expect(first_state).to eql(second_state) end end end describe "#state_for" do it "returns a state for the matching command" do subject.state_for('command').foobar = 1 expect(subject.state_for('command').foobar).to eq(1) end it "returns new state for new command" do expect(subject.state_for('command')) .not_to equal(subject.state_for('other-command')) end it "memoizes state for the same command" do expect(subject.state_for('command')).to equal(subject.state_for('command')) end end describe "#reset" do it "resets the command state for the given command" do subject.state_for('command').foobar = 1 subject.reset('command') expect(subject.state_for('command').foobar).to be_nil end it "doesn't reset command state for other commands" do subject.state_for('command').foobar = 1 subject.state_for('other-command').foobar = 1 subject.reset('command') expect(subject.state_for('other-command').foobar).to eq(1) end end end pry-0.13.1/spec/commands/000077500000000000000000000000001364454223300151475ustar00rootroot00000000000000pry-0.13.1/spec/commands/amend_line_spec.rb000066400000000000000000000124451364454223300206070ustar00rootroot00000000000000# frozen_string_literal: true describe "amend-line" do before do @t = pry_tester end it 'should amend the last line of input when no line number specified' do @t.push(*unindent(<<-STR).split("\n")) def hello puts :bing STR @t.process_command 'amend-line puts :blah' expect(@t.eval_string).to eq unindent(<<-STR) def hello puts :blah STR end it 'should amend the specified line of input when line number given' do @t.push(*unindent(<<-STR).split("\n")) def hello puts :bing puts :bang STR @t.process_command 'amend-line 1 def goodbye' expect(@t.eval_string).to eq unindent(<<-STR) def goodbye puts :bing puts :bang STR end it 'should amend the first line of input when 0 given as line number' do @t.push(*unindent(<<-STR).split("\n")) def hello puts :bing puts :bang STR @t.process_command 'amend-line 0 def goodbye' expect(@t.eval_string).to eq unindent(<<-STR) def goodbye puts :bing puts :bang STR end it 'should amend a specified line when negative number given' do @t.push(*unindent(<<-STR).split("\n")) def hello puts :bing puts :bang STR @t.process_command 'amend-line -1 puts :bink' expect(@t.eval_string).to eq unindent(<<-STR) def hello puts :bing puts :bink STR @t.process_command 'amend-line -2 puts :bink' expect(@t.eval_string).to eq unindent(<<-STR) def hello puts :bink puts :bink STR end it 'should amend a range of lines of input when negative numbers given' do @t.push(*unindent(<<-STR).split("\n")) def hello puts :bing puts :bang puts :boat STR @t.process_command 'amend-line -3..-2 puts :bink' expect(@t.eval_string).to eq unindent(<<-STR) def hello puts :bink puts :boat STR end it 'should correctly amend the specified line with interpolated text' do @t.push(*unindent(<<-STR).split("\n")) def hello puts :bing puts :bang STR # rubocop:disable Lint/InterpolationCheck @t.process_command 'amend-line puts "#{goodbye}"' # rubocop:enable Lint/InterpolationCheck expect(@t.eval_string).to eq unindent(<<-'STR') def hello puts :bing puts "#{goodbye}" STR end it 'should display error if nothing to amend' do error = nil begin @t.process_command 'amend-line' rescue Pry::CommandError => e error = e end expect(error).not_to equal nil expect(error.message).to match(/No input to amend/) end it 'should correctly amend the specified range of lines' do @t.push(*unindent(<<-STR).split("\n")) def hello puts :bing puts :bang puts :heart STR @t.process_command 'amend-line 2..3 puts :bong' expect(@t.eval_string).to eq unindent(<<-STR) def hello puts :bong puts :heart STR end it 'should correctly delete a specific line using the ! for content' do @t.push(*unindent(<<-STR).split("\n")) def hello puts :bing puts :bang puts :boast puts :heart STR @t.process_command 'amend-line 3 !' expect(@t.eval_string).to eq unindent(<<-STR) def hello puts :bing puts :boast puts :heart STR end it 'should correctly delete a range of lines using the ! for content' do @t.push(*unindent(<<-STR).split("\n")) def hello puts :bing puts :bang puts :boast puts :heart STR @t.process_command 'amend-line 2..4 !' expect(@t.eval_string).to eq unindent(<<-STR) def hello puts :heart STR end it 'should correctly delete the previous line using the ! for content' do @t.push(*unindent(<<-STR).split("\n")) def hello puts :bing puts :bang puts :boast puts :heart STR @t.process_command 'amend-line !' expect(@t.eval_string).to eq unindent(<<-STR) def hello puts :bing puts :bang puts :boast STR end it 'should amend the specified range of lines, with numbers < 0 in range' do @t.push(*unindent(<<-STR).split("\n")) def hello puts :bing puts :bang puts :boast puts :heart STR @t.process_command 'amend-line 2..-2 puts :bong' expect(@t.eval_string).to eq unindent(<<-STR) def hello puts :bong puts :heart STR end it 'should correctly insert a line before a specified line using >' do @t.push(*unindent(<<-STR).split("\n")) def hello puts :bing puts :bang STR @t.process_command 'amend-line 2 > puts :inserted' expect(@t.eval_string).to eq unindent(<<-STR) def hello puts :inserted puts :bing puts :bang STR end it 'should ignore second value of range with > syntax' do @t.push(*unindent(<<-STR).split("\n")) def hello puts :bing puts :bang STR @t.process_command 'amend-line 2..21 > puts :inserted' expect(@t.eval_string).to eq unindent(<<-STR) def hello puts :inserted puts :bing puts :bang STR end end pry-0.13.1/spec/commands/bang_spec.rb000066400000000000000000000010001364454223300174040ustar00rootroot00000000000000# frozen_string_literal: true describe "!" do before do @t = pry_tester end it 'should correctly clear the input buffer ' do @t.push unindent(<<-STR) def hello puts :bing STR @t.process_command '!' expect(@t.last_output).to match(/Input buffer cleared!/) expect(@t.eval_string).to eq('') end it 'should not clear the input buffer for negation' do @t.push '! false' expect(@t.last_output).to match(/true/) expect(@t.eval_string).to eq('') end end pry-0.13.1/spec/commands/cat/000077500000000000000000000000001364454223300157165ustar00rootroot00000000000000pry-0.13.1/spec/commands/cat/file_formatter_spec.rb000066400000000000000000000064361364454223300222700ustar00rootroot00000000000000# frozen_string_literal: true describe Pry::Command::Cat::FileFormatter do before do @p = Pry.new @opt = Pry::Slop.new end describe "#file_and_line" do before do expect(Pry::Code).to receive(:from_file) end describe "windows filesystem" do it "parses '/'style absolute path without line_num" do file_with_embedded_line = "C:/Ruby193/pry_instance.rb" ff = described_class.new(file_with_embedded_line, @p, @opt) file_name, line_num = ff.file_and_line expect(file_name).to eq "C:/Ruby193/pry_instance.rb" expect(line_num).to eq nil end it "parses '/'style absolute path with line_num" do file_with_embedded_line = "C:/Ruby193/pry_instance.rb:2" ff = described_class.new(file_with_embedded_line, @p, @opt) file_name, line_num = ff.file_and_line expect(file_name).to eq "C:/Ruby193/pry_instance.rb" expect(line_num).to eq 2 end it "parses '\\'style absolute path without line_num" do file_with_embedded_line = "C:\\Ruby193\\pry_instance.rb" ff = described_class.new(file_with_embedded_line, @p, @opt) file_name, line_num = ff.file_and_line expect(file_name).to eq "C:\\Ruby193\\pry_instance.rb" expect(line_num).to eq nil end it "parses '\\'style absolute path with line_num" do file_with_embedded_line = "C:\\Ruby193\\pry_instance.rb:2" ff = described_class.new(file_with_embedded_line, @p, @opt) file_name, line_num = ff.file_and_line expect(file_name).to eq "C:\\Ruby193\\pry_instance.rb" expect(line_num).to eq 2 end end describe "UNIX-like filesystem" do it "parses absolute path without line_num" do file_with_embedded_line = "/Ruby193/pry_instance.rb" ff = described_class.new(file_with_embedded_line, @p, @opt) file_name, line_num = ff.file_and_line expect(file_name).to eq "/Ruby193/pry_instance.rb" expect(line_num).to eq nil end it "parses absolute path with line_num" do file_with_embedded_line = "/Ruby193/pry_instance.rb:2" ff = described_class.new(file_with_embedded_line, @p, @opt) file_name, line_num = ff.file_and_line expect(file_name).to eq "/Ruby193/pry_instance.rb" expect(line_num).to eq 2 end end it "parses relative path without line_num" do file_with_embedded_line = "pry_instance.rb" ff = described_class.new(file_with_embedded_line, @p, @opt) file_name, line_num = ff.file_and_line expect(file_name).to eq "pry_instance.rb" expect(line_num).to eq nil end it "parses relative path with line_num" do file_with_embedded_line = "pry_instance.rb:2" ff = described_class.new(file_with_embedded_line, @p, @opt) file_name, line_num = ff.file_and_line expect(file_name).to eq "pry_instance.rb" expect(line_num).to eq 2 end end describe "#format" do it "formats given files" do ff = described_class.new(__FILE__, @p, @opt) expect(ff.format).to match(/it "formats given files" do/) end it "formats given files with line number" do ff = described_class.new(__FILE__ + ':83', @p, @opt) expect(ff.format).to match(/it "formats given files" do/) end end end pry-0.13.1/spec/commands/cat_spec.rb000066400000000000000000000106471364454223300172650ustar00rootroot00000000000000# frozen_string_literal: true require 'tempfile' describe "cat" do before do @str_output = StringIO.new @t = pry_tester do def insert_nil_input @pry.update_input_history(nil) end def last_exception=(exception) @pry.last_exception = exception end end end describe "when invoked without arguments" do it 'should display an error message' do expect { @t.eval 'cat' } .to raise_error(Pry::CommandError, /Must provide a filename/) end end describe "on receiving a file that does not exist" do it 'should display an error message' do expect { @t.eval 'cat supercalifragilicious66' } .to raise_error(StandardError, /Cannot open/) end end describe "with --in" do it 'should display the last few expressions with indices' do expect(@t.eval('10', '20', 'cat --in')).to eq unindent(<<-STR) 1: 10 2: 20 STR end end describe "with --in 1" do it 'should display the first expression with no index' do expect(@t.eval('10', '20', 'cat --in 1')).to eq("10\n") end end describe "with --in -1" do it 'should display the last expression with no index' do expect(@t.eval('10', '20', 'cat --in -1')).to eq("20\n") end end describe "with --in 1..2" do it 'should display the given range with indices, omitting nils' do @t.eval '10' @t.insert_nil_input # normally happens when a command is executed @t.eval ':hello' expect(@t.eval('cat --in 1..3')).to eq(unindent(<<-RANGE)) 1: 10 3: :hello RANGE end end describe "with --ex" do before do @o = Object.new # this is to test exception code (cat --ex) def @o.broken_method this method is broken end end it 'cat --ex should display repl code that generated exception' do @t.eval(unindent(<<-LAST_EXCEPTION)) begin this raises error rescue => e pry_instance.last_exception = e end LAST_EXCEPTION expect(@t.eval('cat --ex')).to match(/\d+:(\s*) this raises error/) end it 'cat --ex should correctly display code that generated exception' do begin @o.broken_method rescue StandardError => e @t.last_exception = e end expect(@t.eval('cat --ex')).to match(/this method is broken/) end end describe "with --ex N" do it 'should cat first level of backtrace when --ex used with no argument ' do temp_file do |f| f << "bt number 1" f.flush @t.last_exception = mock_exception("#{f.path}:1", 'x', 'x') expect(@t.eval('cat --ex')).to match(/bt number 1/) end end it 'should cat first level of backtrace when --ex 0 used ' do temp_file do |f| f << "bt number 1" f.flush @t.last_exception = mock_exception("#{f.path}:1", 'x', 'x') expect(@t.eval('cat --ex 0')).to match(/bt number 1/) end end it 'should cat second level of backtrace when --ex 1 used ' do temp_file do |f| f << "bt number 2" f.flush @t.last_exception = mock_exception('x', "#{f.path}:1", 'x') expect(@t.eval('cat --ex 1')).to match(/bt number 2/) end end it 'should cat third level of backtrace when --ex 2 used' do temp_file do |f| f << "bt number 3" f.flush @t.last_exception = mock_exception('x', 'x', "#{f.path}:1") expect(@t.eval('cat --ex 2')).to match(/bt number 3/) end end it 'should show error when backtrace level out of bounds' do @t.last_exception = mock_exception('x', 'x', 'x') expect { @t.eval('cat --ex 3') } .to raise_error(Pry::CommandError, /out of bounds/) end it( 'each successive cat --ex should show the next level of backtrace, ' \ 'and going past the final level should return to the first' ) do temp_files = [] 3.times do |i| temp_files << Tempfile.new(['pry', '.rb']) temp_files.last << "bt number #{i}" temp_files.last.flush end @t.last_exception = mock_exception(*temp_files.map { |f| "#{f.path}:1" }) 3.times do |i| expect(@t.eval('cat --ex')).to match(/bt number #{i}/) end expect(@t.eval('cat --ex')).to match(/bt number 0/) temp_files.each do |file| file.close(true) end end end end pry-0.13.1/spec/commands/cd_spec.rb000066400000000000000000000163611364454223300171030ustar00rootroot00000000000000# frozen_string_literal: true describe 'cd' do before do @o = Object.new @obj = Object.new @obj.instance_variable_set(:@x, 66) @obj.instance_variable_set(:@y, 79) @o.instance_variable_set(:@obj, @obj) @t = pry_tester(@o) do def mapped_binding_stack binding_stack.map { |b| b.eval('self') } end def binding_stack pry.binding_stack.dup end def command_state pry.commands['cd'].state end def old_stack pry.commands['cd'].state.old_stack.dup end end end after { Pry::CommandState.default.reset('cd') } describe 'old stack toggling with `cd -`' do describe 'in fresh pry instance' do it 'should not toggle when there is no old stack' do 2.times do @t.eval 'cd -' expect(@t.mapped_binding_stack).to eq [@o] end end end describe 'when an error was raised' do it 'should not toggle and should keep correct stacks' do expect { @t.eval 'cd %' }.to raise_error Pry::CommandError expect(@t.old_stack).to eq [] expect(@t.mapped_binding_stack).to eq [@o] @t.eval 'cd -' expect(@t.old_stack).to eq [] expect(@t.mapped_binding_stack).to eq [@o] end end describe 'when using simple cd syntax' do it 'should toggle' do @t.eval 'cd :mon_dogg', 'cd -' expect(@t.mapped_binding_stack).to eq [@o] @t.eval 'cd -' expect(@t.mapped_binding_stack).to eq [@o, :mon_dogg] end end describe "when using complex cd syntax" do it 'should toggle with a complex path (simple case)' do @t.eval 'cd 1/2/3', 'cd -' expect(@t.mapped_binding_stack).to eq [@o] @t.eval 'cd -' expect(@t.mapped_binding_stack).to eq [@o, 1, 2, 3] end it 'should toggle with a complex path (more complex case)' do @t.eval 'cd 1/2/3', 'cd ../4', 'cd -' expect(@t.mapped_binding_stack).to eq [@o, 1, 2, 3] @t.eval 'cd -' expect(@t.mapped_binding_stack).to eq [@o, 1, 2, 4] end end describe 'series of cd calls' do it 'should toggle with fuzzy `cd -` calls' do @t.eval 'cd :mon_dogg', 'cd -', 'cd 42', 'cd -' expect(@t.mapped_binding_stack).to eq [@o] @t.eval 'cd -' expect(@t.mapped_binding_stack).to eq [@o, 42] end end describe 'when using cd ..' do it 'should toggle with a simple path' do @t.eval 'cd :john_dogg', 'cd ..' expect(@t.mapped_binding_stack).to eq [@o] @t.eval 'cd -' expect(@t.mapped_binding_stack).to eq [@o, :john_dogg] end it 'should toggle with a complex path' do @t.eval 'cd 1/2/3/../4', 'cd -' expect(@t.mapped_binding_stack).to eq [@o] @t.eval 'cd -' expect(@t.mapped_binding_stack).to eq [@o, 1, 2, 4] end end describe 'when using cd ::' do it 'should toggle' do @t.eval 'cd ::', 'cd -' expect(@t.mapped_binding_stack).to eq [@o] @t.eval 'cd -' expect(@t.mapped_binding_stack).to eq [@o, TOPLEVEL_BINDING.eval('self')] end end describe 'when using cd /' do it 'should toggle' do @t.eval 'cd /', 'cd -' expect(@t.mapped_binding_stack).to eq [@o] @t.eval 'cd :john_dogg', 'cd /', 'cd -' expect(@t.mapped_binding_stack).to eq [@o, :john_dogg] end end describe 'when using ^D (Control-D) key press' do it 'should keep correct old binding' do @t.eval 'cd :john_dogg', 'cd :mon_dogg', 'cd :kyr_dogg', 'Pry.config.control_d_handler.call(pry_instance)' expect(@t.mapped_binding_stack).to eq [@o, :john_dogg, :mon_dogg] @t.eval 'cd -' expect(@t.mapped_binding_stack).to eq [@o, :john_dogg, :mon_dogg, :kyr_dogg] @t.eval 'cd -' expect(@t.mapped_binding_stack).to eq [@o, :john_dogg, :mon_dogg] end end end it 'should cd into simple input' do @t.eval 'cd :mon_ouie' expect(@t.eval('self')).to eq :mon_ouie end it 'should break out of session with cd ..' do @t.eval 'cd :outer', 'cd :inner' expect(@t.eval('self')).to eq :inner @t.eval 'cd ..' expect(@t.eval('self')).to eq :outer end it "should not leave the REPL session when given 'cd ..'" do @t.eval 'cd ..' expect(@t.eval('self')).to eq @o end it 'should break out to outer-most session with cd /' do @t.eval 'cd :inner' expect(@t.eval('self')).to eq :inner @t.eval 'cd 5' expect(@t.eval('self')).to eq 5 @t.eval 'cd /' expect(@t.eval('self')).to eq @o end it 'should break out to outer-most session with just cd (no args)' do @t.eval 'cd :inner' expect(@t.eval('self')).to eq :inner @t.eval 'cd 5' expect(@t.eval('self')).to eq 5 @t.eval 'cd' expect(@t.eval('self')).to eq @o end it 'should cd into an object and its ivar using cd obj/@ivar syntax' do @t.eval 'cd @obj/@x' expect(@t.mapped_binding_stack).to eq [@o, @obj, 66] end it 'cds into an object and its ivar using cd obj/@ivar/ syntax (note following /)' do @t.eval 'cd @obj/@x/' expect(@t.mapped_binding_stack).to eq [@o, @obj, 66] end it 'should cd into previous object and its local using cd ../local syntax' do @t.eval 'cd @obj', 'local = :local', 'cd @x', 'cd ../local' expect(@t.mapped_binding_stack).to eq [@o, @obj, :local] end it 'cds into an object and its ivar and back again using cd obj/@ivar/.. syntax' do @t.eval 'cd @obj/@x/..' expect(@t.mapped_binding_stack).to eq [@o, @obj] end it( 'cds into an object and its ivar and back and then into another ivar ' \ 'using cd obj/@ivar/../@y syntax' ) do @t.eval 'cd @obj/@x/../@y' expect(@t.mapped_binding_stack).to eq [@o, @obj, 79] end it 'should cd back to top-level and then into another ivar using cd /@ivar/ syntax' do @t.eval '@z = 20', 'cd @obj/@x/', 'cd /@z' expect(@t.mapped_binding_stack).to eq [@o, 20] end it 'should start a session on TOPLEVEL_BINDING with cd ::' do @t.eval 'cd ::' expect(@t.eval('self')).to eq TOPLEVEL_BINDING.eval('self') end it 'should cd into complex input (with spaces)' do def @o.hello(_x, _y, _z) # rubocop:disable Naming/UncommunicativeMethodParamName :mon_ouie end @t.eval 'cd hello 1, 2, 3' expect(@t.eval('self')).to eq :mon_ouie end it 'should not cd into complex input when it encounters an exception' do expect { @t.eval 'cd 1/2/swoop_a_doop/3' }.to raise_error Pry::CommandError expect(@t.mapped_binding_stack).to eq [@o] end it 'can cd into an expression containing a string with slashes in it' do @t.eval 'cd ["http://google.com"]' expect(@t.eval('self')).to eq ["http://google.com"] end it 'can cd into an expression with division in it' do @t.eval 'cd (10/2)/even?' expect(@t.eval('self')).to eq false end # Regression test for ticket #516. it 'should be able to cd into the Object BasicObject' do expect { @t.eval 'cd BasicObject.new' }.to_not raise_error end # https://github.com/pry/pry/issues/1596 it "can cd into objects that redefine #respond_to? to return true" do expect { @t.eval('cd Class.new { def respond_to?(m) true end }.new') } .to_not raise_error end end pry-0.13.1/spec/commands/clear_screen_spec.rb000066400000000000000000000013351364454223300211350ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe "clear-screen" do before do @t = pry_tester end it 'calls the "clear" command on non-Windows platforms' do expect(Pry::Helpers::Platform).to receive(:windows?) .at_least(:once).and_return(false) expect(Pry.config.system).to receive(:call) .with(an_instance_of(Pry::Output), 'clear', an_instance_of(Pry)) @t.process_command 'clear-screen' end it 'calls the "cls" command on Windows' do expect(Pry::Helpers::Platform).to receive(:windows?) .at_least(:once).and_return(true) expect(Pry.config.system).to receive(:call) .with(an_instance_of(Pry::Output), 'cls', an_instance_of(Pry)) @t.process_command 'clear-screen' end end pry-0.13.1/spec/commands/disable_pry_spec.rb000066400000000000000000000007241364454223300210060ustar00rootroot00000000000000# frozen_string_literal: true describe "disable-pry" do before do @t = pry_tester end after do ENV.delete 'DISABLE_PRY' end it 'should quit the current session' do expect { @t.process_command 'disable-pry' }.to throw_symbol :breakout end it "should set DISABLE_PRY" do expect(ENV['DISABLE_PRY']).to eq nil expect { @t.process_command 'disable-pry' }.to throw_symbol :breakout expect(ENV['DISABLE_PRY']).to eq 'true' end end pry-0.13.1/spec/commands/edit_spec.rb000066400000000000000000000564161364454223300174470ustar00rootroot00000000000000# frozen_string_literal: true require 'pathname' require 'tempfile' describe "edit" do before do @old_editor = Pry.config.editor @file = @line = @contents = nil Pry.config.editor = lambda do |file, line| @file = file @line = line @contents = File.read(@file) nil end end after do Pry.config.editor = @old_editor end describe "with FILE" do before do # OS-specific tempdir name. For GNU/Linux it's "tmp", for Windows it's # something "Temp". @tf_dir = if Pry::Helpers::Platform.mri_19? Pathname.new(Dir::Tmpname.tmpdir) else Pathname.new(Dir.tmpdir) end @tf_path = File.expand_path(File.join(@tf_dir.to_s, 'bar.rb')) FileUtils.touch(@tf_path) end after do FileUtils.rm(@tf_path) if File.exist?(@tf_path) end it "should not allow patching any known kind of file" do ["file.rb", "file.c", "file.py", "file.yml", "file.gemspec", "/tmp/file", "\\\\Temp\\\\file"].each do |file| expect { pry_eval "edit -p #{file}" } .to raise_error(NotImplementedError, /Cannot yet patch false objects!/) end end it "should invoke Pry.config.editor with absolutified filenames" do pry_eval 'edit lib/pry.rb' expect(@file).to eq File.expand_path('lib/pry.rb') pry_eval "edit #{@tf_path}" expect(@file).to eq @tf_path end it "should guess the line number from a colon" do pry_eval 'edit lib/pry.rb:10' expect(@line).to eq 10 end it "should use the line number from -l" do pry_eval 'edit -l 10 lib/pry.rb' expect(@line).to eq 10 end it "should not delete the file!" do pry_eval 'edit Rakefile' expect(File.exist?(@file)).to eq true end it "works with files that contain blanks in their names" do tf_path = File.join(File.dirname(@tf_path), 'swoop and doop.rb') FileUtils.touch(tf_path) pry_eval "edit #{tf_path}" expect(@file).to eq tf_path FileUtils.rm(tf_path) end if respond_to?(:require_relative, true) it "should work with require relative" do Pry.config.editor = lambda { |file, _line| File.open(file, 'w') { |f| f << 'require_relative "baz.rb"' } File.open(file.gsub('bar.rb', 'baz.rb'), 'w') do |f| f << "Pad.required = true; FileUtils.rm(__FILE__)" end nil } pry_eval "edit #{@tf_path}" expect(Pad.required).to eq true end end describe do before do Pad.counter = 0 Pry.config.editor = lambda { |file, _line| File.open(file, 'w') { |f| f << "Pad.counter = Pad.counter + 1" } nil } end it "should reload the file if it is a ruby file" do temp_file do |tf| counter = Pad.counter path = tf.path pry_eval "edit #{path}" expect(Pad.counter).to eq counter + 1 end end it "should not reload the file if it is not a ruby file" do temp_file('.py') do |tf| counter = Pad.counter path = tf.path pry_eval "edit #{path}" expect(Pad.counter).to eq counter end end it "should not reload a ruby file if -n is given" do temp_file do |tf| counter = Pad.counter path = tf.path pry_eval "edit -n #{path}" expect(Pad.counter).to eq counter end end it "should reload a non-ruby file if -r is given" do temp_file('.pryrc') do |tf| counter = Pad.counter path = tf.path pry_eval "edit -r #{path}" expect(Pad.counter).to eq counter + 1 end end end describe do before do @reloading = nil Pry.config.editor = lambda do |file, line, reloading| @file = file @line = line @reloading = reloading nil end end it "should pass the editor a reloading arg" do pry_eval 'edit lib/pry.rb' expect(@reloading).to eq true pry_eval 'edit -n lib/pry.rb' expect(@reloading).to eq false end end end describe "with --ex" do before do @t = pry_tester do def last_exception=(exception) @pry.last_exception = exception end def last_exception @pry.last_exception end end end describe "with a real file" do before do @tf = Tempfile.new(["pry", ".rb"]) @path = @tf.path @tf << "_foo = 1\n_bar = 2\nraise RuntimeError" @tf.flush begin load @path rescue RuntimeError => e @t.last_exception = e end end after do @tf.close(true) end it "should reload the file" do @t.pry.config.editor = lambda { |file, _line| File.open(file, 'w') { |f| f << "FOO = 'BAR'" } nil } expect(defined?(FOO)).to equal nil @t.eval 'edit --ex' expect(FOO).to eq 'BAR' end # regression test (this used to edit the current method instead # of the exception) it 'edits the exception even when in a patched method context' do source_location = nil Pry.config.editor = lambda { |file, line| source_location = [file, line] nil } Pad.le = @t.last_exception redirect_pry_io(InputTester.new("def broken_method", "binding.pry", "end", "broken_method", "pry_instance.last_exception = Pad.le", "edit --ex -n", "exit-all", "exit-all")) do Object.new.pry end expect(source_location).to contain_exactly(%r{(/private)?#{@path}}, 3) Pad.clear end it "should not reload the file if -n is passed" do Pry.config.editor = lambda { |file, _line| File.open(file, 'w') { |f| f << "FOO2 = 'BAZ'" } nil } expect(defined?(FOO2)).to equal nil @t.eval 'edit -n --ex' expect(defined?(FOO2)).to equal nil end describe "with --patch" do # Original source code must be untouched. it "should apply changes only in memory (monkey patching)" do @t.pry.config.editor = lambda { |file, _line| File.open(file, 'w') { |f| f << "FOO3 = 'PIYO'" } @patched_def = File.open(file, 'r').read nil } expect(defined?(FOO3)).to equal nil @t.eval 'edit --ex --patch' expect(FOO3).to eq 'PIYO' @tf.rewind expect(@tf.read).to eq "_foo = 1\n_bar = 2\nraise RuntimeError" expect(@patched_def).to eq "FOO3 = 'PIYO'" end end end describe "with --ex NUM" do before do @t.pry.config.editor = proc do |file, line| @__ex_file__ = file @__ex_line__ = line nil end @t.last_exception = mock_exception('a:1', 'b:2', 'c:3') end it 'should start on first level of backtrace with just --ex' do @t.eval 'edit -n --ex' expect(@__ex_file__).to eq "a" expect(@__ex_line__).to eq 1 end it 'should start editor on first level of backtrace with --ex 0' do @t.eval 'edit -n --ex 0' expect(@__ex_file__).to eq "a" expect(@__ex_line__).to eq 1 end it 'should start editor on second level of backtrace with --ex 1' do @t.eval 'edit -n --ex 1' expect(@__ex_file__).to eq "b" expect(@__ex_line__).to eq 2 end it 'should start editor on third level of backtrace with --ex 2' do @t.eval 'edit -n --ex 2' expect(@__ex_file__).to eq "c" expect(@__ex_line__).to eq 3 end it 'should display error message when backtrace level is invalid' do expect { @t.eval 'edit -n --ex 4' }.to raise_error Pry::CommandError end end end describe "without FILE" do before do @t = pry_tester end it "should edit the current expression if it's incomplete" do @t.push 'def a' @t.process_command 'edit' expect(@contents).to eq "def a\n" end it "should edit the previous expression if the current is empty" do @t.eval 'undef a if self.singleton_class.method_defined? :a' @t.eval 'def a; 2; end', 'edit' expect(@contents).to eq "def a; 2; end\n" end it "should use a blank file if -t is specified" do @t.eval 'undef a if self.singleton_class.method_defined? :a' @t.eval 'def a; 5; end', 'edit -t' expect(@contents).to eq "\n" end it "should use a blank file if -t given, even during an expression" do @t.push 'def a;' @t.process_command 'edit -t' expect(@contents).to eq "\n" end it "should position the cursor at the end of the expression" do @t.eval 'undef a if self.singleton_class.method_defined? :a' @t.eval "def a; 2;\nend" @t.process_command 'edit' expect(@line).to eq 2 end it "should evaluate the expression" do @t.pry.config.editor = lambda { |file, _line| File.open(file, 'w') { |f| f << "'FOO'\n" } nil } @t.process_command 'edit' expect(@t.eval_string).to eq "'FOO'\n" end it "should ignore -n for tempfiles" do @t.pry.config.editor = lambda { |file, _line| File.open(file, 'w') { |f| f << "'FOO'\n" } nil } @t.process_command "edit -n" expect(@t.eval_string).to eq "'FOO'\n" end it "should not evaluate a file with -n" do @t.pry.config.editor = lambda { |file, _line| File.open(file, 'w') { |f| f << "'FOO'\n" } nil } begin @t.process_command 'edit -n spec/fixtures/foo.rb' expect(File.read("spec/fixtures/foo.rb")).to eq "'FOO'\n" expect(@t.eval_string).to eq '' ensure FileUtils.rm "spec/fixtures/foo.rb" end end it "should write the evaluated command to history" do quote = 'history repeats itself, first as tradegy...' @t.pry.config.editor = lambda { |file, _line| File.open(file, 'w') do |f| f << quote end nil } @t.process_command 'edit' expect(Pry.history.to_a.last).to eq quote end end describe "with --in" do it "should edit the nth line of _in_" do pry_eval '10', '11', 'edit --in -2' expect(@contents).to eq "10\n" end it "should edit the last line if no argument is given" do pry_eval '10', '11', 'edit --in' expect(@contents).to eq "11\n" end it "should edit a range of lines if a range is given" do pry_eval "10", "11", "edit -i 1,2" expect(@contents).to eq "10\n11\n" end it "should edit a multi-line expression as it occupies one line of _in_" do pry_eval "class #{1.class}\n def invert; -self; end\nend", "edit -i 1" expect(@contents).to eq "class #{1.class}\n def invert; -self; end\nend\n" end it "should not work with a filename" do expect { pry_eval 'edit ruby.rb -i' }.to raise_error( Pry::CommandError, /Only one of --ex, --temp, --in, --method and FILE/ ) end it "should not work with nonsense" do expect { pry_eval 'edit --in three' }.to raise_error( Pry::CommandError, /Not a valid range: three/ ) end end describe 'when editing a method by name' do def use_editor(tester, options) tester.pry.config.editor = lambda do |filename, _line| File.open(filename, 'w') { |f| f.write options.fetch(:replace_all) } nil end tester end # rubocop:disable Style/SingleLineMethods it 'uses patch editing on methods that were previously patched' do # initial definition tester = pry_tester binding filename = __FILE__ line = __LINE__ + 2 klass = Class.new do def m; 1; end end expect(klass.new.m).to eq 1 # now patch it use_editor(tester, replace_all: 'def m; 2; end').eval('edit --patch klass#m') expect(klass.new.m).to eq 2 # edit by name, no --patch use_editor(tester, replace_all: 'def m; 3; end').eval("edit klass#m") expect(klass.new.m).to eq 3 # original file is unchanged expect(File.readlines(filename)[line - 1].strip).to eq 'def m; 1; end' end # rubocop:enable Style/SingleLineMethods it 'can repeatedly edit methods that were defined in the console' do # initial definition tester = pry_tester binding tester.eval("klass = Class.new do\n"\ " def m; 1; end\n"\ "end") expect(tester.eval("klass.new.m")).to eq 1 # first edit use_editor(tester, replace_all: 'def m; 2; end').eval('edit klass#m') expect(tester.eval('klass.new.m')).to eq 2 # repeat edit use_editor(tester, replace_all: 'def m; 3; end').eval('edit klass#m') expect(tester.eval('klass.new.m')).to eq 3 end end describe "old edit-method tests now migrated to edit" do describe "on a method defined in a file" do before do Object.remove_const :X if defined? ::X Object.remove_const :A if defined? ::A @tempfile = Tempfile.new(['pry', '.rb']) @tempfile.puts(<<-CLASSES) module A def a :yup end def b :kinda end end class X include A def self.x :double_yup end def x :nope end def b super end alias c b def y? :because end class B G = :nawt def foo _foo = :possibly G end end end CLASSES @tempfile.flush load @tempfile.path @tempfile_path = @tempfile.path end after do @tempfile.close(true) end describe 'without -p' do before do @file = @line = @contents = nil Pry.config.editor = lambda do |file, line| @file = file @line = line nil end end # Workaround for https://github.com/jruby/jruby/issues/5436. let(:expected_file) { %r{(/private)?#{@tempfile_path}} } it "should correctly find a class method" do pry_eval 'edit X.x' expect(@file).to match(expected_file) expect(@line).to eq 14 end it "should correctly find an instance method" do pry_eval 'edit X#x' expect(@file).to match(expected_file) expect(@line).to eq 18 end it "should correctly find a method on an instance" do pry_eval 'x = X.new', 'edit x.x' expect(@file).to match(expected_file) expect(@line).to eq 18 end it "should correctly find a method from a module" do pry_eval 'edit X#a' expect(@file).to match(expected_file) expect(@line).to eq 2 end it "should correctly find an aliased method" do pry_eval 'edit X#c' expect(@file).to match(expected_file) expect(@line).to eq 22 end end describe 'with -p' do before do Pry.config.editor = lambda do |file, _line| lines = File.read(file).lines.to_a lines[1] = if lines[2] =~ /end/ ":maybe\n" else "_foo = :maybe\n" end File.open(file, 'w') do |f| f.write(lines.join) end @patched_def = String(lines[1]).chomp nil end end it "should successfully replace a class method" do pry_eval 'edit -p X.x' expect(X.method(:x).owner).to eq class << X; self end expect(X.method(:x).receiver).to eq X expect(X.x).to eq :maybe end it "should successfully replace an instance method" do pry_eval 'edit -p X#x' expect(X.instance_method(:x).owner).to eq X expect(X.new.x).to eq :maybe end it "should successfully replace a method on an instance" do pry_eval 'instance = X.new', 'edit -p instance.x' instance = X.new expect(instance.method(:x).owner).to eq X expect(instance.x).to eq :maybe end it "should successfully replace a method from a module" do pry_eval 'edit -p X#a' expect(X.instance_method(:a).owner).to eq A expect(X.new.a).to eq :maybe end it "should successfully replace a method with a question mark" do pry_eval 'edit -p X#y?' expect(X.instance_method(:y?).owner).to eq X expect(X.new.y?).to eq :maybe end it "should preserve module nesting" do pry_eval 'edit -p X::B#foo' expect(X::B.instance_method(:foo).owner).to eq X::B expect(X::B.new.foo).to eq :nawt end describe "monkey-patching" do before do @edit = 'edit --patch ' # A shortcut. end # @param [Integer] lineno # @return [String] the stripped line from the tempfile at +lineno+ def stripped_line_at(lineno) @tempfile.rewind @tempfile.each_line.to_a[lineno].strip end # Applies the monkey patch for +method+ with help of evaluation of # +eval_strs+. The idea is to capture the initial line number (before # the monkey patch), because it gets overwritten by the line number from # the monkey patch. And our goal is to check that the original # definition hasn't changed. # @param [UnboundMethod] method # @param [Array] eval_strs # @return [Array e expect(e.status).to eq(66) else raise "Failed to raise SystemExit" end end end pry-0.13.1/spec/commands/exit_spec.rb000066400000000000000000000015131364454223300174570ustar00rootroot00000000000000# frozen_string_literal: true describe "exit" do before { @pry = Pry.new(target: :outer, output: StringIO.new) } it "should pop a binding" do @pry.eval "cd :inner" expect(@pry.evaluate_ruby("self")).to eq :inner @pry.eval "exit" expect(@pry.evaluate_ruby("self")).to eq :outer end it "should break out of the repl when binding_stack has only one binding" do expect(@pry.eval("exit")).to equal false expect(@pry.exit_value).to equal nil end it "should break out of the repl and return user-given value" do expect(@pry.eval("exit :john")).to equal false expect(@pry.exit_value).to eq :john end it "should break out of the repl even after an exception" do @pry.eval "exit = 42" expect(@pry.output.string).to match(/^SyntaxError/) expect(@pry.eval("exit")).to equal false end end pry-0.13.1/spec/commands/find_method_spec.rb000066400000000000000000000033261364454223300207720ustar00rootroot00000000000000# frozen_string_literal: true describe "find-method" do MyKlass = Class.new do def hello "timothy" end def goodbye "jenny" end def tea_tim? "timothy" end def tea_time? "polly" end end describe "find matching methods by name regex (-n option)" do it "should find a method by regex" do expect(pry_eval(binding, "find-method hell MyKlass")).to match( /MyKlass.*?hello/m ) end it "should NOT match a method that does not match the regex" do expect(pry_eval(binding, "find-method hell MyKlass")).not_to match( /MyKlass.*?goodbye/m ) end end describe "find matching methods by content regex (-c option)" do it "should find a method by regex" do expect(pry_eval(binding, "find-method -c timothy MyKlass")).to match( /MyKlass.*?hello/m ) end it "should NOT match a method that does not match the regex" do expect(pry_eval(binding, "find-method timothy MyKlass")).not_to match( /MyKlass.*?goodbye/m ) end end it "should work with badly behaved constants" do MyKlass::X = Object.new def (MyKlass::X).hash raise "mooo" end expect(pry_eval(binding, "find-method -c timothy MyKlass")).to match( /MyKlass.*?hello/m ) end it "should escape regexes correctly" do good = /tea_time\?/ bad = /tea_tim\?/ expect(pry_eval(binding, 'find-method tea_time? MyKlass')).to match(good) expect(pry_eval(binding, 'find-method tea_time? MyKlass')).to match(good) expect(pry_eval(binding, 'find-method tea_time\? MyKlass')).not_to match(bad) expect(pry_eval(binding, 'find-method tea_time\? MyKlass')).to match(good) end end pry-0.13.1/spec/commands/help_spec.rb000066400000000000000000000031341364454223300174370ustar00rootroot00000000000000# frozen_string_literal: true describe "help" do before do @oldset = Pry.config.commands @set = Pry.config.commands = Pry::CommandSet.new do import Pry::Commands end end after do Pry.config.commands = @oldset end it 'should display help for a specific command' do expect(pry_eval('help ls')).to match(/Usage: ls/) end it 'should display help for a regex command with a "listing"' do @set.command(/bar(.*)/, "Test listing", listing: "foo") { ; } expect(pry_eval('help foo')).to match(/Test listing/) end it 'should display help for a command with a spaces in its name' do @set.command('cmd with spaces', 'desc of a cmd with spaces') {} expect(pry_eval('help "cmd with spaces"')).to match(/desc of a cmd with spaces/) end it 'should display help for all commands with a description' do @set.command(/bar(.*)/, "Test listing", listing: "foo") { ; } @set.command('b', 'description for b', listing: 'foo') {} @set.command('c') {} @set.command('d', '') {} output = pry_eval('help') expect(output).to match(/Test listing/) expect(output).to match(/Description for b/) expect(output).to match(/No description/) end it "should sort the output of the 'help' command" do @set.command('faa', 'Fooerizes') {} @set.command('gaa', 'Gooerizes') {} @set.command('maa', 'Mooerizes') {} @set.command('baa', 'Booerizes') {} doc = pry_eval('help') order = [doc.index("baa"), doc.index("faa"), doc.index("gaa"), doc.index("maa")] expect(order).to eq(order.sort) end end pry-0.13.1/spec/commands/hist_spec.rb000066400000000000000000000146371364454223300174700ustar00rootroot00000000000000# frozen_string_literal: true describe "hist" do before do Pry.history.clear @hist = Pry.history @str_output = StringIO.new @t = pry_tester history: @hist do # For looking at what hist pushes into the input stack. The implementation # of this helper will definitely have to change at some point. def next_input @pry.input.string end end end it 'should replay history correctly (single item)' do o = Object.new @hist.push "@x = 10" @hist.push "@y = 20" @hist.push "@z = 30" @t.push_binding o @t.eval 'hist --replay -1' expect(o.instance_variable_get(:@z)).to eq 30 end it 'should replay a range of history correctly (range of items)' do o = Object.new @hist.push "@x = 10" @hist.push "@y = 20" @t.push_binding o @t.eval 'hist --replay 0..2' expect(@t.eval('[@x, @y]')).to eq [10, 20] end # this is to prevent a regression where input redirection is # replaced by just appending to `eval_string` it 'should replay a range of history correctly (range of commands)' do @hist.push "cd 1" @hist.push "cd 2" @t.eval("hist --replay 0..2") stack = @t.eval("Pad.stack = pry_instance.binding_stack.dup") expect(stack.map { |b| b.eval("self") }).to eq [TOPLEVEL_BINDING.eval("self"), 1, 2] end it 'should grep for correct lines in history' do @hist.push "abby" @hist.push "box" @hist.push "button" @hist.push "pepper" @hist.push "orange" @hist.push "grape" @hist.push "def blah 1" @hist.push "def boink 2" @hist.push "place holder" expect(@t.eval('hist --grep o')).to match(/\d:.*?box\n\d:.*?button\n\d:.*?orange/) # test more than one word in a regex match (def blah) expect(@t.eval('hist --grep def blah')).to match(/def blah 1/) # test more than one word with leading white space in a regex match (def boink) expect(@t.eval('hist --grep def boink')).to match(/def boink 2/) end it 'should return last N lines in history with --tail switch' do ("a".."z").each do |v| @hist.push v end out = @t.eval 'hist --tail 3' expect(out.each_line.count).to eq 3 expect(out).to match(/x\n\d+:.*y\n\d+:.*z/) end it "should start from beginning if tail number is longer than history" do @hist.push 'Hyacinth' out = @t.eval 'hist --tail' expect(out).to match(/Hyacinth/) end it 'should apply --tail after --grep' do @hist.push "print 1" @hist.push "print 2" @hist.push "puts 3" @hist.push "print 4" @hist.push "puts 5" out = @t.eval 'hist --tail 2 --grep print' expect(out.each_line.count).to eq 2 expect(out).to match(/\d:.*?print 2\n\d:.*?print 4/) end it 'should apply --head after --grep' do @hist.push "puts 1" @hist.push "print 2" @hist.push "puts 3" @hist.push "print 4" @hist.push "print 5" out = @t.eval 'hist --head 2 --grep print' expect(out.each_line.count).to eq 2 expect(out).to match(/\d:.*?print 2\n\d:.*?print 4/) end # strangeness in this test is due to bug in Readline::HISTORY not # always registering first line of input it 'should return first N lines in history with --head switch' do ("a".."z").each do |v| @hist.push v end out = @t.eval 'hist --head 4' expect(out.each_line.count).to eq 4 expect(out).to match(/a\n\d+:.*b\n\d+:.*c/) end # strangeness in this test is due to bug in Readline::HISTORY not # always registering first line of input it 'should show lines between lines A and B with the --show switch' do ("a".."z").each do |v| @hist.push v end out = @t.eval 'hist --show 1..4' expect(out.each_line.count).to eq 4 expect(out).to match(/b\n\d+:.*c\n\d+:.*d/) end it 'should show lines between offsets A and B with the --show switch' do ("a".."f").each do |v| @hist.push v end out = @t.eval 'hist --show -4..-2' expect(out).to eq "3: c\n4: d\n5: e\n" end it "should store a call with `--replay` flag" do @t.eval ":banzai" @t.eval "hist --replay 1" expect(@t.eval("hist")).to match(/hist --replay 1/) end it "should not contain lines produced by `--replay` flag" do @t.eval ":banzai" @t.eval ":geronimo" @t.eval ":huzzah" @t.eval("hist --replay 1..3") output = @t.eval("hist") expect(output).to eq "1: :banzai\n2: :geronimo\n3: :huzzah\n4: hist --replay 1..3\n" end it( "raises CommandError when index of `--replay` points out to another " \ "`hist --replay`" ) do @t.eval ":banzai" @t.eval "hist --replay 1" expect { @t.eval "hist --replay 2" }.to raise_error( Pry::CommandError, /Replay index 2 points out to another replay call: `hist --replay 1`/ ) end it "should disallow execution of `--replay ` when CommandError raised" do @t.eval "a = 0" @t.eval "a += 1" @t.eval "hist --replay 2" expect { @t.eval "hist --replay 3" }.to raise_error Pry::CommandError expect(@t.eval("a")).to eq 2 expect(@t.eval("hist").lines.to_a.size).to eq 5 end it "excludes Pry commands from the history with `-e` switch" do @hist.push('a = 20') @hist.push('ls') expect(pry_eval('hist -e')).to eq "1: a = 20\n" end describe "sessions" do before do @old_file = Pry.config.history_file Pry.config.history_file = File.expand_path('spec/fixtures/pry_history') @hist.load end after do Pry.config.history_file = @old_file end it "displays history only for current session" do @hist.push('hello') @hist.push('world') expect(@t.eval('hist')).to match(/1:\shello\n2:\sworld/) end it "displays all history (including the current sesion) with `--all` switch" do @hist.push('goodbye') @hist.push('world') output = @t.eval('hist --all') expect(output).to match(/1:\s:athos\n2:\s:porthos\n3:\s:aramis\n/) expect(output).to match(/4:\sgoodbye\n5:\sworld/) end it "should not display histignore words in history" do Pry.config.history_ignorelist = [ "well", "hello", "beautiful", /show*/, "exit" ] @hist.push("well") @hist.push("hello") @hist.push("beautiful") @hist.push("why") @hist.push("so") @hist.push("serious?") @hist.push("show-method") @hist.push("exit") output = @t.eval("hist") expect(output).to match(/1:\swhy\n2:\sso\n3:\sserious\?\n/) end end end pry-0.13.1/spec/commands/jump_to_spec.rb000066400000000000000000000011541364454223300201640ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe "jump-to" do let(:obj) { Object.new } it 'jumps to the proper binding index in the stack' do expect(pry_eval(obj, "cd 1", "cd 2", "jump-to 0", 'self')).to eq obj expect(pry_eval(obj, 'cd 1', 'cd 2', 'jump-to 1', 'self')).to eq 1 end it 'prints an error when trying to jump to the same binding index' do expect(pry_eval(obj, "cd 1", "cd 2", "jump-to 2")).to match(/Already/) end it 'prints error when trying to jump to a non-existent binding index' do expect(pry_eval(obj, "cd 1", "cd 2", "jump-to 3")).to match(/Invalid nest level/) end end pry-0.13.1/spec/commands/ls_spec.rb000066400000000000000000000225051364454223300171300ustar00rootroot00000000000000# frozen_string_literal: true describe "ls" do describe "bug #1407" do it "behaves as usual when a method of the same name exists." do expect( pry_eval("def ls; 5; end", "ls") ).to match(/self\.methods: /) pry_eval("undef ls") end end describe "below ceiling" do it "should stop before Object by default" do expect(pry_eval("cd Class.new{ def goo; end }.new", "ls")).not_to match(/Object/) expect(pry_eval("cd Class.new{ def goo; end }", "ls -M")).not_to match(/Object/) end it "should include object if -v is given" do expect(pry_eval("cd Class.new{ def goo; end }.new", "ls -m -v")).to match(/Object/) expect(pry_eval("cd Class.new{ def goo; end }", "ls -vM")).to match(/Object/) end it "should include super-classes by default" do expect( pry_eval( "cd Class.new(Class.new{ def goo; end; public :goo }).new", "ls" ) ).to match(/goo/) expect( pry_eval( "cd Class.new(Class.new{ def goo; end; public :goo })", "ls -M" ) ).to match(/goo/) end it "should not include super-classes when -q is given" do expect(pry_eval("cd Class.new(Class.new{ def goo; end }).new", "ls -q")) .not_to match(/goo/) expect(pry_eval("cd Class.new(Class.new{ def goo; end })", "ls -M -q")) .not_to match(/goo/) end end describe "help" do it 'should show help with -h' do expect(pry_eval("ls -h")).to match(/Usage: ls/) end end describe "BasicObject" do it "should work on BasicObject" do expect(pry_eval("ls BasicObject.new")).to match(/BasicObject#methods:.*__send__/m) end it "should work on subclasses of BasicObject" do expect( pry_eval( "class LessBasic < BasicObject; def jaroussky; 5; end; end", "ls LessBasic.new" ) ).to match(/LessBasic#methods:.*jaroussky/m) end end describe "immediates" do # Ruby 2.4+ if 5.class.name == 'Integer' it "should work on Integer" do expect(pry_eval("ls 5")).to match(/Integer#methods:.*modulo/m) end else it "should work on Fixnum" do expect(pry_eval("ls 5")).to match(/Fixnum#methods:.*modulo/m) end end end describe "methods" do it "should show public methods by default" do output = pry_eval("ls Class.new{ def goo; end; public :goo }.new") expect(output).to match(/methods: goo/) end it "should not show protected/private by default" do expect(pry_eval("ls -M Class.new{ def goo; end; private :goo }")) .not_to match(/goo/) expect(pry_eval("ls Class.new{ def goo; end; protected :goo }.new")) .not_to match(/goo/) end it "should show public methods with -p" do expect(pry_eval("ls -p Class.new{ def goo; end }.new")).to match(/methods: goo/) end it "should show protected/private methods with -p" do expect(pry_eval("ls -pM Class.new{ def goo; end; protected :goo }")) .to match(/methods: goo/) expect(pry_eval("ls -p Class.new{ def goo; end; private :goo }.new")) .to match(/methods: goo/) end it "should work for objects with an overridden method method" do require 'net/http' # This doesn't actually touch the network, promise! expect(pry_eval("ls Net::HTTP::Get.new('localhost')")) .to match(/Net::HTTPGenericRequest#methods/) end it( "should work for objects which instance_variables returns array of " \ "symbol but there is no Symbol#downcase" ) do test_case = "class Object; alias :fg :instance_variables; " \ "def instance_variables; fg.map(&:to_sym); end end;" normalize = "class Object; def instance_variables; fg; end end;" test = lambda do begin pry_eval( test_case, "class GeFromulate2; @flurb=1.3; end", "cd GeFromulate2", "ls" ) pry_eval(normalize) rescue StandardError pry_eval(normalize) raise end end expect(test).to_not raise_error end it "should show error message when instance is given with -M option" do expect { pry_eval("ls -M String.new") } .to raise_error(Pry::CommandError, /-M only makes sense with a Module or a Class/) end it "should handle classes that (pathologically) define .ancestors" do output = pry_eval("ls Class.new{ def self.ancestors; end; def hihi; end }") expect(output).to match(/hihi/) end end describe 'with -l' do focus 'should find locals and sort by descending size' do result = pry_eval(Object.new, "aa = 'asdf'; bb = 'xyz'", 'ls -l') expect(result).not_to match(/=>/) expect(result).not_to match(/0x\d{5}/) expect(result).to match(/asdf.*xyz/m) end it 'should not list pry noise' do expect(pry_eval('ls -l')).not_to match(/_(?:dir|file|ex|pry|out|in)_/) end end describe "when inside Modules" do it "should still work" do expect( pry_eval( "cd Module.new{ def foobie; end; public :foobie }", "ls -M" ) ).to match(/foobie/) end it "should work for ivars" do expect( pry_eval( "module StigmaT1sm; def foobie; @@gharble = 456; end; end", "Object.new.tap{ |o| o.extend(StigmaT1sm) }.foobie", "cd StigmaT1sm", "ls -i" ) ).to match(/@@gharble/) end it "should include instance methods by default" do output = pry_eval( "ls Module.new{ def shinanagarns; 4; end; public :shinanagarns }" ) expect(output).to match(/shinanagarns/) end it "should behave normally when invoked on Module itself" do expect(pry_eval("ls Module")).not_to match(/Pry/) end end describe "constants" do it "works on top-level" do toplevel_consts = pry_eval('ls -c') [/RUBY_PLATFORM/, /ARGF/, /STDOUT/].each do |const| expect(toplevel_consts).to match(const) end end it "should show constants defined on the current module" do expect(pry_eval("class TempFoo1; BARGHL = 1; end", "ls TempFoo1")) .to match(/BARGHL/) end it "should not show constants defined on parent modules by default" do output = pry_eval( "class TempFoo2; LHGRAB = 1; end; " \ "class TempFoo3 < TempFoo2; BARGHL = 1; end", "ls TempFoo3" ) expect(output).not_to match(/LHGRAB/) end it "should show constants defined on ancestors with -v" do output = pry_eval( "class TempFoo4; LHGRAB = 1; end; " \ "class TempFoo5 < TempFoo4; BARGHL = 1; end", "ls -v TempFoo5" ) expect(output).to match(/LHGRAB/) end it "should not autoload constants!" do autoload :McflurgleTheThird, "/tmp/this-file-d000esnat-exist.rb" expect { pry_eval("ls -c") }.to_not raise_error end it "should show constants for an object's class regardless of mixins" do expect( pry_eval( "cd Pry.new", "extend Module.new", "ls -c" ) ).to match(/Method/) end end describe "grep" do it "should reduce the number of outputted things" do expect(pry_eval("ls -c Object")).to match(/ArgumentError/) expect(pry_eval("ls -c Object --grep Run")).not_to match(/ArgumentError/) end it "should still output matching things" do expect(pry_eval("ls -c Object --grep Run")).to match(/RuntimeError/) end end describe "when no arguments given" do describe "when at the top-level" do it "should show local variables" do expect(pry_eval("ls")).to match(/pry_instance/) expect(pry_eval("arbitrar = 1", "ls")).to match(/arbitrar/) end end describe "when in a class" do it "should show constants" do output = pry_eval( "class GeFromulate1; FOOTIFICATE=1.3; end", "cd GeFromulate1", "ls" ) expect(output).to match(/FOOTIFICATE/) end it "should show class variables" do output = pry_eval( "class GeFromulate2; @@flurb=1.3; end", "cd GeFromulate2", "ls" ) expect(output).to match(/@@flurb/) end it "should show methods" do output = pry_eval( "class GeFromulate3; def self.mooflight; end ; end", "cd GeFromulate3", "ls" ) expect(output).to match(/mooflight/) end end describe "when in an object" do it "should show methods" do expect(pry_eval("cd Class.new{ def self.fooerise; end; self }", "ls")) .to match(/fooerise/) end it "should show instance variables" do expect(pry_eval("cd Class.new", "@alphooent = 1", "ls")).to match(/@alphooent/) end end end describe 'on java objects', skip: !Pry::Helpers::Platform.jruby? do it 'should omit java-esque aliases by default' do expect(pry_eval('ls java.lang.Thread.current_thread')) .to match(/\bthread_group\b/) expect(pry_eval('ls java.lang.Thread.current_thread')) .not_to match(/\bgetThreadGroup\b/) end it 'should include java-esque aliases if requested' do expect(pry_eval('ls java.lang.Thread.current_thread -J')) .to match(/\bthread_group\b/) expect(pry_eval('ls java.lang.Thread.current_thread -J')) .to match(/\bgetThreadGroup\b/) end end end pry-0.13.1/spec/commands/play_spec.rb000066400000000000000000000123331364454223300174550ustar00rootroot00000000000000# frozen_string_literal: true # This command needs a TONNE more tests for it, but i can't figure out # how to do them yet, and i really want to release. Sorry. Someone # come along and do a better job. describe "play" do before do @o = Object.new @t = pry_tester(@o) end describe "with an argument" do # can't think of a f*cking way to test this!! describe "implied file" do # it 'should play from the file associated with the current binding' do # # require 'fixtures/play_helper' # end # describe "integer" do # it "should process one line from pry_instance.last_file" do # @t.process_command 'play --lines 1', @eval_str # @eval_str.should =~ /bing = :bing\n/ # end # end # describe "range" do # it "should process multiple lines at once from pry_instance.last_file" do # @t.process_command 'play --lines 1..3', @eval_str # [/bing = :bing\n/, /bang = :bang\n/, /bong = :bong\n/].each { |str| # @eval_str.should =~ str # } # end end end describe "playing a file" do it 'should play a file' do @t.process_command 'play spec/fixtures/whereami_helper.rb' expect(@t.eval_string).to eq unindent(<<-STR) # frozen_string_literal: true # rubocop:disable Layout/EmptyLineBetweenDefs class Cor def a; end def b; end def c; end def d; end end # rubocop:enable Layout/EmptyLineBetweenDefs STR end it 'should output file contents with print option' do @t.process_command 'play --print spec/fixtures/whereami_helper.rb' expect(@t.last_output).to eq( " 1: \# frozen_string_literal: true\n" \ " 2: \n" \ " 3: \# rubocop:disable Layout/EmptyLineBetweenDefs\n" \ " 4: class Cor\n" \ " 5: def a; end\n" \ " 6: def b; end\n" \ " 7: def c; end\n" \ " 8: def d; end\n" \ " 9: end\n" \ "10: \# rubocop:enable Layout/EmptyLineBetweenDefs\n" ) end end describe "whatever" do before do def @o.test_method :test_method_content end end it 'should play documentation with the -d switch' do @o.singleton_class.send :remove_method, :test_method # @v = 10 # @y = 20 def @o.test_method :test_method_content end @t.process_command 'play -d test_method' expect(@t.eval_string).to eq unindent(<<-STR) @v = 10 @y = 20 STR end it 'should restrict -d switch with --lines' do @o.singleton_class.send :remove_method, :test_method # @x = 0 # @v = 10 # @y = 20 # @z = 30 def @o.test_method :test_method_content end @t.process_command 'play -d test_method --lines 2..3' expect(@t.eval_string).to eq unindent(<<-STR) @v = 10 @y = 20 STR end it 'has pretty error messages when -d cant find object' do expect { @t.process_command "play -d sdfsdf" } .to raise_error(Pry::CommandError, /Cannot locate/) end it 'should play a method (a single line)' do @t.process_command 'play test_method --lines 2' expect(@t.eval_string).to eq ":test_method_content\n" end it 'should properly reindent lines' do @o.singleton_class.send :remove_method, :test_method def @o.test_method 'hello world' end @t.process_command 'play test_method --lines 2' expect(@t.eval_string).to eq "'hello world'\n" end it 'should APPEND to the input buffer when playing a method line, not replace it' do @t.eval_string = unindent(<<-STR) def another_test_method STR @t.process_command 'play test_method --lines 2' expect(@t.eval_string).to eq unindent(<<-STR) def another_test_method :test_method_content STR end it 'should play a method (multiple lines)' do @o.singleton_class.send :remove_method, :test_method def @o.test_method @var0 = 10 @var1 = 20 @var2 = 30 @var3 = 40 end @t.process_command 'play test_method --lines 3..4' expect(@t.eval_string).to eq unindent(<<-STR, 0) @var1 = 20 @var2 = 30 STR end describe "play -i" do it 'should play multi-ranged input expressions' do a = b = c = d = e = 0 redirect_pry_io( InputTester.new( 'a += 1', 'b += 1', 'c += 1', 'd += 1', 'e += 1', 'play -i 1..3' ), StringIO.new ) do binding.pry # rubocop:disable Lint/Debugger end [a, b, c].all? { |v| expect(v).to eq 2 } expect(d).to eq 1 expect(e).to eq 1 end end describe "play -e" do it 'should run an expression from given line number' do @o.singleton_class.send :remove_method, :test_method def @o.test_method @s = [ 1, 2, 3, 4, 5, 6 ] end @t.process_command 'play test_method -e 2' expect(@t.eval_string).to eq unindent(<<-STR, 0) @s = [ 1, 2, 3, 4, 5, 6 ] STR end end end end pry-0.13.1/spec/commands/pry_backtrace_spec.rb000066400000000000000000000003551364454223300213220ustar00rootroot00000000000000# frozen_string_literal: true describe "pry_backtrace" do before do @t = pry_tester end it 'should print a backtrace' do @t.process_command 'pry-backtrace' expect(@t.last_output).to start_with('Backtrace:') end end pry-0.13.1/spec/commands/raise_up_spec.rb000066400000000000000000000036131364454223300203200ustar00rootroot00000000000000# frozen_string_literal: true describe "raise-up" do before do @self = "Pad.self = self" @inner = "Pad.inner = self" @outer = "Pad.outer = self" end after do Pad.clear end it "should raise the exception with raise-up" do redirect_pry_io(InputTester.new("raise NoMethodError", "raise-up NoMethodError")) do expect { Object.new.pry }.to raise_error NoMethodError end end it "should raise an unamed exception with raise-up" do redirect_pry_io(InputTester.new("raise 'stop'", "raise-up 'noreally'")) do expect { Object.new.pry }.to raise_error(RuntimeError, "noreally") end end it "should eat the exception at the last new pry instance on raise-up" do redirect_pry_io(InputTester.new(":inner.pry", "raise NoMethodError", @inner, "raise-up NoMethodError", @outer, "exit-all")) do Pry.start(:outer) end expect(Pad.inner).to eq :inner expect(Pad.outer).to eq :outer end it "should raise the most recently raised exception" do expect { mock_pry("raise NameError, 'homographery'", "raise-up") } .to raise_error(NameError, 'homographery') end it "should allow you to cd up and (eventually) out" do redirect_pry_io(InputTester.new("cd :inner", "raise NoMethodError", @inner, "deep = :deep", "cd deep", "Pad.deep = self", "raise-up NoMethodError", "raise-up", @outer, "raise-up", "exit-all")) do expect { Pry.start(:outer) }.to raise_error NoMethodError end expect(Pad.deep).to eq :deep expect(Pad.inner).to eq :inner expect(Pad.outer).to eq :outer end it "should jump immediately out of nested contexts with !" do expect { mock_pry("cd 1", "cd 2", "cd 3", "raise-up! 'fancy that...'") } .to raise_error(RuntimeError, 'fancy that...') end end pry-0.13.1/spec/commands/reload_code_spec.rb000066400000000000000000000016521364454223300207520ustar00rootroot00000000000000# frozen_string_literal: true describe "reload_code" do describe "reload_current_file" do it 'raises an error source code not found' do expect do eval <<-RUBY, TOPLEVEL_BINDING, 'does_not_exist.rb', 1 pry_eval(binding, "reload-code") RUBY end.to raise_error(Pry::CommandError) end it 'raises an error when class not found' do expect do pry_eval( "cd Class.new(Class.new{ def goo; end; public :goo })", "reload-code" ) end.to raise_error(Pry::CommandError) end it 'reloads pry commmand' do expect(pry_eval("reload-code reload-code")).to match(/reload-code was reloaded!/) end it 'raises an error when pry command not found' do expect do pry_eval( "reload-code not-a-real-command" ) end.to raise_error(Pry::CommandError, /Cannot locate not-a-real-command!/) end end end pry-0.13.1/spec/commands/ri_command_spec.rb000066400000000000000000000003511364454223300206150ustar00rootroot00000000000000# frozen_string_literal: true describe "ri" do it "prints an error message without an argument" do expect(pry_eval("ri")).to include( "Please provide a class, module, or method name (e.g: ri Array#push)" ) end end pry-0.13.1/spec/commands/save_file_spec.rb000066400000000000000000000124121364454223300204430ustar00rootroot00000000000000# frozen_string_literal: true require 'tempfile' describe "save-file" do before do @tf = Tempfile.new(["pry", ".py"]) @path = @tf.path @t = pry_tester end after do @tf.close(true) end describe "-f" do it 'should save a file to a file' do temp_file do |f| path = f.path f.puts ":cute_horse" f.flush @t.eval("save-file '#{path}' --to '#{@path}'") expect(File.read(@path)).to eq(File.read(path)) end end end describe "-i" do it 'should save input expressions to a file (single expression)' do @t.eval ':horse_nostrils' @t.eval "save-file -i 1 --to '#{@path}'" expect(File.read(@path)).to eq(":horse_nostrils\n") end it "should display a success message on save" do @t.eval ':horse_nostrils' expect(@t.eval("save-file -i 1 --to '#{@path}'")).to match(/successfully saved/) end it 'should save input expressions to a file (range)' do @t.eval ':or_nostrils', ':sucking_up_all_the_oxygen', ':or_whatever' @t.eval "save-file -i 1..2 --to '#{@path}'" expect(File.read(@path)).to eq(":or_nostrils\n:sucking_up_all_the_oxygen\n") end it 'should save multi-ranged input expressions' do @t.eval ':or_nostrils', ':sucking_up_all_the_oxygen', ':or_whatever', ':baby_ducks', ':cannot_escape' @t.eval "save-file -i 1..2 -i 4..5 --to '#{@path}'" expect(File.read(@path)).to eq( ":or_nostrils\n:sucking_up_all_the_oxygen\n:baby_ducks\n:cannot_escape\n" ) end end describe "saving methods" do before do @o = Object.new def @o.baby :baby end def @o.bang :bang end @t = pry_tester(@o) end describe "single method" do it 'should save a method to a file' do @t.eval "save-file --to '#{@path}' baby" expect(File.read(@path)).to eq(Pry::Method.from_obj(@o, :baby).source) end it "should display a success message on save" do expect(@t.eval("save-file --to '#{@path}' baby")) .to match(/successfully saved/) end it 'should save a method to a file truncated by --lines' do @t.eval "save-file --to '#{@path}' baby --lines 2..4" # must add 1 as first line of method is 1 expect(File.read(@path)).to eq( Pry::Method.from_obj(@o, :baby).source.lines.to_a[1..5].join ) end end # TODO: do we want to reintroduce this spec?? # # describe "multiple method" do # it 'should save multiple methods to a file' do # @t.eval "save-file #{@path} -m baby -m bang" # File.read(@path).should == Pry::Method.from_obj(@o, :baby).source + # Pry::Method.from_obj(@o, :bang).source # end # it 'should save multiple methods to a file trucated by --lines' do # @t.eval "save-file #{@path} -m baby -m bang --lines 2..-2" # # must add 1 as first line of method is 1 # File.read(@path).should == (Pry::Method.from_obj(@o, :baby).source + # Pry::Method.from_obj(@o, :bang).source).lines.to_a[1..-2].join # end # it 'should save multiple methods to a file trucated by --lines 1 ' \ # '(single parameter, not range)' do # @t.eval "save-file #{@path} -m baby -m bang --lines 1" # # must add 1 as first line of method is 1 # File.read(@path).should == (Pry::Method.from_obj(@o, :baby).source + # Pry::Method.from_obj(@o, :bang).source).lines.to_a[0] # end # end end describe "overwrite by default (no --append)" do it 'should overwrite specified file with new input' do @t.eval ':horse_nostrils' @t.eval "save-file -i 1 --to '#{@path}'" @t.eval ':sucking_up_all_the_oxygen' @t.eval "save-file -i 2 --to '#{@path}'" expect(File.read(@path)).to eq(":sucking_up_all_the_oxygen\n") end end describe "--append" do it 'should append to end of specified file' do @t.eval ':horse_nostrils' @t.eval "save-file -i 1 --to '#{@path}'" @t.eval ':sucking_up_all_the_oxygen' @t.eval "save-file -i 2 --to '#{@path}' -a" expect(File.read(@path)).to eq( ":horse_nostrils\n:sucking_up_all_the_oxygen\n" ) end end describe "saving commands" do it 'should save a command to a file' do @t.eval "save-file --to '#{@path}' show-source" cmd_source = Pry.config.commands["show-source"].source expect(File.read(@path)).to eq(cmd_source) end end # TODO: reintroduce these specs at some point? # # describe "combined options" do # before do # @o = Object.new # def @o.baby # :baby # end # @t = pry_tester(@o) # end # it 'should save input cache and a method to a file (in that order)' do # @t.eval ":horse_nostrils" # @t.eval "save-file -i 1 -m baby #{@path}" # File.read(@path).should == ":horse_nostrils\n" + # Pry::Method.from_obj(@o, :baby).source # end # it 'should select a portion to save using --lines' do # @t.eval ":horse_nostrils" # @t.eval "save-file -i 1 -m baby #{@path} --lines 2..-2" # str = ":horse_nostrils\n" + Pry::Method.from_obj(@o, :baby).source # File.read(@path).should == str.lines.to_a[1..-2].join # end # end end pry-0.13.1/spec/commands/shell_command_spec.rb000066400000000000000000000054001364454223300213120ustar00rootroot00000000000000# frozen_string_literal: true describe Pry::Command::ShellCommand do describe 'cd' do before do @o = Object.new @t = pry_tester(@o) do def command_state Pry::CommandState.default.state_for(Pry::Command::ShellCommand.match) end end end after { Pry::CommandState.default.reset(Pry::Command::ShellCommand.match) } describe ".cd" do before do allow(Dir).to receive(:chdir) end it "saves the current working directory" do expect(Dir).to receive(:pwd).and_return("initial_path") @t.eval ".cd new_path" expect(@t.command_state.old_pwd).to eq("initial_path") end describe "given a path" do it "sends the path to File.expand_path" do expect(Dir).to receive(:chdir).with(File.expand_path("new_path")) @t.eval ".cd new_path" end end describe "given an empty string" do it "sends ~ to File.expand_path" do expect(Dir).to receive(:chdir).with(File.expand_path("~")) @t.eval ".cd " end end describe "given a dash" do describe "given no prior directory" do it "raises the correct error" do expect { @t.eval ".cd -" } .to raise_error(StandardError, "No prior directory available") end end describe "given a prior directory" do it "sends the user's last pry working directory to File.expand_path" do expect(Dir).to receive(:pwd).twice.and_return("initial_path") expect(Dir).to receive(:chdir).with(File.expand_path("new_path")) @t.eval ".cd new_path" expect(Dir).to receive(:chdir).with(File.expand_path("initial_path")) @t.eval ".cd -" end end end describe "with CDPATH" do let(:cdpath) { File.expand_path(File.join('spec', 'fixtures', 'cdpathdir')) } let(:nonexisting_path) { File.expand_path('nonexisting_path') } let(:long_cdpath) do [nonexisting_path, cdpath].join(File::PATH_SEPARATOR) end describe "when it is defined" do before do @stub = allow_any_instance_of(described_class).to receive(:cd_path_env) end describe "simple cdpath" do it "cd's into the dir" do @stub.and_return(cdpath) expect(Dir).to receive(:chdir).with(cdpath) pry_eval '.cd cdpathdir' end end describe "complex cdpath" do it "cd's into the dir" do @stub.and_return(long_cdpath) expect(Dir).to receive(:chdir).with(cdpath) pry_eval '.cd cdpathdir' end end end end end end end pry-0.13.1/spec/commands/show_doc_spec.rb000066400000000000000000000006511364454223300203150ustar00rootroot00000000000000# frozen_string_literal: true describe "show-doc" do before do @obj = Object.new # obj docs def @obj.sample_method; end end it "emits a deprecation warning" do expect(pry_eval(binding, 'show-doc @obj.sample_method')) .to match(/WARNING: the show-doc command is deprecated/) end it "shows docs" do expect(pry_eval(binding, 'show-doc @obj.sample_method')).to match(/obj docs/) end end pry-0.13.1/spec/commands/show_input_spec.rb000066400000000000000000000005651364454223300207130ustar00rootroot00000000000000# frozen_string_literal: true describe "show-input" do before do @t = pry_tester end it 'should correctly show the current lines in the input buffer' do @t.push(*unindent(<<-STR).split("\n")) def hello puts :bing STR @t.process_command 'show-input' expect(@t.last_output).to match(/\A\d+: def hello\n\d+: puts :bing/) end end pry-0.13.1/spec/commands/show_source_spec.rb000066400000000000000000001410101364454223300210430ustar00rootroot00000000000000# frozen_string_literal: true describe "show-source" do # rubocop:disable Metrics/BlockLength def define_persistent_class(file, class_body) file.puts(class_body) file.close require(file.path) end before do @o = Object.new def @o.sample_method :sample end Object.remove_const :Test if Object.const_defined? :Test Object.const_set(:Test, Module.new) end after do Pad.clear end it "should output a method's source" do expect(pry_eval(binding, 'show-source @o.sample_method')).to match(/def @o.sample/) end it "should output help" do expect(pry_eval('show-source -h')).to match(/Usage:\s+show-source/) end it "should output a method's source with line numbers" do expect(pry_eval(binding, 'show-source -l @o.sample_method')) .to match(/\d+: def @o.sample/) end it "should output a method's source with line numbers starting at 1" do expect(pry_eval(binding, 'show-source -b @o.sample_method')) .to match(/1: def @o.sample/) end it "should output a method's source if inside method and no name given" do def @o.sample pry_eval(binding, 'show-source') end docs = @o.sample expect(docs).to match(/def @o.sample/) end it "should output a method's source inside method using the -l switch" do def @o.sample pry_eval(binding, 'show-source -l') end docs = @o.sample expect(docs).to match(/def @o.sample/) end it "should find methods even if there are spaces in the arguments" do def @o.foo(*_bars) @foo = "Mr flibble" self end out = pry_eval(binding, "show-source @o.foo('bar', 'baz bam').foo") expect(out).to match(/Mr flibble/) end it "should find methods even if the object overrides method method" do _c = Class.new do def method 98 end end expect(pry_eval(binding, "show-source _c.new.method")).to match(/98/) end it "should not show the source when a non-extant method is requested" do _c = Class.new do def method 98 end end expect(mock_pry(binding, "show-source _c#wrongmethod")) .to match(/Couldn't locate/) end it "doesn't show the source and deliver an error message without exclamation point" do _c = Class.new error_message = "Error: Couldn't locate a definition for _c#wrongmethod\n" expect(mock_pry(binding, "show-source _c#wrongmethod")).to eq(error_message) end it "should find instance_methods if the class overrides instance_method" do _c = Class.new do def method 98 end def self.instance_method 789 end end expect(pry_eval(binding, "show-source _c#method")).to match(/98/) end it "should find instance methods with self#moo" do _c = Class.new do def moo "ve over!" end end expect(pry_eval(binding, "cd _c", "show-source self#moo")).to match(/ve over/) end it "should not find instance methods with self.moo" do _c = Class.new do def moo "ve over!" end end expect { pry_eval(binding, 'cd _c', 'show-source self.moo') } .to raise_error(Pry::CommandError, /Couldn't locate/) end it "should find normal methods with self.moo" do _c = Class.new do def self.moo "ve over!" end end expect(pry_eval(binding, 'cd _c', 'show-source self.moo')).to match(/ve over/) end it "should not find normal methods with self#moo" do _c = Class.new do def self.moo "ve over!" end end expect { pry_eval(binding, 'cd _c', 'show-source self#moo') } .to raise_error(Pry::CommandError, /Couldn't locate/) end it "should find normal methods (i.e non-instance methods) by default" do _c = Class.new do def self.moo "ve over!" end end expect(pry_eval(binding, "cd _c", "show-source moo")).to match(/ve over/) end it "should find instance methods if no normal methods available" do _c = Class.new do def moo "ve over!" end end expect(pry_eval(binding, "cd _c", "show-source moo")).to match(/ve over/) end describe "with -e option" do before do class FooBar def bar :bar end end end after do Object.remove_const(:FooBar) end it "shows the source code for the returned value as Ruby" do ReplTester.start target: binding do input 'show-source -e FooBar.new' output(/class FooBar/) end end end it "should raise a CommandError when super method doesn't exist" do def @o.foo(*bars); end expect { pry_eval(binding, "show-source --super @o.foo") } .to raise_error(Pry::CommandError, /No superclass found/) end it "should output the source of a method defined inside Pry" do out = pry_eval("def dyn_method\n:test\nend", 'show-source dyn_method') expect(out).to match(/def dyn_method/) Object.remove_method :dyn_method end it 'should output source for an instance method defined inside pry' do pry_tester.tap do |t| t.eval "class Test::A\n def yo\n end\nend" expect(t.eval('show-source Test::A#yo')).to match(/def yo/) end end it 'should output source for a repl method defined using define_method' do pry_tester.tap do |t| t.eval "class Test::A\n define_method(:yup) {}\nend" expect(t.eval('show-source Test::A#yup')).to match(/define_method\(:yup\)/) end end it "should output the source of a command defined inside Pry" do command_definition = %( Pry.config.commands.command "hubba-hubba" do puts "that's what she said!" end ) out = pry_eval(command_definition, 'show-source hubba-hubba') expect(out).to match(/what she said/) Pry.config.commands.delete "hubba-hubba" end context "when there's no source code but the comment exists" do before do class Foo # Bingo. def bar; end end allow_any_instance_of(Pry::Method).to receive(:source).and_return(nil) end after do Object.remove_const(:Foo) end it "outputs zero line numbers" do out = pry_eval('show-source Foo#bar') expect(out).to match(/ Owner:\sFoo .+ Number\sof\slines:\s0 .+ \*\*\sWarning:\sCannot\sfind\scode\sfor\s'bar'\s\(source_location\sis\snil\) /mx) end end describe "finding super methods with help of `--super` switch" do before do class Foo def foo(*_bars) :super_wibble end end end after do Object.remove_const(:Foo) end it "finds super methods with explicit method argument" do o = Foo.new def o.foo(*_bars) :wibble end expect(pry_eval(binding, "show-source --super o.foo")).to match(/:super_wibble/) end it "finds super methods without explicit method argument" do o = Foo.new def o.foo(*bars) @foo = :wibble pry_eval(binding, 'show-source --super') end expect(o.foo).to match(/:super_wibble/) end it "finds super methods with multiple --super " do o = Foo.new o.extend( Module.new do def foo :nibble end end ) def o.foo(*bars) @foo = :wibble pry_eval(binding, 'show-source --super --super') end expect(o.foo).to match(/:super_wibble/) end end describe "on sourcable objects" do it "should output source defined inside pry" do pry_tester.tap do |t| t.eval "hello = proc { puts 'hello world!' }" expect(t.eval("show-source hello")).to match(/proc \{ puts/) end end it "should output source for procs/lambdas stored in variables" do _hello = proc { puts 'hello world!' } expect(pry_eval(binding, 'show-source _hello')).to match(/proc \{ puts/) end it "should output source for procs/lambdas stored in constants" do HELLO = proc { puts 'hello world!' } expect(pry_eval(binding, "show-source HELLO")).to match(/proc \{ puts/) Object.remove_const(:HELLO) end it "should output source for method objects" do def @o.hi puts 'hi world' end _meth = @o.method(:hi) expect(pry_eval(binding, "show-source _meth")).to match(/puts 'hi world'/) end describe "on variables that shadow methods" do before do @t = pry_tester.eval(unindent(<<-SHADOWED_VAR)) class ::TestHost def hello hello = proc { ' smile ' } _foo = hello pry_tester(binding) end end ::TestHost.new.hello SHADOWED_VAR end after { Object.remove_const(:TestHost) } it "source of variable takes precedence over method that is being shadowed" do source = @t.eval('show-source hello') expect(source).not_to match(/def hello/) expect(source).to match(/proc \{ ' smile ' \}/) end it "source of method being shadowed should take precedence over variable if given self.meth_name syntax" do expect(@t.eval('show-source self.hello')).to match(/def hello/) end end end describe "on variable or constant" do before do class TestHost def hello "hi there" end end end after { Object.remove_const(:TestHost) } it "outputs source of its class if variable doesn't respond to source_location" do _test_host = TestHost.new expect(pry_eval(binding, 'show-source _test_host')) .to match(/class TestHost\n.*def hello/) end it "outputs source of its class if constant doesn't respond to source_location" do TEST_HOST = TestHost.new expect(pry_eval(binding, 'show-source TEST_HOST')) .to match(/class TestHost\n.*def hello/) Object.remove_const(:TEST_HOST) end end describe "on modules" do before do class ShowSourceTestSuperClass def alpha; end end class ShowSourceTestClass < ShowSourceTestSuperClass def alpha; end end module ShowSourceTestSuperModule def alpha; end end module ShowSourceTestModule include ShowSourceTestSuperModule def alpha; end end ShowSourceTestClassWeirdSyntax = Class.new do def beta; end end ShowSourceTestModuleWeirdSyntax = Module.new do def beta; end end end after do Object.remove_const :ShowSourceTestSuperClass Object.remove_const :ShowSourceTestClass Object.remove_const :ShowSourceTestClassWeirdSyntax Object.remove_const :ShowSourceTestSuperModule Object.remove_const :ShowSourceTestModule Object.remove_const :ShowSourceTestModuleWeirdSyntax end describe "basic functionality, should find top-level module definitions" do it 'should show source for a class' do expect(pry_eval('show-source ShowSourceTestClass')) .to match(/class ShowSourceTestClass.*?def alpha/m) end it 'should show source for a super class' do expect(pry_eval('show-source -s ShowSourceTestClass')) .to match(/class ShowSourceTestSuperClass.*?def alpha/m) end it 'should show source for a module' do expect(pry_eval('show-source ShowSourceTestModule')) .to match(/module ShowSourceTestModule/) end it 'should show source for an ancestor module' do expect(pry_eval('show-source -s ShowSourceTestModule')) .to match(/module ShowSourceTestSuperModule/) end it 'should show source for a class when Const = Class.new syntax is used' do expect(pry_eval('show-source ShowSourceTestClassWeirdSyntax')) .to match(/ShowSourceTestClassWeirdSyntax = Class.new/) end it 'should show source for a super class when Const = Class.new syntax is used' do expect(pry_eval('show-source -s ShowSourceTestClassWeirdSyntax')) .to match(/class Object/) end it 'should show source for a module when Const = Module.new syntax is used' do expect(pry_eval('show-source ShowSourceTestModuleWeirdSyntax')) .to match(/ShowSourceTestModuleWeirdSyntax = Module.new/) end end before do pry_eval(unindent(<<-CLASSES)) class Dog def woof end end class TobinaMyDog < Dog def woof end end CLASSES end after do Object.remove_const :Dog Object.remove_const :TobinaMyDog end describe "in REPL" do it 'should find class defined in repl' do expect(pry_eval('show-source TobinaMyDog')).to match(/class TobinaMyDog/) end it 'should find superclass defined in repl' do expect(pry_eval('show-source -s TobinaMyDog')).to match(/class Dog/) end end it 'should lookup module name with respect to current context' do temporary_constants(:AlphaClass, :BetaClass) do class BetaClass def alpha; end end class AlphaClass class BetaClass def beta; end end end expect(pry_eval(AlphaClass, 'show-source BetaClass')).to match(/def beta/) end end it 'should lookup nested modules' do temporary_constants(:AlphaClass) do class AlphaClass class BetaClass def beta; end end end expect(pry_eval('show-source AlphaClass::BetaClass')).to match(/class Beta/) end end # note that pry assumes a class is only monkey-patched at most # ONCE per file, so will not find multiple monkeypatches in the # SAME file. describe "show-source -a" do let(:tempfile) { Tempfile.new(%w[pry .rb]) } context "when there are instance method monkeypatches in different files" do before do define_persistent_class(tempfile, <<-CLASS) class TestClass def alpha; end end CLASS class TestClass def beta; end end end after do Object.remove_const(:TestClass) tempfile.unlink end it "shows the source for all monkeypatches" do result = pry_eval('show-source TestClass -a') expect(result).to match(/def alpha/) expect(result).to match(/def beta/) end end context "when there are class method monkeypatches in different files" do before do define_persistent_class(tempfile, <<-CLASS) class TestClass def self.alpha; end end CLASS class TestClass def self.beta; end end end after do Object.remove_const(:TestClass) tempfile.unlink end it "shows the source for all monkeypatches" do result = pry_eval('show-source TestClass -a') expect(result).to match(/def self.alpha/) expect(result).to match(/def self.beta/) end end context "when there are class-eval monkeypatches in different files" do let(:tempfile) { Tempfile.new(%w[pry .rb]) } before do define_persistent_class(tempfile, <<-CLASS) class TestClass def self.alpha; end end CLASS TestClass.class_eval do def class_eval_method :bing end end end after do Object.remove_const(:TestClass) tempfile.unlink end it "shows the source for all monkeypatches" do result = pry_eval('show-source TestClass -a') expect(result).to match(/def class_eval_method/) end it "ignores -a because object is not a module" do result = pry_eval('show-source TestClass#class_eval_method -a') expect(result).to match(/bing/) end end context "when there are instance-eval monkeypatches in different files" do let(:tempfile) { Tempfile.new(%w[pry .rb]) } before do define_persistent_class(tempfile, <<-CLASS) class TestClass def self.alpha; end end CLASS TestClass.instance_eval do def instance_eval_method :bing end end end after do Object.remove_const(:TestClass) tempfile.unlink end it "shows the source for all monkeypatches" do result = pry_eval('show-source TestClass -a') expect(result).to match(/def instance_eval_method/) end end context "when -a is not used and there are multiple monkeypatches" do let(:tempfile) { Tempfile.new(%w[pry .rb]) } before do define_persistent_class(tempfile, <<-CLASS) class TestClass def self.alpha; end end CLASS class TestClass def beta; end end end after do Object.remove_const(:TestClass) tempfile.unlink end it "mentions available monkeypatches" do result = pry_eval('show-source TestClass') expect(result).to match(/available monkeypatches/) end end context "when -a is not used and there's only one candidate for the class" do before do # alpha class TestClass def alpha; end end end after do Object.remove_const(:TestClass) end it "doesn't mention anything about monkeypatches" do result = pry_eval('show-source TestClass') expect(result).not_to match(/available monkeypatches/) end end end describe "when show-source is invoked without a method or class argument" do before do module TestHost class M def alpha; end def beta; end end module C end module D def self.invoked_in_method pry_eval(binding, 'show-source') end end end end after do Object.remove_const(:TestHost) end describe "inside a module" do it 'should display module source by default' do out = pry_eval(TestHost::M, 'show-source') expect(out).to match(/class M/) expect(out).to match(/def alpha/) expect(out).to match(/def beta/) end it 'should be unable to find module source if no methods defined' do expect { pry_eval(TestHost::C, 'show-source') } .to raise_error(Pry::CommandError, /Couldn't locate/) end it( 'displays method code (rather than class) if Pry started inside ' \ 'method binding' ) do out = TestHost::D.invoked_in_method expect(out).to match(/invoked_in_method/) expect(out).not_to match(/module D/) end it 'should display class source when inside instance' do out = pry_eval(TestHost::M.new, 'show-source') expect(out).to match(/class M/) expect(out).to match(/def alpha/) expect(out).to match(/def beta/) end it 'should allow options to be passed' do out = pry_eval(TestHost::M, 'show-source -b') expect(out).to match(/\d:\s*class M/) expect(out).to match(/\d:\s*def alpha/) expect(out).to match(/\d:\s*def beta/) end describe "should skip over broken modules" do before do module BabyDuck module Muesli binding.eval("def a; end", "dummy.rb", 1) binding.eval("def b; end", "dummy.rb", 2) binding.eval("def c; end", "dummy.rb", 3) end module Muesli def d; end def e; end end end end after do Object.remove_const(:BabyDuck) end it 'should return source for first valid module' do out = pry_eval('show-source BabyDuck::Muesli') expect(out).to match(/def d; end/) expect(out).not_to match(/def a; end/) end end describe "monkey-patched C modules" do # Monkey-patch Array and add 15 methods, so its internal rank is # high enough to make this definition primary. class Array 15.times do |i| define_method(:"doge#{i}") do :"doge#{i}" end end end describe "when current context is a C object" do it "should display a warning, and not monkey-patched definition" do out = pry_eval([1, 2, 3], 'show-source') expect(out).not_to match(/doge/) expect(out).to match(/Pry cannot display the information/) end it "recommends to use the --all switch when other candidates are found" do out = pry_eval([], 'show-source') expect(out).to match(/'--all' switch/i) end end describe "when current context is something other than a C object" do it "should display a candidate, not a warning" do out = pry_eval('show-source Array') expect(out).to match(/doge/) expect(out).not_to match(/warning/i) end end end end end end describe "on commands" do let(:default_commands) { Pry.config.commands } let(:command_set) do Pry::CommandSet.new { import Pry::Commands } end before { Pry.config.commands = command_set } after { Pry.config.commands = default_commands } describe "block commands" do it 'should show source for an ordinary command' do command_set.command('foo', :body_of_foo) {} expect(pry_eval(binding, 'show-source foo')).to match(/:body_of_foo/) end it "should output source of commands using special characters" do command_set.command('!%$', 'I gots the yellow fever') {} expect(pry_eval(binding, 'show-source !%$')).to match(/yellow fever/) end it 'should show source for a command with spaces in its name' do command_set.command('foo bar', :body_of_foo_bar) {} expect(pry_eval(binding, 'show-source foo bar')).to match(/:body_of_foo_bar/) end it 'should show source for a command by listing name' do command_set.command(/foo(.*)/, :body_of_foo_bar_regex, listing: "bar") {} expect(pry_eval(binding, 'show-source bar')).to match(/:body_of_foo_bar_regex/) end end describe "create_command commands" do it 'should show source for a command' do command_set.create_command "foo", "babble" do def process :body_of_foo end end expect(pry_eval(binding, 'show-source foo')).to match(/:body_of_foo/) end it 'should show source for a command defined inside pry' do pry_eval %{ pry_instance.commands.create_command "foo", "babble" do def process() :body_of_foo end end } expect(pry_eval(binding, 'show-source foo')).to match(/:body_of_foo/) end end describe "real class-based commands" do before do # rubocop:disable Style/ClassAndModuleChildren class ::TemporaryCommand < Pry::ClassCommand match 'temp-command' def process :body_of_temp end end # rubocop:enable Style/ClassAndModuleChildren Pry.config.commands.add_command(::TemporaryCommand) end after do Object.remove_const(:TemporaryCommand) end it 'should show source for a command' do expect(pry_eval('show-source temp-command')).to match(/:body_of_temp/) end it 'should show source for a command defined inside pry' do pry_eval %{ class ::TemporaryCommandInPry < Pry::ClassCommand match 'temp-command-in-pry' def process() :body_of_temp end end } Pry.config.commands.add_command(::TemporaryCommandInPry) expect(pry_eval('show-source temp-command-in-pry')).to match(/:body_of_temp/) Object.remove_const(:TemporaryCommandInPry) end end end describe "should set _file_ and _dir_" do let(:tempfile) { Tempfile.new(%w[pry .rb]) } before do define_persistent_class(tempfile, <<-CLASS) class TestClass def alpha; end end CLASS end after do Object.remove_const(:TestClass) tempfile.unlink end it 'should set _file_ and _dir_ to file containing method source' do t = pry_tester t.process_command "show-source TestClass#alpha" path = tempfile.path.split('/')[0..-2].join('/') expect(t.pry.last_dir).to match(path) expect(t.pry.last_file).to match(tempfile.path) end end describe "can't find class/module code" do describe "for classes" do before do module Jesus module Pig def lillybing :lillybing end end class Brian; end class Jingle def a :doink end end class Jangle < Jingle; include Pig; end class Bangle < Jangle; end end end after do Object.remove_const(:Jesus) end it 'shows superclass code' do t = pry_tester t.process_command "show-source Jesus::Jangle" expect(t.last_output).to match(/doink/) end it 'ignores included modules' do t = pry_tester t.process_command "show-source Jesus::Jangle" expect(t.last_output).not_to match(/lillybing/) end it 'errors when class has no superclass to show' do t = pry_tester expect { t.process_command "show-source Jesus::Brian" } .to raise_error(Pry::CommandError, /Couldn't locate/) end it 'shows warning when reverting to superclass code' do t = pry_tester t.process_command "show-source Jesus::Jangle" expect(t.last_output).to match( /Warning.*?Cannot find.*?Jesus::Jangle.*Showing.*Jesus::Jingle instead/ ) end it( 'shows nth level superclass code (when no intermediary ' \ 'superclasses have code either)' ) do t = pry_tester t.process_command "show-source Jesus::Bangle" expect(t.last_output).to match(/doink/) end it 'shows correct warning when reverting to nth level superclass' do t = pry_tester t.process_command "show-source Jesus::Bangle" expect(t.last_output).to match( /Warning.*?Cannot find.*?Jesus::Bangle.*Showing.*Jesus::Jingle instead/ ) end end describe "for modules" do before do module Jesus module Alpha def alpha :alpha end end module Zeta; end module Beta include Alpha end module Gamma include Beta end end end after do Object.remove_const(:Jesus) end it 'shows included module code' do t = pry_tester t.process_command "show-source Jesus::Beta" expect(t.last_output).to match(/alpha/) end it 'shows warning when reverting to included module code' do t = pry_tester t.process_command "show-source Jesus::Beta" expect(t.last_output).to match( /Warning.*?Cannot find.*?Jesus::Beta.*Showing.*Jesus::Alpha instead/ ) end it 'errors when module has no included module to show' do t = pry_tester expect { t.process_command "show-source Jesus::Zeta" } .to raise_error(Pry::CommandError, /Couldn't locate/) end it( 'shows nth level included module code (when no intermediary modules ' \ 'have code either)' ) do t = pry_tester t.process_command "show-source Jesus::Gamma" expect(t.last_output).to match(/alpha/) end it 'shows correct warning when reverting to nth level included module' do t = pry_tester t.process_command "show-source Jesus::Gamma" expect(t.last_output).to match( /Warning.*?Cannot find.*?Jesus::Gamma.*Showing.*Jesus::Alpha instead/ ) end end end describe "show-source --doc" do context "when given a class with a doc" do before do # Foo has docs. class Foo def bar; end end end after { Object.remove_const(:Foo) } it "shows documentation for the code object along with source code" do expect(pry_eval(binding, "show-source Foo -d")).to match( /Foo has docs\.\n\s+class Foo/ ) end end context "when given a module with a doc" do before do # TestMod has docs module TestMod def foo; end end end after { Object.remove_const(:TestMod) } it "shows documentation for the code object along with source code" do expect(pry_eval(binding, "show-source TestMod -d")).to match( /TestMod has docs\n\s+module TestMod/ ) end end context "when the Const = Class.new syntax is used" do before do # TestClass has docs TestClass = Class.new do def foo; end end end after { Object.remove_const(:TestClass) } it "shows documentation for the class" do expect(pry_eval(binding, "show-source TestClass -d")).to match( /TestClass has docs\n\s+TestClass = Class.new/ ) end end context "when the Const = Module.new syntax is used" do before do # TestMod has docs TestMod = Module.new do def foo; end end end after { Object.remove_const(:TestMod) } it "shows documentation for the module" do expect(pry_eval(binding, "show-source TestMod -d")).to match( /TestMod has docs\n\s+TestMod = Module.new/ ) end end context "when given a class defined in a REPL session" do after { Object.remove_const(:TobinaMyDog) } it "shows documentation for the class" do t = pry_tester t.eval <<-RUBY # hello tobina class TobinaMyDog def woof end end RUBY expect(t.eval('show-source -d TobinaMyDog')).to match(/hello tobina/) end end context "when the current context is a non-nested class" do before do # top-level beta class BetaClass def alpha; end end class AlphaClass # nested beta class BetaClass def beta; end end end end after do [:BetaClass, :AlphaClass].each { |name| Object.remove_const(name) } end it "shows docs for the nested classes" do expect(pry_eval(AlphaClass, "show-source -d BetaClass")) .to match(/nested beta/) end end context "when given a nested class" do before do # top-level beta class BetaClass def alpha; end end class AlphaClass # nested beta class BetaClass def beta; end end end end after do [:BetaClass, :AlphaClass].each { |name| Object.remove_const(name) } end it "shows docs for the nested classes" do expect(pry_eval(AlphaClass, "show-source -d AlphaClass::BetaClass")) .to match(/nested beta/) end end context "when given a method with a doc" do before do @obj = Object.new # test doc def @obj.test_method; end end it "finds the method's documentation" do expect(pry_eval(binding, "show-source -d @obj.test_method")) .to match(/test doc/) end end context "when #call is defined on Symbol" do before do class Symbol def call; end end @obj = Object.new # test doc def @obj.test_method; end end after { Symbol.class_eval { undef :call } } it "still finds documentation" do expect(pry_eval(binding, "show-source -d @obj.test_method")) .to match(/test doc/) end end context "when no docs can be found for the given class" do before do class TestClass def test_method; end end end after { Object.remove_const(:TestClass) } it "raises Pry::CommandError" do expect { pry_eval(binding, "show-source -d TestClass") } .to raise_error(Pry::CommandError) end end context "when no docs can be found for the given method" do before do @obj = Object.new def @obj.test_method; end end it "raises Pry::CommandError" do expect { pry_eval(binding, "show-source -d @obj.test_method") } .to raise_error(Pry::CommandError) end end context "when the --line-numbers switch is provided" do before do @obj = Object.new # test doc def @obj.test_method; end end it "outputs a method's docs with line numbers" do expect(pry_eval(binding, "show-source -d --line-numbers @obj.test_method")) .to match(/\d: test doc/) end end context "when the --base-one switch is provided" do before do @obj = Object.new # test doc def @obj.test_method; end end it "outputs a method's docs with line numbering starting at 1" do expect(pry_eval(binding, "show-source -d --base-one @obj.test_method")) .to match(/1: test doc/) end end context "when the current context is a method" do it "outputs the method without needing to use its name" do obj = Object.new # test method def obj.test_method pry_eval(binding, 'show-source -d') end expect(obj.test_method).to match(/test method/) end end context "when given a proc" do it "should show documentation for object" do # this is a documentation _the_proc = proc { puts 'hello world!' } expect(mock_pry(binding, "show-source -d _the_proc")) .to match(/this is a documentation/) end end context "when no class/module arg is given" do before do module TestHost # hello there froggy module M def d; end def e; end end end end after { Object.remove_const(:TestHost) } it "returns the doc for the current module" do expect(pry_eval(TestHost::M, 'show-source -d')) .to match(/hello there froggy/) end end context "when given a 'broken' module" do before do module TestHost # hello module M binding.eval("def a; end", "dummy.rb", 1) binding.eval("def b; end", "dummy.rb", 2) binding.eval("def c; end", "dummy.rb", 3) end # goodbye module M def d; end def e; end end end end after { Object.remove_const(:TestHost) } # FIXME: THis is nto a good spec anyway, because i dont think it # SHOULD skip! it "skips over the module" do output = pry_eval('show-source -d TestHost::M') expect(output).to match(/goodbye/) expect(output).not_to match(/hello/) end end describe "should set _file_ and _dir_" do let(:tempfile) { Tempfile.new(%w[pry .rb]) } before do define_persistent_class(tempfile, <<-CLASS) class TestClass # this is alpha def alpha; end end CLASS end after do Object.remove_const(:TestClass) tempfile.unlink end it "sets _file_ and _dir_ to file containing method source" do t = pry_tester t.process_command "show-source -d TestClass#alpha" path = tempfile.path.split('/')[0..-2].join('/') expect(t.pry.last_dir).to match(path) expect(t.pry.last_file).to match(tempfile.path) end end context "when provided a class without docs that has a superclass with docs" do before do # parent class class Parent def foo; end end class Child < Parent; end end after do [:Child, :Parent].each { |name| Object.remove_const(name) } end it "shows the docs of the superclass" do expect(pry_eval(binding, 'show-source -d Child')).to match(/parent class/) end it "shows a warning about superclass reversion" do expect(pry_eval(binding, 'show-source -d Child')).to match( /Warning.*?Cannot find.*?Child.*Showing.*Parent instead/ ) end end context "when provided a class without docs that has nth superclass with docs" do before do # grandparent class class Grandparent def foo; end end class Parent < Grandparent; end class Child < Parent; end end after do [:Grandparent, :Child, :Parent].each { |name| Object.remove_const(name) } end it "shows the docs of the superclass" do expect(pry_eval(binding, 'show-source -d Child')) .to match(/grandparent class/) end it "shows a warning about superclass reversion" do expect(pry_eval(binding, 'show-source -d Child')).to match( /Warning.*?Cannot find.*?Child.*Showing.*Grandparent instead/ ) end end context "when provided a class without docs that has a superclass without docs" do before do class Parent def foo; end end class Child < Parent; end end after do [:Child, :Parent].each { |name| Object.remove_const(name) } end it "raises Pry::CommandError" do expect { pry_eval(binding, 'show-source -d Child') } .to raise_error(Pry::CommandError) end end context "when the module with docs was included in another module" do before do # mod module doc module Alpha def foo; end end module Beta include Alpha end end after do [:Beta, :Alpha].each { |name| Object.remove_const(name) } end it "shows the included module's doc" do expect(pry_eval(binding, 'show-source -d Beta')) .to match(/mod module doc/) end it "shows a warning about the included module reversion" do expect(pry_eval(binding, 'show-source -d Beta')).to match( /Warning.*?Cannot find.*?Beta.*Showing.*Alpha instead/ ) end end context "when both the base mod and the included module have no docs" do before do module Alpha def foo; end end module Beta include Alpha end end after do [:Beta, :Alpha].each { |name| Object.remove_const(name) } end it "raises Pry::CommandError" do expect { pry_eval(binding, 'show-source -d Beta') } .to raise_error(Pry::CommandError) end end context "when included module has docs and there are intermediary docless modules" do before do # alpha doc module Alpha def alpha; end end module Beta include Alpha end module Gamma include Beta end end after do [:Gamma, :Beta, :Alpha].each { |name| Object.remove_const(name) } end it "shows nth level included module doc" do expect(pry_eval(binding, 'show-source -d Gamma')).to match(/alpha doc/) end it "shows a warning about module reversion" do expect(pry_eval(binding, 'show-source -d Gamma')).to match( /Warning.*?Cannot find.*?Gamma.*Showing.*Alpha instead/ ) end end context "when the --super switch is provided" do before do class Grandparent # grandparent init def initialize; end end class Parent < Grandparent # parent init def initialize; end end class Child < Parent # child init def initialize; end end @obj = Child.new # instance init def @obj.initialize; end end after do [:Grandparent, :Parent, :Child].each { |name| Object.remove_const(name) } end context "and when it's passed once" do it "finds the super method docs" do expect(pry_eval(binding, 'show-source -d --super @obj.initialize')) .to match(/child init/) end end context "and when it's passed twice" do it "finds the parent method docs" do expect(pry_eval(binding, 'show-source -d -ss @obj.initialize')) .to match(/parent init/) end end context "and when it's passed thrice" do it "finds the grandparent method docs" do expect(pry_eval(binding, 'show-source -d -sss @obj.initialize')) .to match(/parent init/) end end context "and when the super method doesn't exist" do it "raises Pry::CommandError" do expect { pry_eval(binding, 'show-source -d -ssss @obj.initialize') } .to raise_error(Pry::CommandError) end end context "and when the explicit argument is not provided" do let(:son) { Child.new } it "finds super method docs without explicit method argument" do # son init def son.initialize pry_eval(binding, 'show-source -d --super') end expect(son.initialize).to match(/child init/) end it "finds super method docs with multiple `--super` switches" do son.extend( Module.new do def initialize; end end ) # son init def son.initialize pry_eval(binding, 'show-source -d --super --super') end expect(son.initialize).to match(/child init/) end end end describe "code highlighting" do context "when there's code in the docs" do let(:klass) do Class.new do # This can initialize your class: # # a = klass.new :foo # # @param foo def initialize(foo); end end end it "highlights the code" do expect(pry_eval(binding, 'show-source -d klass#initialize')) .to match(/klass.new :foo/) # We don't want the test to rely on which colour codes are there, so # we just assert that something is being colorized. expect( pry_eval( binding, 'pry_instance.color = true', "show-source -d klass#initialize" ) ).not_to match(/klass.new :foo/) end end context "when there's inline code in the docs" do let(:klass) do Class.new do # After initializing your class with `klass.new(:inline)`, go have # fun! # # @param foo def initialize(foo); end end end it "highlights the code" do expect(pry_eval(binding, 'show-source -d klass#initialize')) .to match(/klass.new\(:inline\)/) # We don't want the test to rely on which colour codes are there, so # we just assert that something is being colorized. expect( pry_eval( binding, 'pry_instance.color = true', "show-source -d klass#initialize" ) ).not_to match(/klass.new\(:inline\)/) end end context "when there's inline code with backticks the docs" do let(:klass) do Class.new do # Convert aligned output (from many shell commands) into nested arrays: # # a = decolumnize `ls -l $HOME` # # @param output def decolumnize(output); end end end it "doesn't highlight the backticks" do output = pry_eval( binding, 'pry_instance.color = true', "show-source -d klass#decolumnize" ) expect(output).to match(/ls -l \$HOME/) expect(output).not_to match(/`ls -l \$HOME`/) end end end describe "the --all switch behavior" do let(:tempfile) { Tempfile.new(%w[pry .rb]) } context "when there are monkeypatches in different files" do before do define_persistent_class(tempfile, <<-CLASS) # file monkeypatch class TestClass def alpha; end end CLASS # local monkeypatch class TestClass def beta; end end end after do Object.remove_const(:TestClass) tempfile.unlink end it "shows them" do result = pry_eval(binding, 'show-source -d TestClass -a') expect(result).to match(/file monkeypatch/) expect(result).to match(/local monkeypatch/) end end context "when --all is not used but there are multiple monkeypatches" do before do define_persistent_class(tempfile, <<-CLASS) # alpha class TestClass def alpha; end end CLASS class TestClass def beta; end end end after do Object.remove_const(:TestClass) tempfile.unlink end it "correctly displays the number of monkeypatches" do result = pry_eval(binding, 'show-source -d TestClass') expect(result).to match(/Number of monkeypatches: 2/) end it "displays the original definition first" do result = pry_eval(binding, 'show-source -d TestClass') expect(result).to match(/alpha/) end it "mentions available monkeypatches" do result = pry_eval(binding, 'show-source -d TestClass') expect(result).to match(/available monkeypatches/) end end context "when --all is not used and there's only 1 candidate for the class" do before do # alpha class TestClass def alpha; end end end after { Object.remove_const(:TestClass) } it "doesn't mention anything about monkeypatches" do result = pry_eval(binding, 'show-source -d TestClass') expect(result).not_to match(/available monkeypatches/) end end end context "when used against a command" do let(:default_commands) { Pry.config.commands } let(:command_set) do Pry::CommandSet.new { import Pry::Commands } end before { Pry.config.commands = command_set } after { Pry.config.commands = default_commands } it "displays help for a specific command" do expect(pry_eval(binding, 'show-source -d ls')).to match(/Usage: ls/) end it "displays help for a regex command with a \"listing\"" do command_set.command(/bar(.*)/, 'Test listing', listing: 'foo') {} expect(pry_eval(binding, 'show-source -d foo')).to match(/Test listing/) end it "displays help for a command with a spaces in its name" do command_set.command('command with spaces', 'command with spaces desc') {} expect(pry_eval(binding, 'show-source -d command with spaces')).to match( /command with spaces desc/ ) end describe "class commands" do before do # pretty pink pincers class LobsterLady < Pry::ClassCommand match 'lobster-lady' description 'nada.' def process 'lobster' end end command_set.add_command(LobsterLady) end after { Object.remove_const(:LobsterLady) } context "when looking up by command name" do it "displays help" do expect(pry_eval('show-source -d lobster-lady')).to match(/nada/) end end context "when class is used (rather than command name) is used for lookup" do it "displays actual preceding comment for a class command" do expect(pry_eval('show-source -d LobsterLady')).to match(/pretty pink pincers/) end end end end end end pry-0.13.1/spec/commands/watch_expression_spec.rb000066400000000000000000000050611364454223300220750ustar00rootroot00000000000000# frozen_string_literal: true describe "watch expression" do # Custom eval that will: # 1) Create an instance of pry that can use for multiple calls # 2) Exercise the after_eval hook # 3) Return the output def watch_eval(expr) output = @tester.eval(expr) @tester.pry.hooks.exec_hook :after_eval, nil, @tester.pry output end before do @tester = pry_tester @tester.pry.hooks.clear_event_hooks(:after_eval) watch_eval('watch --delete') end it "registers the after_eval hook" do watch_eval('watch 1+1') watch_eval('') expect(@tester.pry.hooks.hook_exists?(:after_eval, :watch_expression)).to eq(true) end it "prints no watched expressions" do expect(watch_eval('watch')).to match(/No watched expressions/) end it "watches an expression" do watch_eval 'watch 1+1' expect(watch_eval('watch')).to match(/=> 2/) end it "watches a local variable" do watch_eval('foo = :bar') watch_eval('watch foo') expect(watch_eval('watch')).to match(/=> :bar/) end it "prints when an expression changes" do ReplTester.start do input 'a = 1' output '=> 1' input 'watch a' output "Watching a\nwatch: a => 1" input "a = 2" output "watch: a => 2\n=> 2" end end it "prints when an expression is mutated" do ReplTester.start do input 'a = "one"' output '=> "one"' input 'watch a' output %(Watching a\nwatch: a => "one") input "a.sub! 'o', 'p'" output %(watch: a => "pne"\n=> "pne") end end it "doesn't print when an expresison remains the same" do ReplTester.start do input 'a = 1' output '=> 1' input 'watch a' output "Watching a\nwatch: a => 1" input "a = 1" output "=> 1" end end it "continues to work if you start a second pry instance" do ReplTester.start do input 'a = 1' output '=> 1' input 'watch a' output "Watching a\nwatch: a => 1" input "a = 2" output "watch: a => 2\n=> 2" end ReplTester.start do input 'b = 1' output '=> 1' input 'watch b' output "Watching b\nwatch: b => 1" input "b = 2" output "watch: b => 2\n=> 2" end end describe "deleting expressions" do before do watch_eval('watch :keeper') watch_eval('watch :delete') watch_eval('watch -d 2') end it "keeps keeper" do expect(watch_eval('watch')).to match(/keeper/) end it "deletes delete" do expect(watch_eval('watch')).not_to match(/delete/) end end end pry-0.13.1/spec/commands/whereami_spec.rb000066400000000000000000000161511364454223300203130ustar00rootroot00000000000000# frozen_string_literal: true require 'method_source' describe "whereami" do it 'should work with methods that have been undefined' do class Cor def blimey! Cor.send :undef_method, :blimey! Pad.binding = binding end end Cor.new.blimey! # using [.] so the regex doesn't match itself expect(pry_eval(Pad.binding, 'whereami')).to match(/self[.]blimey!/) Object.remove_const(:Cor) end it 'should work in objects with no method methods' do class Cor def blimey! pry_eval(binding, 'whereami') end def method "moo" end end expect(Cor.new.blimey!).to match(/Cor[#]blimey!/) Object.remove_const(:Cor) end it 'should properly set _file_, _line_ and _dir_' do class Cor def blimey! pry_eval(binding, 'whereami', '_file_') end end expect(Cor.new.blimey!).to eq File.expand_path(__FILE__) Object.remove_const(:Cor) end if RUBY_VERSION > "2.0.0" it 'should work with prepended methods' do module Cor2 def blimey! super end end class Cor prepend Cor2 def blimey! pry_eval(binding, 'whereami') end end expect(Cor.new.blimey!).to match(/Cor2[#]blimey!/) Object.remove_const(:Cor) Object.remove_const(:Cor2) end end it 'should work in BasicObjects' do cor = Class.new(BasicObject) do def blimey! ::Kernel.binding # omnom end end.new.blimey! expect(pry_eval(cor, 'whereami')).to match(/::Kernel.binding [#] omnom/) end it( 'shows description and corrects code when __LINE__ and __FILE__ are ' \ 'outside @method.source_location' ) do class Cor def blimey! eval(<<-WHEREAMI, binding, 'spec/fixtures/example.erb', 1) pry_eval(binding, 'whereami') WHEREAMI end end expect(Cor.instance_method(:blimey!).source).to match(/pry_eval/) expect(Cor.new.blimey!).to match(/Cor#blimey!.*Look at me/m) Object.remove_const(:Cor) end it( 'shows description and corrects code when @method.source_location ' \ 'would raise an error' ) do class Cor eval <<-WHEREAMI, binding, "spec/fixtures/example.erb", 1 def blimey! pry_eval(binding, 'whereami') end WHEREAMI end expect { Cor.instance_method(:blimey!).source } .to raise_error MethodSource::SourceNotFoundError expect(Cor.new.blimey!).to match(/Cor#blimey!.*Look at me/m) Object.remove_const(:Cor) end # Now that we use stagger_output (paging output) we no longer get # the "From: " line, as we output everything in one go (not separate output.puts) # and so the user just gets a single `Error: Cannot open # "not.found.file.erb" for reading.` # which is good enough IMO. Unfortunately we can't test for it # though, as we don't hook stdout. # # it 'should display a description and error if reading the file goes wrong' do # class Cor # def blimey! # eval <<-END, binding, "not.found.file.erb", 7 # Pad.tester = pry_tester(binding) # Pad.tester.eval('whereami') # END # end # end # proc { Cor.new.blimey! }.should.raise(MethodSource::SourceNotFoundError) # Pad.tester.last_output.should =~ # /From: not.found.file.erb:7 Cor#blimey!/ # Object.remove_const(:Cor) # end it 'should show code window (not just method source) if parameter passed to whereami' do class Cor def blimey! pry_eval(binding, 'whereami 3') end end expect(Cor.new.blimey!).to match(/class Cor/) Object.remove_const(:Cor) end it 'should show entire method when -m option used' do old_size = Pry.config.default_window_size Pry.config.default_window_size = 1 old_cutoff = Pry::Command::Whereami.method_size_cutoff Pry::Command::Whereami.method_size_cutoff = 1 class Cor def blimey! @foo = 1 @bar = 2 pry_eval(binding, 'whereami -m') end end Pry::Command::Whereami.method_size_cutoff = old_cutoff Pry.config.default_window_size = old_size result = Cor.new.blimey! Object.remove_const(:Cor) expect(result).to match(/def blimey/) end it 'should show entire file when -f option used' do class Cor def blimey! pry_eval(binding, 'whereami -f') end end result = Cor.new.blimey! Object.remove_const(:Cor) expect(result).to match(/show entire file when -f option used/) end describe "-c" do it 'should show class when -c option used, and locate correct candidate' do require 'fixtures/whereami_helper' class Cor def blimey! pry_eval(binding, 'whereami -c') end end out = Cor.new.blimey! Object.remove_const(:Cor) expect(out).to match(/class Cor/) expect(out).to match(/blimey/) end it 'should show class when -c option used, and locate correct superclass' do class Cor def blimey! pry_eval(binding, 'whereami -c') end end class Horse < Cor def pig; end end out = Horse.new.blimey! Object.remove_const(:Cor) Object.remove_const(:Horse) expect(out).to match(/class Cor/) expect(out).to match(/blimey/) end it 'should show class when -c option used, and binding is outside a method' do class Cor extend RSpec::Matchers def blimey; end out = pry_eval(binding, 'whereami -c') expect(out).to match(/class Cor/) expect(out).to match(/blimey/) end Object.remove_const(:Cor) end it 'should show class when -c option used, and beginning of the class is on the' \ 'same line as another expression' do out = class Cor def blimey; end pry_eval(binding, 'whereami -c') end expect(out).to match(/class Cor/) expect(out).to match(/blimey/) Object.remove_const(:Cor) end end it 'should not show line numbers or marker when -n switch is used' do class Cor def blimey! pry_eval(binding, 'whereami -n') end end out = Cor.new.blimey! expect(out).to match(/^\s*def/) expect(out).to_not match(/\=\>/) Object.remove_const(:Cor) end it( 'uses Pry.config.default_window_size for window size when outside a method context' ) do old_size = Pry.config.default_window_size Pry.config.default_window_size = 1 _foo = :litella _foo = :pig out = pry_eval(binding, 'whereami') _foo = :punk _foo = :sanders expect(out).not_to match(/:litella/) expect(out).to match(/:pig/) expect(out).to match(/:punk/) expect(out).not_to match(/:sanders/) Pry.config.default_window_size = old_size end it "should work at the top level" do expect(pry_eval(Pry.toplevel_binding, 'whereami')).to match( /At the top level/ ) end it "should work inside a class" do expect(pry_eval(Pry, 'whereami')).to match(/Inside Pry/) end it "should work inside an object" do expect(pry_eval(Object.new, 'whereami')).to match(/Inside #'" }) error end let(:pry_instance) do instance = Pry.new instance.last_exception = exception instance end before do subject.pry_instance = pry_instance subject.output = output subject.opts = Pry::Slop.new subject.captures = [''] end context "when there wasn't an exception raised" do before { subject.pry_instance = Pry.new } it "raises Pry::CommandError" do expect { subject.process } .to raise_error(Pry::CommandError, 'No most-recent exception') end end context "when the verbose flag is missing" do before { expect(subject.opts).to receive(:verbose?).and_return(false) } it "prints only a part of the exception backtrace" do subject.process expect(subject.output.string).to eq( "\e[1mException:\e[0m RuntimeError: oops\n" \ "--\n" \ "0: /bin/pry:23:in `
'\n" \ "1: /bin/pry:23:in `
'\n" \ "2: /bin/pry:23:in `
'\n" \ "3: /bin/pry:23:in `
'\n" \ "4: /bin/pry:23:in `
'\n" ) end end context "when the verbose flag is present" do before { expect(subject.opts).to receive(:verbose?).and_return(true) } it "prints full exception backtrace" do subject.process expect(subject.output.string).to eq( "\e[1mException:\e[0m RuntimeError: oops\n" \ "--\n" \ "0: /bin/pry:23:in `
'\n" \ "1: /bin/pry:23:in `
'\n" \ "2: /bin/pry:23:in `
'\n" \ "3: /bin/pry:23:in `
'\n" \ "4: /bin/pry:23:in `
'\n" \ "5: /bin/pry:23:in `
'\n" ) end end context "when captures contains exclamations (wtf?! invocation)" do before { subject.captures = ['!'] } it "prints more of backtrace" do subject.process expect(subject.output.string).to eq( "\e[1mException:\e[0m RuntimeError: oops\n" \ "--\n" \ "0: /bin/pry:23:in `
'\n" \ "1: /bin/pry:23:in `
'\n" \ "2: /bin/pry:23:in `
'\n" \ "3: /bin/pry:23:in `
'\n" \ "4: /bin/pry:23:in `
'\n" \ "5: /bin/pry:23:in `
'\n" \ ) end end context "when given a nested exception" do let(:nested_exception) do begin begin begin raise 'inner' rescue RuntimeError raise 'outer' end end rescue RuntimeError => error error.set_backtrace(Array.new(6) { "/bin/pry:23:in `
'" }) error.cause.set_backtrace(Array.new(6) { "/bin/pry:23:in `
'" }) error end end before do if Gem::Version.new(RUBY_VERSION) <= Gem::Version.new('2.0.0') skip('Exception#cause is not supported') end pry_instance.last_exception = nested_exception end context "and when the verbose flag is missing" do before { expect(subject.opts).to receive(:verbose?).twice.and_return(false) } it "prints parts of both original and nested exception backtrace" do subject.process expect(subject.output.string).to eq( "\e[1mException:\e[0m RuntimeError: outer\n" \ "--\n" \ "0: /bin/pry:23:in `
'\n" \ "1: /bin/pry:23:in `
'\n" \ "2: /bin/pry:23:in `
'\n" \ "3: /bin/pry:23:in `
'\n" \ "4: /bin/pry:23:in `
'\n" \ "\e[1mCaused by:\e[0m RuntimeError: inner\n" \ "--\n" \ "0: /bin/pry:23:in `
'\n" \ "1: /bin/pry:23:in `
'\n" \ "2: /bin/pry:23:in `
'\n" \ "3: /bin/pry:23:in `
'\n" \ "4: /bin/pry:23:in `
'\n" ) end end context "and when the verbose flag present" do before { expect(subject.opts).to receive(:verbose?).twice.and_return(true) } it "prints both original and nested exception backtrace" do subject.process expect(subject.output.string).to eq( "\e[1mException:\e[0m RuntimeError: outer\n" \ "--\n" \ "0: /bin/pry:23:in `
'\n" \ "1: /bin/pry:23:in `
'\n" \ "2: /bin/pry:23:in `
'\n" \ "3: /bin/pry:23:in `
'\n" \ "4: /bin/pry:23:in `
'\n" \ "5: /bin/pry:23:in `
'\n" \ "\e[1mCaused by:\e[0m RuntimeError: inner\n" \ "--\n" \ "0: /bin/pry:23:in `
'\n" \ "1: /bin/pry:23:in `
'\n" \ "2: /bin/pry:23:in `
'\n" \ "3: /bin/pry:23:in `
'\n" \ "4: /bin/pry:23:in `
'\n" \ "5: /bin/pry:23:in `
'\n" ) end end end context "when the code flag is present" do let(:exception) do error = RuntimeError.new('oops') error.set_backtrace( Array.new(6) { "#{__FILE__}:#{__LINE__}:in `
'" } ) error end before do expect(subject.opts).to receive(:code?).at_least(:once).and_return(true) end it "prints lines of code that exception frame references" do subject.process expect(subject.output.string).to eq( "\e[1mException:\e[0m RuntimeError: oops\n" \ "--\n" \ "0: \e[1m#{__FILE__}:168:in `
'\e[0m\n" \ " Array.new(6) { \"\#{__FILE__}:\#{__LINE__}:in `
'\" }\n" \ "1: \e[1m#{__FILE__}:168:in `
'\e[0m\n" \ " Array.new(6) { \"\#{__FILE__}:\#{__LINE__}:in `
'\" }\n" \ "2: \e[1m#{__FILE__}:168:in `
'\e[0m\n" \ " Array.new(6) { \"\#{__FILE__}:\#{__LINE__}:in `
'\" }\n" \ "3: \e[1m#{__FILE__}:168:in `
'\e[0m\n" \ " Array.new(6) { \"\#{__FILE__}:\#{__LINE__}:in `
'\" }\n" \ "4: \e[1m#{__FILE__}:168:in `
'\e[0m\n" \ " Array.new(6) { \"\#{__FILE__}:\#{__LINE__}:in `
'\" }\n" ) end context "and when referenced frame doesn't exist" do before do expect(File).to receive(:open).at_least(:once).and_raise(Errno::ENOENT) end it "skips code and prints only the backtrace frame" do subject.process expect(subject.output.string).to eq( "\e[1mException:\e[0m RuntimeError: oops\n" \ "--\n" \ "0: \e[1m#{__FILE__}:168:in `
'\e[0m\n" \ "1: \e[1m#{__FILE__}:168:in `
'\e[0m\n" \ "2: \e[1m#{__FILE__}:168:in `
'\e[0m\n" \ "3: \e[1m#{__FILE__}:168:in `
'\e[0m\n" \ "4: \e[1m#{__FILE__}:168:in `
'\e[0m\n" ) end end end end end pry-0.13.1/spec/completion_spec.rb000066400000000000000000000134611364454223300170630ustar00rootroot00000000000000# frozen_string_literal: true require "readline" unless defined?(Readline) require "pry/input_completer" def completer_test(bind, pry = nil, assert_flag = true) test = proc do |symbol| input = pry || Readline input_completer = Pry::InputCompleter.new(input, pry) completions = input_completer.call(symbol[0..-2], target: Pry.binding_for(bind)) expect(completions.include?(symbol)).to eq(assert_flag) end proc { |*symbols| symbols.each(&test) } end describe Pry::InputCompleter do before do # The AMQP gem has some classes like this: # pry(main)> AMQP::Protocol::Test::ContentOk.name # => :content_ok module SymbolyName def self.name :symboly_name end end @before_completer = Pry.config.completer Pry.config.completer = Pry::InputCompleter end after do Pry.config.completer = @before_completer Object.remove_const :SymbolyName end it "should not crash if there's a Module that has a symbolic name." do skip unless Pry::Helpers::Platform.jruby? expect do Pry::InputCompleter.new(Readline).call( "a.to_s.", target: Pry.binding_for(Object.new) ) end.not_to raise_error end it 'should take parenthesis and other characters into account for symbols' do expect do Pry::InputCompleter.new(Readline).call( ":class)", target: Pry.binding_for(Object.new) ) end.not_to raise_error end it 'should complete instance variables' do object = Class.new.new # set variables in appropriate scope object.instance_variable_set(:'@name', 'Pry') object.class.send(:class_variable_set, :'@@number', 10) # check to see if variables are in scope expect(object.instance_variables .map(&:to_sym) .include?(:'@name')).to eq true expect(object.class.class_variables .map(&:to_sym) .include?(:'@@number')).to eq true # Complete instance variables. b = Pry.binding_for(object) completer_test(b).call('@name', '@name.downcase') # Complete class variables. b = Pry.binding_for(object.class) completer_test(b).call('@@number', '@@number.class') end it 'should complete for stdlib symbols' do o = Object.new # Regexp completer_test(o).call('/foo/.extend') # Array completer_test(o).call('[1].push') # Hash completer_test(o).call('{"a" => "b"}.keys') # Proc completer_test(o).call('{2}.call') # Symbol completer_test(o).call(':symbol.to_s') # Absolute Constant completer_test(o).call('::IndexError') end it 'should complete for target symbols' do o = Object.new # Constant module Mod remove_const :CON if defined? CON CON = 'Constant'.freeze module Mod2 end end completer_test(Mod).call('CON') # Constants or Class Methods completer_test(o).call('Mod::CON') # Symbol _foo = :symbol completer_test(o).call(':symbol') # Variables class << o attr_accessor :foo end o.foo = 'bar' completer_test(binding).call('o.foo') # trailing slash expect(Pry::InputCompleter.new(Readline).call('Mod2/', target: Pry.binding_for(Mod)) .include?('Mod2/')).to eq(true) end it 'should complete for arbitrary scopes' do module Bar @barvar = :bar end module Baz remove_const :CON if defined? CON @bar = Bar @bazvar = :baz CON = :constant end pry = Pry.new(target: Baz) pry.push_binding(Bar) b = Pry.binding_for(Bar) completer_test(b, pry).call("../@bazvar") completer_test(b, pry).call('/CON') end it 'should complete for stdlib symbols' do o = Object.new # Regexp completer_test(o).call('/foo/.extend') # Array completer_test(o).call('[1].push') # Hash completer_test(o).call('{"a" => "b"}.keys') # Proc completer_test(o).call('{2}.call') # Symbol completer_test(o).call(':symbol.to_s') # Absolute Constant completer_test(o).call('::IndexError') end it 'should complete for target symbols' do o = Object.new # Constant module Mod remove_const :CON if defined? CON CON = 'Constant'.freeze module Mod2 end end completer_test(Mod).call('CON') # Constants or Class Methods completer_test(o).call('Mod::CON') # Symbol _foo = :symbol completer_test(o).call(':symbol') # Variables class << o attr_accessor :foo end o.foo = 'bar' completer_test(binding).call('o.foo') # trailing slash expect(Pry::InputCompleter.new(Readline).call('Mod2/', target: Pry.binding_for(Mod)) .include?('Mod2/')).to eq(true) end it 'should complete for arbitrary scopes' do module Bar @barvar = :bar end module Baz remove_const :CON if defined? CON @bar = Bar @bazvar = :baz CON = :constant end pry = Pry.new(target: Baz) pry.push_binding(Bar) b = Pry.binding_for(Bar) completer_test(b, pry).call("../@bazvar") completer_test(b, pry).call('/CON') end it 'should not return nil in its output' do pry = Pry.new expect(Pry::InputCompleter.new(Readline, pry).call("pry.", target: binding)) .not_to include nil end it 'completes expressions with all available methods' do completer_test(self).call("[].size.chars") end it 'does not offer methods from restricted modules' do require 'irb' completer_test(self, nil, false).call("[].size.parse_printf_format") end unless Pry::Helpers::Platform.jruby? # Classes that override .hash are still hashable in JRuby, for some reason. it 'ignores methods from modules that override Object#hash incompatibly' do require 'irb' m = Module.new do def self.hash; end def aaaa; end end completer_test(m, nil, false).call("[].size.aaaa") end end end pry-0.13.1/spec/config/000077500000000000000000000000001364454223300146135ustar00rootroot00000000000000pry-0.13.1/spec/config/attributable_spec.rb000066400000000000000000000013351364454223300206360ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe Pry::Config::Attributable do subject { klass.new } describe "#attribute" do let(:klass) do Class.new do extend Pry::Config::Attributable attribute :foo end end it "creates a reader attribute for the given name" do expect(klass.instance_method(:foo)).to be_a(UnboundMethod) end it "creates a writer attribute for the given name" do expect(klass.instance_method(:foo=)).to be_a(UnboundMethod) end context "and when the attribute is invoked" do it "sends the 'call' message to the value" do expect_any_instance_of(Pry::Config::Value).to receive(:call) subject.foo end end end end pry-0.13.1/spec/config/lazy_value_spec.rb000066400000000000000000000004001364454223300203170ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe Pry::Config::LazyValue do describe "#call" do subject { described_class.new { rand } } it "doesn't memoize the result of call" do expect(subject.call).not_to eq(subject.call) end end end pry-0.13.1/spec/config/memoized_value_spec.rb000066400000000000000000000010101364454223300211470ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe Pry::Config::MemoizedValue do describe "#call" do it "memoizes the result of call" do instance = described_class.new { rand } expect(instance.call).to eq(instance.call) end it "doesn't conflate falsiness with unmemoizedness" do count = 0 instance = described_class.new do count += 1 nil end expect(instance.call).to eq nil expect(instance.call).to eq nil expect(count).to eq 1 end end end pry-0.13.1/spec/config/value_spec.rb000066400000000000000000000017411364454223300172710ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe Pry::Config::Value do describe "#call" do context "when given value is a MemoizedValue" do subject { described_class.new(Pry::Config::MemoizedValue.new { 123 }) } it "calls the MemoizedLazy object" do expect(subject.call).to eq(123) end end context "when given value is a LazyValue" do subject { described_class.new(Pry::Config::LazyValue.new { 123 }) } it "calls the LazyValue object" do expect(subject.call).to eq(123) end end context "when given value is a Proc" do let(:callable) { proc {} } subject { described_class.new(callable) } it "returns the value as is" do expect(subject.call).to eq(callable) end end context "when given value is a non-callable object" do subject { described_class.new('test') } it "returns the value as is" do expect(subject.call).to eq('test') end end end end pry-0.13.1/spec/config_spec.rb000066400000000000000000000217541364454223300161630ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe Pry::Config do specify { expect(subject.input).to respond_to(:readline) } specify { expect(subject.output).to be_an(IO) } specify { expect(subject.commands).to be_a(Pry::CommandSet) } specify { expect(subject.prompt_name).to be_a(String) } specify { expect(subject.prompt).to be_a(Pry::Prompt) } specify { expect(subject.prompt_safe_contexts).to be_an(Array) } specify { expect(subject.print).to be_a(Method) } specify { expect(subject.quiet).to be(true).or be(false) } specify { expect(subject.exception_handler).to be_a(Method) } specify { expect(subject.unrescued_exceptions).to be_an(Array) } specify { expect(subject.hooks).to be_a(Pry::Hooks) } specify { expect(subject.pager).to be(true).or be(false) } specify { expect(subject.system).to be_a(Method) } specify { expect(subject.color).to be(true).or be(false) } specify { expect(subject.default_window_size).to be_a(Numeric) } specify { expect(subject.editor).to be_a(String) } specify { expect(subject.should_load_rc).to be(true).or be(false) } specify { expect(subject.should_load_local_rc).to be(true).or be(false) } specify { expect(subject.should_trap_interrupts).to be(true).or be(false) } specify { expect(subject.disable_auto_reload).to be(true).or be(false) } specify { expect(subject.command_prefix).to be_a(String) } specify { expect(subject.auto_indent).to be(true).or be(false) } specify { expect(subject.correct_indent).to be(true).or be(false) } specify { expect(subject.collision_warning).to be(true).or be(false) } specify { expect(subject.output_prefix).to be_a(String) } specify { expect(subject.requires).to be_an(Array) } specify { expect(subject.should_load_requires).to be(true).or be(false) } specify { expect(subject.should_load_plugins).to be(true).or be(false) } specify { expect(subject.windows_console_warning).to be(true).or be(false) } specify { expect(subject.control_d_handler).to respond_to(:call) } specify { expect(subject.memory_size).to be_a(Numeric) } specify { expect(subject.extra_sticky_locals).to be_a(Hash) } specify { expect(subject.command_completions).to be_a(Proc) } specify { expect(subject.file_completions).to be_a(Proc) } specify { expect(subject.ls).to be_an(OpenStruct) } specify { expect(subject.completer).to eq(Pry::InputCompleter) } specify { expect(subject.history).to be_a(Pry::History) } specify { expect(subject.history_save).to eq(true).or be(false) } specify { expect(subject.history_load).to eq(true).or be(false) } specify { expect(subject.history_file).to be_a(String) } specify { expect(subject.exec_string).to be_a(String) } specify { expect(subject.rc_file).to be_a(String) } describe "#rc_file" do context "when $PRYRC env variable is set" do before do allow(Pry::Env).to receive(:[]) allow(Pry::Env).to receive(:[]).with('PRYRC').and_return('/foo/pryrc') end it "defaults to the value of PRYRC env variable" do expect(subject.rc_file).to eq('/foo/pryrc') end end context "when ~/.pryrc exists and $XDG_CONFIG_HOME is undefined" do before do allow(File).to receive(:exist?) expect(File).to receive(:exist?) .with(File.expand_path('~/.pryrc')).and_return(true) allow(Pry::Env).to receive(:[]) allow(Pry::Env).to receive(:[]) .with('XDG_CONFIG_HOME').and_return(nil) end it "defaults to ~/.pryrc" do expect(subject.rc_file).to eq('~/.pryrc') end end context "when $XDG_CONFIG_HOME is defined" do before do allow(Pry::Env).to receive(:[]) allow(Pry::Env).to receive(:[]) .with('XDG_CONFIG_HOME').and_return('/xdg_home') allow(File).to receive(:exist?) end context "and when ~/.pryrc exists" do before do allow(File).to receive(:exist?) .with(File.expand_path('~/.pryrc')).and_return(true) end it "defaults to $XDG_CONFIG_HOME/pry/pryrc" do expect(subject.rc_file).to eq('/xdg_home/pry/pryrc') end end context "and when ~/.pryrc doesn't exist" do before do allow(File).to receive(:exist?) .with(File.expand_path('~/.pryrc')).and_return(false) end it "defaults to $XDG_CONFIG_HOME/pry/pryrc" do expect(subject.rc_file).to eq('/xdg_home/pry/pryrc') end end end end describe "#merge!" do it "merges given hash with the config instance" do subject.merge!(output_prefix: '~> ', exec_string: '!') expect(subject.output_prefix).to eq('~> ') expect(subject.exec_string).to eq('!') end it "returns self" do config = subject.merge!(output_prefix: '~> ') expect(subject).to eql(config) end context "when an undefined option is given" do it "adds the option to the config" do subject.merge!(new_option: 1, other_option: 2) expect(subject.new_option).to eq(1) expect(subject.other_option).to eq(2) end end end describe "#merge" do it "returns a new config object" do expect(subject).not_to equal(subject.merge(new_option: 1, other_option: 2)) end it "doesn't mutate the original config" do subject.merge(new_option: 1, other_option: 2) expect(subject).not_to respond_to(:new_option) expect(subject).not_to respond_to(:other_option) end end describe "#method_missing" do context "when invoked method ends with =" do it "assigns a new custom option" do subject.foo = 1 expect(subject.foo).to eq(1) end end context "when invoked method is not an option" do it "returns nil" do expect(subject.foo).to be_nil end end context "when invoked method is a LazyValue" do it "defines a callable attribute" do subject.foo = Pry::Config::LazyValue.new { 1 } expect(subject.foo).to eq(1) end end end describe "#respond_to?" do context "when checking an undefined option" do it "returns false" do expect(subject.respond_to?(:foo)).to be(false) end end context "when checking a defined option" do before { subject.foo = 1 } it "returns true for the reader" do expect(subject.respond_to?(:foo)).to be(true) end it "returns true for the writer" do expect(subject.respond_to?(:foo=)).to be(true) end end end describe "#[]" do it "reads the config value" do expect_any_instance_of(Pry::Config::Value).to receive(:call) subject[:foo] = 1 subject[:foo] end it "returns the config value" do subject[:foo] = 1 expect(subject[:foo]).to eq(1) end end describe "#control_d_handler=" do context "when the handler expects multiple arguments" do it "prints a warning" do expect(Pry::Warning).to receive(:warn).with( "control_d_handler's arity of 2 parameters was deprecated " \ '(eval_string, pry_instance). Now it gets passed just 1 ' \ 'parameter (pry_instance)' ) subject.control_d_handler = proc { |_arg1, _arg2| } end end context "when the handler expects just one argument" do it "doesn't print a warning" do expect(Pry::Warning).not_to receive(:warn) subject.control_d_handler = proc { |_arg1| } end end end describe "#control_d_handler" do let(:pry_instance) { Pry.new } context "when it returns a callable accepting one argument" do context "and when it is called with one argument" do it "calls the handler with a pry instance" do subject.control_d_handler = proc do |arg| expect(arg).to eql(pry_instance) end subject.control_d_handler.call(pry_instance) end end context "and when it is called with multiple arguments" do before { allow(Pry::Warning).to receive(:warn) } it "calls the handler with a pry instance" do subject.control_d_handler = proc do |arg| expect(arg).to eql(pry_instance) end subject.control_d_handler.call('', pry_instance) end end end context "when it returns a callabale with two arguments" do before { allow(Pry::Warning).to receive(:warn) } context "and when it's called with one argument" do it "calls the handler with a eval_string and a pry instance" do subject.control_d_handler = proc do |arg1, arg2| expect(arg1).to eq('') expect(arg2).to eql(pry_instance) end subject.control_d_handler.call(pry_instance) end end context "and when it's called with multiple arguments" do it "calls the handler with a eval_string and a pry instance" do subject.control_d_handler = proc do |arg1, arg2| expect(arg1).to eq('') expect(arg2).to eql(pry_instance) end subject.control_d_handler.call('', pry_instance) end end end end end pry-0.13.1/spec/control_d_handler_spec.rb000066400000000000000000000031171364454223300203670ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe Pry::ControlDHandler do context "when given eval string is non-empty" do let(:pry_instance) do Pry.new.tap do |p| p.eval_string = 'hello' end end it "clears input buffer" do described_class.default(pry_instance) expect(pry_instance.eval_string).to be_empty end end context "when given eval string is empty & pry instance has one binding" do let(:pry_instance) do Pry.new.tap do |p| p.eval_string = '' p.binding_stack = [binding] end end it "throws :breakout" do expect { described_class.default(pry_instance) } .to throw_symbol(:breakout) end it "clears binding stack" do expect { described_class.default(pry_instance) } .to throw_symbol expect(pry_instance.binding_stack).to be_empty end end context "when given eval string is empty & pry instance has 2+ bindings" do let(:binding1) { binding } let(:binding2) { binding } let(:binding_stack) { [binding1, binding2] } let(:pry_instance) do Pry.new.tap do |p| p.eval_string = '' p.binding_stack = binding_stack end end it "saves a dup of the current binding stack in the 'cd' command" do described_class.default(pry_instance) cd_state = pry_instance.commands['cd'].state expect(cd_state.old_stack).to eq([binding1, binding2]) end it "pops the binding off the stack" do described_class.default(pry_instance) expect(pry_instance.binding_stack).to eq([binding1]) end end end pry-0.13.1/spec/documentation_helper_spec.rb000066400000000000000000000044501364454223300211200ustar00rootroot00000000000000# frozen_string_literal: true describe Pry::Helpers::DocumentationHelpers do before do @helper = Pry::Helpers::DocumentationHelpers end describe "get_comment_content" do it "should strip off the hash and unindent" do expect(@helper.get_comment_content(" # hello\n # world\n")).to eq("hello\nworld\n") end it "should strip out leading lines of hashes" do expect(@helper.get_comment_content("###############\n#hello\n#world\n")) .to eq("hello\nworld\n") end it "should remove shebangs" do expect(@helper.get_comment_content("#!/usr/bin/env ruby\n# This is a program\n")) .to eq("This is a program\n") end it "should unindent past separators" do str = " # Copyright Me \n #--\n # So there.\n" expect(@helper.get_comment_content(str)) .to eq("Copyright Me \n--\nSo there.\n") end end describe "process_rdoc" do before do Pry.config.color = true end after do Pry.config.color = false end it "should syntax highlight indented code" do expect(@helper.process_rdoc(" 4 + 4\n")).not_to eq(" 4 + 4\n") end it "should highlight words surrounded by +s" do expect(@helper.process_rdoc("the +parameter+")).to match(/the \e.*parameter\e.*/) end it "should syntax highlight things in backticks" do expect(@helper.process_rdoc("for `Example`")).to match(/for `\e.*Example\e.*`/) end it "should emphasise em tags" do expect(@helper.process_rdoc("for science")).to eq("for \e[1mscience\e[0m") end it "should emphasise italic tags" do expect(@helper.process_rdoc("for science")).to eq("for \e[1mscience\e[0m") end it "should syntax highlight code in " do expect(@helper.process_rdoc("for Example")) .to match(/for \e.*Example\e.*/) end it "should syntax highlight code in " do expect(@helper.process_rdoc("for Example")).to match(/for \e.*Example\e.*/) end it "should not double-highlight backticks inside indented code" do expect(@helper.process_rdoc(" `echo 5`")).to match(/echo 5/) end it "should not remove ++" do expect(@helper.process_rdoc("--\n comment in a bubble\n++")).to match(/\+\+/) end end end pry-0.13.1/spec/editor_spec.rb000066400000000000000000000055351364454223300162030ustar00rootroot00000000000000# frozen_string_literal: true require 'pathname' describe Pry::Editor do before do # OS-specific tempdir name. For GNU/Linux it's "tmp", for Windows it's # something "Temp". @tf_dir = if Pry::Helpers::Platform.mri_19? Pathname.new(Dir::Tmpname.tmpdir) else Pathname.new(Dir.tmpdir) end @tf_path = File.join(@tf_dir.to_s, 'hello world.rb') @editor = Pry::Editor.new(Pry.new) end describe ".default" do context "when $VISUAL is defined" do before do allow(Pry::Env).to receive(:[]) expect(Pry::Env).to receive(:[]).with('VISUAL').and_return('emacs') end it "returns the value of $VISUAL" do expect(described_class.default).to eq('emacs') end end context "when $EDITOR is defined" do before do allow(Pry::Env).to receive(:[]) expect(Pry::Env).to receive(:[]).with('EDITOR').and_return('vim') end it "returns the value of $EDITOR" do expect(described_class.default).to eq('vim') end end context "when platform is Windows" do before do allow(Pry::Env).to receive(:[]) allow(Pry::Env).to receive(:[]).with('VISUAL').and_return(nil) allow(Pry::Env).to receive(:[]).with('EDITOR').and_return(nil) allow(Pry::Helpers::Platform).to receive(:windows?).and_return(true) end it "returns 'notepad'" do expect(described_class.default).to eq('notepad') end end context "when no editor is detected" do before do allow(ENV).to receive(:key?).and_return(false) allow(Kernel).to receive(:system) end %w[editor nano vi].each do |text_editor_name| it "shells out to find '#{text_editor_name}'" do expect(Kernel).to receive(:system) .with("which #{text_editor_name} > /dev/null 2>&1") described_class.default end end end end describe "build_editor_invocation_string", skip: !Pry::Helpers::Platform.windows? do it 'should shell-escape files' do invocation_str = @editor.build_editor_invocation_string(@tf_path, 5, true) expect(invocation_str).to match(/#{@tf_dir}.+hello\\ world\.rb/) end end describe "build_editor_invocation_string on windows" do before do allow(Pry::Helpers::Platform).to receive(:windows?).and_return(true) end it "should not shell-escape files" do invocation_str = @editor.build_editor_invocation_string(@tf_path, 5, true) expect(invocation_str).to match(/hello world\.rb/) end end describe 'invoke_editor with a proc' do it 'should not shell-escape files' do editor = Pry::Editor.new(Pry.new(editor: proc { |file, _line, _blocking| @file = file nil })) editor.invoke_editor(@tf_path, 10, true) expect(@file).to eq(@tf_path) end end end pry-0.13.1/spec/env_spec.rb000066400000000000000000000011641364454223300154770ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe Pry::Env do describe "#[]" do let(:key) { 'PRYTESTKEY' } after { ENV.delete(key) } context "when ENV contains the passed key" do before { ENV[key] = 'val' } after { ENV.delete(key) } specify { expect(described_class[key]).to eq('val') } end context "when ENV doesn't contain the passed key" do specify { expect(described_class[key]).to be_nil } end context "when ENV contains the passed key but its value is nil" do before { ENV[key] = '' } specify { expect(described_class[key]).to be_nil } end end end pry-0.13.1/spec/exception_handler_spec.rb000066400000000000000000000035411364454223300204030ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe Pry::ExceptionHandler do describe ".handle_exception" do let(:output) { StringIO.new } let(:pry_instance) { Pry.new } context "when exception is a UserError and a SyntaxError" do let(:exception) do SyntaxError.new('cool syntax error, dude').extend(Pry::UserError) end it "prints the syntax error with customized message" do described_class.handle_exception(output, exception, pry_instance) expect(output.string).to start_with("SyntaxError: dude\n") end end context "when exception is a standard error" do let(:exception) do error = StandardError.new('oops') error.set_backtrace(["/bin/pry:23:in `
'"]) error end it "prints standard error message" do described_class.handle_exception(output, exception, pry_instance) expect(output.string) .to eq("StandardError: oops\nfrom /bin/pry:23:in `
'\n") end end context "when exception is a nested standard error" do let(:exception) do error = nil begin begin raise 'nested oops' rescue # rubocop:disable Style/RescueStandardError raise 'outer oops' end rescue StandardError => outer_error error = outer_error end error end before do if RUBY_VERSION.start_with?('1.9', '2.0') skip("Ruby #{RUBY_VERSION} doesn't support nested exceptions") end end it "prints standard error message" do described_class.handle_exception(output, exception, pry_instance) expect(output.string).to match( /RuntimeError:\souter\soops\n from\s.+\n Caused\sby\sRuntimeError:\snested\soops\n from.+/x ) end end end end pry-0.13.1/spec/fixtures/000077500000000000000000000000001364454223300152175ustar00rootroot00000000000000pry-0.13.1/spec/fixtures/Gemfile000066400000000000000000000000751364454223300165140ustar00rootroot00000000000000# frozen_string_literal: true source 'https://rubygems.org' pry-0.13.1/spec/fixtures/candidate_helper1.rb000066400000000000000000000001651364454223300211020ustar00rootroot00000000000000# frozen_string_literal: true # rank 0 class CandidateTest def test1; end def test2; end def test3; end end pry-0.13.1/spec/fixtures/candidate_helper2.rb000066400000000000000000000001431364454223300210770ustar00rootroot00000000000000# frozen_string_literal: true # rank 1 class CandidateTest def test4; end def test5; end end pry-0.13.1/spec/fixtures/cat_load_path000066400000000000000000000000001364454223300177120ustar00rootroot00000000000000pry-0.13.1/spec/fixtures/cat_load_path.rb000066400000000000000000000000001364454223300203140ustar00rootroot00000000000000pry-0.13.1/spec/fixtures/cdpathdir/000077500000000000000000000000001364454223300171615ustar00rootroot00000000000000pry-0.13.1/spec/fixtures/cdpathdir/.gitkeep000066400000000000000000000000001364454223300206000ustar00rootroot00000000000000pry-0.13.1/spec/fixtures/example.erb000066400000000000000000000000571364454223300173460ustar00rootroot00000000000000 Look at me, testing my erb! pry-0.13.1/spec/fixtures/example_nesting.rb000066400000000000000000000031501364454223300207250ustar00rootroot00000000000000 # [] class A # ["class A"] def a; end # ["class A"] class B; def b; end; end # ["class A", "class B"] end # [] # [] class << A # ["class << A"] class B # ["class << A", "class B"] def c; end # ["class << A", "class B"] end # ["class << A"] # ["class << A"] module F::B # ["class << A", "module F::B"] def foo; end # ["class << A", "module F::B"] end # ["class << A"] end # [] # [] module :symbol.class::Exciting # def foo; end # class B # def goo; end # end # end # [] # [] module C # ["module C"] class D # ["module C", "class D"] def guh; foo.end; end # ["module C", "class D"] end # ["module C"] def bar; :end; end # ["module C"] class << new.bar; end # ["module C"] class << new.bar; def f; end; end # # ["module C"] class << self; def mug; end; end # ["module C", "class << self"] end # [] pry-0.13.1/spec/fixtures/pry_history000066400000000000000000000000301364454223300175260ustar00rootroot00000000000000:athos :porthos :aramis pry-0.13.1/spec/fixtures/slinky.rb000066400000000000000000000000001364454223300170430ustar00rootroot00000000000000pry-0.13.1/spec/fixtures/slinky/000077500000000000000000000000001364454223300165305ustar00rootroot00000000000000pry-0.13.1/spec/fixtures/slinky/stinky.rb000066400000000000000000000000001364454223300203640ustar00rootroot00000000000000pry-0.13.1/spec/fixtures/testlinkrc000077700000000000000000000000001364454223300205462testrcustar00rootroot00000000000000pry-0.13.1/spec/fixtures/testrc000066400000000000000000000000761364454223300164510ustar00rootroot00000000000000TEST_RC = [] if !Object.const_defined?(:TEST_RC) TEST_RC << 0 pry-0.13.1/spec/fixtures/testrcbad000066400000000000000000000000551364454223300171150ustar00rootroot00000000000000TEST_BEFORE_RAISE = 1 raise "messin with ya" pry-0.13.1/spec/fixtures/whereami_helper.rb000066400000000000000000000002741364454223300207070ustar00rootroot00000000000000# frozen_string_literal: true # rubocop:disable Layout/EmptyLineBetweenDefs class Cor def a; end def b; end def c; end def d; end end # rubocop:enable Layout/EmptyLineBetweenDefs pry-0.13.1/spec/helpers/000077500000000000000000000000001364454223300150105ustar00rootroot00000000000000pry-0.13.1/spec/helpers/command_helpers_spec.rb000066400000000000000000000156261364454223300215210ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe Pry::Helpers::CommandHelpers do describe "#temp_file" do it "yields a tempfile" do expect { |block| subject.temp_file(&block) } .to yield_with_args(an_instance_of(Tempfile)) end it "creates a tempfile with rb extension" do subject.temp_file do |file| expect(file.path).to end_with('.rb') end end it "allows overwriting file extension" do subject.temp_file('.clj') do |file| expect(file.path).to end_with('.clj') end end it "closes the tempfile" do tempfile = nil subject.temp_file('.clj') do |file| tempfile = file end expect(tempfile).to be_closed end it "unlinks the tempfile" do tempfile = nil subject.temp_file('.clj') do |file| tempfile = file end expect(tempfile.path).to be_nil end end describe "#internal_binding?" do context "when target's __method__ returns __binding__" do it "returns true" do expect(subject.internal_binding?(Object.__binding__)).to be_truthy end end context "when target's __method__ returns __pry__" do it "returns true" do expect(subject.internal_binding?(Object.new.__binding__)).to be_truthy end end context "when target's __method__ returns nil" do it "returns true" do expect(subject.internal_binding?(TOPLEVEL_BINDING)).to be_falsey end end end describe "#get_method_or_raise" do subject do Class.new do class << self include Pry::Helpers::CommandHelpers def command_name 'test-command' end def pry_instance Pry.new end def target binding end end end end context "when there's name but no corresponding method" do it "raises MethodNotFound" do expect { subject.get_method_or_raise('foobar', binding) } .to raise_error(Pry::MethodNotFound, /method.+could not be found/) end end context "when super opt is provided but there's no super method" do let(:test_binding) do def test_method; end binding end it "raises MethodNotFound" do expect { subject.get_method_or_raise('test_method', test_binding, super: 1) } .to raise_error(Pry::MethodNotFound, /has no super method/) end end context "when super opt is provided and there's a parent method" do let(:test_binding) do ParentClass = Class.new do def test_method :parent end end ChildClass = Class.new(ParentClass) do def test_method :child end end binding end it "gets the parent method" do method = subject.get_method_or_raise( 'ChildClass#test_method', test_binding, super: 1 ) expect(method.owner).to eq(ParentClass) end end context "when there's no method name" do it "raises MethodNotFound" do expect { subject.get_method_or_raise(nil, binding) } .to raise_error(Pry::MethodNotFound, /no method name given/) end end end describe "#unindent" do it "removes the same prefix from all lines" do expect(subject.unindent(" one\n two\n")).to eq("one\ntwo\n") end it "should not be phased by empty lines" do expect(subject.unindent(" one\n\n two\n")).to eq("one\n\ntwo\n") end it "should only remove a common prefix" do expect(subject.unindent(" one\n two\n")).to eq(" one\ntwo\n") end it "should also remove tabs if present" do expect(subject.unindent("\tone\n\ttwo\n")).to eq("one\ntwo\n") end it "should ignore lines starting with --" do expect(subject.unindent(" one\n--\n two\n")).to eq("one\n--\ntwo\n") end end describe "#restrict_to_lines" do context "when lines are specified as an integer" do it "restricts the given string to the specified line number" do expect(subject.restrict_to_lines("one\ntwo\n\three\nfour\n", 2)) .to eq("two\n") end end context "when lines are specified as a range" do it "restricts the given string to the specified range" do expect(subject.restrict_to_lines("one\ntwo\n\three\nfour\n", 2...3)) .to eq("two\n\three\n") end end end describe "#one_index_number" do context "when line number is more than 0" do it "decrements the line number" do expect(subject.one_index_number(2)).to eq(1) end end context "when line number is 0" do it "returns the line number" do expect(subject.one_index_number(0)).to eq(0) end end end describe "#one_index_range" do it "decrements range boundaries" do expect(subject.one_index_range(3..30)).to eq(2..29) end end describe "#one_index_range_or_number" do context "when given an integer" do it "decrements the line number" do expect(subject.one_index_range_or_number(2)).to eq(1) end end context "when given a range" do it "decrements range boundaries" do expect(subject.one_index_range_or_number(3..30)).to eq(2..29) end end end describe "#absolute_index_number" do context "when line number is zero" do it "returns the line number" do expect(subject.absolute_index_number(0, 1)).to eq(0) end end context "when line number is less than zero" do it "returns the absolute sum of line number and array length" do expect(subject.absolute_index_number(-2, 5)).to eq(3) end end end describe "#absolute_index_range" do context "when given an integer" do it "returns a range based on the integer and array length" do expect(subject.absolute_index_range(1, 2)).to eq(1..1) end end context "when given an integer" do it "returns an absolute range that was decremented" do expect(subject.absolute_index_range(-3..-20, 22)).to eq(19..2) end end end describe "#set_file_and_dir_locals" do let(:pry_instance) { Pry.new } let(:context) { binding } it "injects local variable _file_" do subject.set_file_and_dir_locals('foo/test.rb', pry_instance, context) expect(context.eval('_file_')).to end_with('test.rb') end it "injects local variable _dir_" do subject.set_file_and_dir_locals('foo/test.rb', pry_instance, context) expect(context.eval('_dir_')).to end_with('foo') end it "sets pry instance's last_file to _file_" do subject.set_file_and_dir_locals('foo/test.rb', pry_instance, context) expect(pry_instance.last_file).to end_with('test.rb') end it "sets pry instance's last_dir to _dir_" do subject.set_file_and_dir_locals('foo/test.rb', pry_instance, context) expect(pry_instance.last_dir).to end_with('foo') end end end pry-0.13.1/spec/helpers/table_spec.rb000066400000000000000000000056501364454223300174440ustar00rootroot00000000000000# frozen_string_literal: true describe 'Formatting Table' do it 'knows about colorized fitting' do t = Pry::Helpers::Table.new %w[hihi], column_count: 1 expect(t.fits_on_line?(4)).to eq true t.items = [] expect(t.fits_on_line?(4)).to eq true t.items = %w[hi hi] expect(t.fits_on_line?(4)).to eq true t.column_count = 2 expect(t.fits_on_line?(4)).to eq false t.items = %w[a ccc bb dddd].sort expect(t.fits_on_line?(8)).to eq true expect(t.fits_on_line?(7)).to eq false end describe 'formatting - should order downward and wrap to columns' do FAKE_COLUMNS = 62 def try_round_trip(expected) things = expected.split(/\s+/).sort actual = Pry::Helpers.tablify(things, FAKE_COLUMNS).to_s.strip expected = expected.gsub(/\s+$/, '') actual = actual.gsub(/\s+$/, '') if actual != expected bar = '-' * 25 puts \ bar + 'expected' + bar, expected, bar + 'actual' + bar, actual end expect(actual).to eq expected end it 'should handle a tiny case' do try_round_trip(<<-TABLE) asdf asfddd fdass TABLE end it 'should handle the basic case' do try_round_trip(<<-TABLE) aadd ddasffssdad sdsaadaasd ssfasaafssd adassdfffaasds f sdsfasddasfds ssssdaa assfsafsfsds fsasa ssdsssafsdasdf TABLE end it 'should handle... another basic case' do try_round_trip(<<-TABLE) aaad dasaasffaasf fdasfdfss safdfdddsasd aaadfasassdfff ddadadassasdf fddsasadfssdss sasf aaddaafaf dddasaaaaaa fdsasad sddsa aas dfsddffdddsdfd ff sddsfsaa adasadfaaffds dsfafdsfdfssda ffadsfafsaafa ss asddaadaaadfdd dssdss ffssfsfafaadss ssas asdsdaa faadf fsddfff ssdfssff asfadsssaaad fasfaafdssd s TABLE end it 'should handle colors' do try_round_trip(<<-TABLE) \e[31maaaaaaaaaa\e[0m \e[31mccccccccccccccccccccccccccccc\e[0m \e[31mbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\e[0m \e[31mddddddddddddd\e[0m TABLE end it 'should handle empty input' do try_round_trip('') end it 'should handle one-token input' do try_round_trip('asdf') end end describe 'line length is smaller than the length of the longest word' do before do element = 'swizzle' @elem_len = element.length @out = [element, 'crime', 'fun'] end it 'should not raise error' do expect { Pry::Helpers.tablify(@out, @elem_len - 1) }.not_to raise_error end it 'should format output as one column' do table = Pry::Helpers.tablify(@out, @elem_len - 1).to_s expect(table).to eq "swizzle\ncrime \nfun " end end specify 'decide between one-line or indented output' do expect(Pry::Helpers.tablify_or_one_line('head', %w[ing])).to eq "head: ing\n" end end pry-0.13.1/spec/history_spec.rb000066400000000000000000000146701364454223300164160ustar00rootroot00000000000000# frozen_string_literal: true require 'tempfile' require 'rbconfig' RSpec.describe Pry::History do before do Pry.history.clear @saved_history = "1\n2\n3\ninvalid\0 line\n" Pry.history.loader = proc do |&blk| @saved_history.lines.each { |l| blk.call(l) } end Pry.load_history end after do Pry.history.clear Pry.history.instance_variable_set(:@original_lines, 0) end describe ".default_file" do let(:xdg_name) { 'XDG_DATA_HOME' } let(:default_path) { File.expand_path '~/.pry_history' } def stub_hist(options) has_default = options.fetch :has_default xdg_home = options.fetch :xdg_home allow(File).to receive(:exist?) # there's a test helper hook that hits this allow(File).to receive(:exist?).with(default_path).and_return(has_default) allow(ENV).to receive(:[]) allow(ENV).to receive(:key?) allow(ENV).to receive(:[]).with(xdg_name).and_return(xdg_home) allow(ENV).to receive(:key?).with(xdg_name).and_return(!!xdg_home) end it "returns ~/.local/share/pry/pry_history" do stub_hist has_default: false, xdg_home: nil expect(described_class.default_file).to match('/.local/share/pry/pry_history') end context "when ~/.pry_history exists" do it "returns ~/.pry_history" do stub_hist has_default: true, xdg_home: nil expect(described_class.default_file).to eq default_path end end context "when $XDG_DATA_HOME is defined" do it "returns config location relative to $XDG_DATA_HOME" do stub_hist has_default: false, xdg_home: '/my/path' expect(described_class.default_file).to eq('/my/path/pry/pry_history') end it "returns config location relative to $XDG_DATA_HOME when ~/.pryrc exists" do stub_hist has_default: true, xdg_home: '/my/path' expect(described_class.default_file).to eq('/my/path/pry/pry_history') end end end describe '#push' do it "does not record duplicated lines" do Pry.history << '3' Pry.history << '_ += 1' Pry.history << '_ += 1' expect(Pry.history.to_a.grep('_ += 1').count).to eq 1 end it "does not record lines that contain a NULL byte" do c = Pry.history.to_a.size Pry.history << "a\0b" expect(Pry.history.to_a.size).to eq c end it "does not record empty lines" do c = Pry.history.to_a.size Pry.history << '' expect(Pry.history.to_a.size).to eq c end end describe "#clear" do before do @old_file = Pry.config.history_file @hist_file_path = File.expand_path('spec/fixtures/pry_history') Pry.config.history_file = @hist_file_path Pry.history.clear Pry.load_history end after do Pry.config.history_file = @old_file end it "clears this session's history" do expect(Pry.history.to_a.size).to be > 0 Pry.history.clear expect(Pry.history.to_a.size).to eq 0 expect(Pry.history.original_lines).to eq 0 end it "doesn't affect the contents of the history file" do expect(Pry.history.to_a.size).to eq 3 Pry.history.clear File.open(@hist_file_path, 'r') do |fh| file = fh.to_a expect(file.length).to eq 3 expect(file.any? { |a| a =~ /athos/ }).to eq true end end end describe "#history_line_count" do it "counts entries in history" do Pry.history.clear saved_history = "olgierd\ngustlik\njanek\ngrzes\ntomek\n" Pry.history.loader = proc do |&blk| saved_history.lines.each { |l| blk.call(l) } end Pry.load_history expect(Pry.history.history_line_count).to eq 5 end end describe "#session_line_count" do it "returns the number of lines in history from just this session" do Pry.history << 'you?' Pry.history << 'you are so precious' expect(Pry.history.session_line_count).to eq 2 end end describe ".load_history" do it "reads the contents of the file" do expect(Pry.history.to_a[-2..-1]).to eq %w[2 3] end end describe "saving to a file" do before do @histfile = Tempfile.new(%w[pryhistory txt]) @history = Pry::History.new(file_path: @histfile.path) Pry.config.history_save = true end after do @histfile.close(true) Pry.config.history_save = false end it "saves lines to a file as they are written" do @history.push "5" expect(File.read(@histfile.path)).to eq "5\n" end it "interleaves lines from many places" do @history.push "5" File.open(@histfile.path, 'a') { |f| f.puts "6" } @history.push "7" expect(File.read(@histfile.path)).to eq "5\n6\n7\n" end it "should not write histignore words to the history file" do Pry.config.history_ignorelist = ["ls", /hist*/, 'exit'] @history.push "ls" @history.push "hist" @history.push "kakaroto" @history.push "exit" expect(File.open(@histfile.path).entries.size).to eq 1 expect(IO.readlines(@histfile.path).first).to eq "kakaroto\n" end end describe "expanding the history file path" do before { Pry.config.history_save = true } after { Pry.config.history_save = false } it "recognizes ~ (#1262)" do # This is a pretty dumb way of testing this, but at least it shouldn't # succeed spuriously. history = Pry::History.new(file_path: '~/test_history') error = Class.new(RuntimeError) expect(File).to receive(:open) .with(File.join(ENV['HOME'].to_s, "/test_history"), 'a', 0o600) .and_raise(error) expect { history.push 'a line' }.to raise_error error end end describe "file io errors" do let(:history) { Pry::History.new(file_path: file_path) } let(:file_path) { Tempfile.new("pry_history_spec").path } [Errno::EACCES, Errno::ENOENT].each do |error_class| it "handles #{error_class} failure to read from history" do expect(File).to receive(:foreach).and_raise(error_class) expect(history).to receive(:warn).with(/Unable to read history file:/) expect { history.load }.to_not raise_error end it "handles #{error_class} failure to write history" do Pry.config.history_save = true expect(File).to receive(:open).with(file_path, "a", 0o600).and_raise(error_class) expect(history).to receive(:warn).with(/Unable to write history file:/) expect { history.push("anything") }.to_not raise_error end end end end pry-0.13.1/spec/hooks_spec.rb000066400000000000000000000415161364454223300160370ustar00rootroot00000000000000# frozen_string_literal: true describe Pry::Hooks do before do @hooks = Pry::Hooks.new end describe ".default" do it "returns hooks with default before_session hook" do hooks = described_class.default expect(hooks.hook_exists?('before_session', :default)).to be_truthy end context "when pry instance is quiet" do let(:pry_instance) { Pry.new(quiet: true) } it "doesn't run the whereami command" do expect(pry_instance).not_to receive(:run_command) hooks = described_class.default hooks.exec_hook(:before_session, StringIO.new, {}, pry_instance) end end context "when pry instance is not quiet" do let(:pry_instance) { Pry.new(quiet: false) } let(:output) { StringIO.new } it "runs the whereami command" do expect(pry_instance).to receive(:run_command).with('whereami --quiet') hooks = described_class.default hooks.exec_hook(:before_session, StringIO.new, {}, pry_instance) end end end describe "adding a new hook" do it 'should not execute hook while adding it' do run = false @hooks.add_hook(:test_hook, :my_name) { run = true } expect(run).to eq false end it 'should not allow adding of a hook with a duplicate name' do @hooks.add_hook(:test_hook, :my_name) {} expect { @hooks.add_hook(:test_hook, :my_name) {} }.to raise_error ArgumentError end it 'should create a new hook with a block' do @hooks.add_hook(:test_hook, :my_name) {} expect(@hooks.hook_count(:test_hook)).to eq 1 end it 'should create a new hook with a callable' do @hooks.add_hook(:test_hook, :my_name, proc {}) expect(@hooks.hook_count(:test_hook)).to eq 1 end it 'should use block if given both block and callable' do run = false foo = false @hooks.add_hook(:test_hook, :my_name, proc { foo = true }) { run = true } expect(@hooks.hook_count(:test_hook)).to eq 1 @hooks.exec_hook(:test_hook) expect(run).to eq true expect(foo).to eq false end it 'should raise if not given a block or any other object' do expect { @hooks.add_hook(:test_hook, :my_name) }.to raise_error ArgumentError end it 'should create multiple hooks for an event' do @hooks.add_hook(:test_hook, :my_name) {} @hooks.add_hook(:test_hook, :my_name2) {} expect(@hooks.hook_count(:test_hook)).to eq 2 end it 'should return a count of 0 for an empty hook' do expect(@hooks.hook_count(:test_hook)).to eq 0 end end describe "Pry::Hooks#merge" do describe "merge!" do it 'should merge in the Pry::Hooks' do h1 = Pry::Hooks.new.add_hook(:test_hook, :testing) {} h2 = Pry::Hooks.new h2.merge!(h1) expect(h2.get_hook(:test_hook, :testing)).to eq h1.get_hook(:test_hook, :testing) end it 'should not share merged elements with original' do h1 = Pry::Hooks.new.add_hook(:test_hook, :testing) {} h2 = Pry::Hooks.new h2.merge!(h1) h2.add_hook(:test_hook, :testing2) {} expect(h2.get_hook(:test_hook, :testing2)).not_to eq( h1.get_hook(:test_hook, :testing2) ) end it 'should NOT overwrite hooks belonging to shared event in receiver' do h1 = Pry::Hooks.new.add_hook(:test_hook, :testing) {} callable = proc {} h2 = Pry::Hooks.new.add_hook(:test_hook, :testing2, callable) h2.merge!(h1) expect(h2.get_hook(:test_hook, :testing2)).to eq callable end it 'should overwrite identical hook in receiver' do callable1 = proc { :one } h1 = Pry::Hooks.new.add_hook(:test_hook, :testing, callable1) callable2 = proc { :two } h2 = Pry::Hooks.new.add_hook(:test_hook, :testing, callable2) h2.merge!(h1) expect(h2.get_hook(:test_hook, :testing)).to eq callable1 expect(h2.hook_count(:test_hook)).to eq 1 end it 'should preserve hook order' do name = '' h1 = Pry::Hooks.new h1.add_hook(:test_hook, :testing3) { name += "h" } h1.add_hook(:test_hook, :testing4) { name += "n" } h2 = Pry::Hooks.new h2.add_hook(:test_hook, :testing1) { name += "j" } h2.add_hook(:test_hook, :testing2) { name += "o" } h2.merge!(h1) h2.exec_hook(:test_hook) expect(name).to eq "john" end describe "merge" do it 'should return a fresh, independent instance' do h1 = Pry::Hooks.new.add_hook(:test_hook, :testing) {} h2 = Pry::Hooks.new h3 = h2.merge(h1) expect(h3).not_to eq h1 expect(h3).not_to eq h2 end it 'should contain hooks from original instance' do h1 = Pry::Hooks.new.add_hook(:test_hook, :testing) {} h2 = Pry::Hooks.new.add_hook(:test_hook2, :testing) {} h3 = h2.merge(h1) expect(h3.get_hook(:test_hook, :testing)).to eq( h1.get_hook(:test_hook, :testing) ) expect(h3.get_hook(:test_hook2, :testing)).to eq( h2.get_hook(:test_hook2, :testing) ) end it 'should not affect original instances when new hooks are added' do h1 = Pry::Hooks.new.add_hook(:test_hook, :testing) {} h2 = Pry::Hooks.new.add_hook(:test_hook2, :testing) {} h3 = h2.merge(h1) h3.add_hook(:test_hook3, :testing) {} expect(h1.get_hook(:test_hook3, :testing)).to eq nil expect(h2.get_hook(:test_hook3, :testing)).to eq nil end end end end describe "dupping a Pry::Hooks instance" do it 'should share hooks with original' do @hooks.add_hook(:test_hook, :testing) do :none_such end hooks_dup = @hooks.dup expect(hooks_dup.get_hook(:test_hook, :testing)).to eq( @hooks.get_hook(:test_hook, :testing) ) end it 'adding a new event to dupped instance should not affect original' do @hooks.add_hook(:test_hook, :testing) { :none_such } hooks_dup = @hooks.dup hooks_dup.add_hook(:other_test_hook, :testing) { :okay_man } expect(hooks_dup.get_hook(:other_test_hook, :testing)) .not_to eq @hooks.get_hook(:other_test_hook, :testing) end it 'adding a new hook to dupped instance should not affect original' do @hooks.add_hook(:test_hook, :testing) { :none_such } hooks_dup = @hooks.dup hooks_dup.add_hook(:test_hook, :testing2) { :okay_man } expect(hooks_dup.get_hook(:test_hook, :testing2)) .not_to eq @hooks.get_hook(:test_hook, :testing2) end end describe "getting hooks" do describe "get_hook" do it 'should return the correct requested hook' do run1 = false run2 = false @hooks.add_hook(:test_hook, :my_name) { run1 = true } @hooks.add_hook(:test_hook, :my_name2) { run2 = true } @hooks.get_hook(:test_hook, :my_name).call expect(run1).to eq true expect(run2).to eq false end it 'should return nil if hook does not exist' do expect(@hooks.get_hook(:test_hook, :my_name)).to eq nil end end describe "get_hooks" do it 'should return a hash of hook names/hook functions for an event' do hook1 = proc { 1 } hook2 = proc { 2 } @hooks.add_hook(:test_hook, :my_name1, hook1) @hooks.add_hook(:test_hook, :my_name2, hook2) hash = @hooks.get_hooks(:test_hook) expect(hash.size).to eq 2 expect(hash[:my_name1]).to eq hook1 expect(hash[:my_name2]).to eq hook2 end it 'should return an empty hash if no hooks defined' do expect(@hooks.get_hooks(:test_hook)).to eq({}) end end end describe "clearing all hooks for an event" do it 'should clear all hooks' do @hooks.add_hook(:test_hook, :my_name) {} @hooks.add_hook(:test_hook, :my_name2) {} @hooks.add_hook(:test_hook, :my_name3) {} @hooks.clear_event_hooks(:test_hook) expect(@hooks.hook_count(:test_hook)).to eq 0 end end describe "deleting a hook" do it 'should successfully delete a hook' do @hooks.add_hook(:test_hook, :my_name) {} @hooks.delete_hook(:test_hook, :my_name) expect(@hooks.hook_count(:test_hook)).to eq 0 end it 'should return the deleted hook' do run = false @hooks.add_hook(:test_hook, :my_name) { run = true } @hooks.delete_hook(:test_hook, :my_name).call expect(run).to eq true end it 'should return nil if hook does not exist' do expect(@hooks.delete_hook(:test_hook, :my_name)).to eq nil end end describe "executing a hook" do it 'should execute block hook' do run = false @hooks.add_hook(:test_hook, :my_name) { run = true } @hooks.exec_hook(:test_hook) expect(run).to eq true end it 'should execute proc hook' do run = false @hooks.add_hook(:test_hook, :my_name, proc { run = true }) @hooks.exec_hook(:test_hook) expect(run).to eq true end it 'should execute a general callable hook' do callable = Object.new.tap do |obj| obj.instance_variable_set(:@test_var, nil) class << obj attr_accessor :test_var def call @test_var = true end end end @hooks.add_hook(:test_hook, :my_name, callable) @hooks.exec_hook(:test_hook) expect(callable.test_var).to eq true end it 'should execute all hooks for an event if more than one is defined' do x = nil y = nil @hooks.add_hook(:test_hook, :my_name1) { y = true } @hooks.add_hook(:test_hook, :my_name2) { x = true } @hooks.exec_hook(:test_hook) expect(x).to eq true expect(y).to eq true end it 'should execute hooks in order' do array = [] @hooks.add_hook(:test_hook, :my_name1) { array << 1 } @hooks.add_hook(:test_hook, :my_name2) { array << 2 } @hooks.add_hook(:test_hook, :my_name3) { array << 3 } @hooks.exec_hook(:test_hook) expect(array).to eq [1, 2, 3] end it 'return value of exec_hook should be that of last executed hook' do @hooks.add_hook(:test_hook, :my_name1) { 1 } @hooks.add_hook(:test_hook, :my_name2) { 2 } @hooks.add_hook(:test_hook, :my_name3) { 3 } expect(@hooks.exec_hook(:test_hook)).to eq 3 end it 'should add exceptions to the errors array' do @hooks.add_hook(:test_hook, :foo1) { raise 'one' } @hooks.add_hook(:test_hook, :foo2) { raise 'two' } @hooks.add_hook(:test_hook, :foo3) { raise 'three' } @hooks.exec_hook(:test_hook) expect(@hooks.errors.map(&:message)).to eq %w[one two three] end it 'should return the last exception raised as the return value' do @hooks.add_hook(:test_hook, :foo1) { raise 'one' } @hooks.add_hook(:test_hook, :foo2) { raise 'two' } @hooks.add_hook(:test_hook, :foo3) { raise 'three' } expect(@hooks.exec_hook(:test_hook)).to eq @hooks.errors.last end end describe "integration tests" do describe "when_started hook" do it 'should yield options to the hook' do options = nil Pry.config.hooks.add_hook(:when_started, :test_hook) do |_target, opt, _| options = opt end redirect_pry_io(StringIO.new("exit"), StringIO.new) do Pry.start binding, hello: :baby end expect(options[:hello]).to eq :baby Pry.config.hooks.delete_hook(:when_started, :test_hook) end describe "target" do it 'should yield the target, as a binding ' do b = nil Pry.config.hooks.add_hook(:when_started, :test_hook) do |target, _opt, _| b = target end redirect_pry_io(StringIO.new("exit"), StringIO.new) do Pry.start 5, hello: :baby end expect(b.is_a?(Binding)).to eq true Pry.config.hooks.delete_hook(:when_started, :test_hook) end it 'should yield the target to the hook' do b = nil Pry.config.hooks.add_hook(:when_started, :test_hook) do |target, _opt, _| b = target end redirect_pry_io(StringIO.new("exit"), StringIO.new) do Pry.start 5, hello: :baby end expect(b.eval('self')).to eq 5 Pry.config.hooks.delete_hook(:when_started, :test_hook) end end it 'should allow overriding of target (and binding_stack)' do o = Object.new class << o; attr_accessor :value; end Pry.config.hooks.add_hook( :when_started, :test_hook ) do |_target, _opt, pry_instance| pry_instance.binding_stack = [Pry.binding_for(o)] end redirect_pry_io(InputTester.new("@value = true", "exit-all")) do Pry.start binding, hello: :baby end expect(o.value).to eq true Pry.config.hooks.delete_hook(:when_started, :test_hook) end end describe "after_session hook" do it 'should always run, even if uncaught exception bubbles out of repl' do o = OpenStruct.new o.great_escape = Class.new(StandardError) old_ew = Pry.config.unrescued_exceptions Pry.config.unrescued_exceptions << o.great_escape array = [1, 2, 3, 4, 5] begin redirect_pry_io(StringIO.new("raise great_escape"), StringIO.new) do Pry.start( o, hooks: Pry::Hooks.new.add_hook(:after_session, :cleanup) { array = nil } ) end rescue StandardError => ex exception = ex end # ensure that an exception really was raised and it broke out # of the repl expect(exception.is_a?(o.great_escape)).to eq true # check that after_session hook ran expect(array).to eq nil # cleanup after test Pry.config.unrescued_exceptions = old_ew end describe "before_eval hook" do describe "modifying input code" do it 'should replace input code with code determined by hook' do hooks = Pry::Hooks.new.add_hook(:before_eval, :quirk) do |code, _pry| code.replace(":little_duck") end redirect_pry_io(InputTester.new(":jemima", "exit-all"), out = StringIO.new) do Pry.start(self, hooks: hooks) end expect(out.string).to match(/little_duck/) expect(out.string).not_to match(/jemima/) end it 'should not interfere with command processing when replacing input code' do commands = Pry::CommandSet.new do import_from Pry::Commands, "exit-all" command "how-do-you-like-your-blue-eyed-boy-now-mister-death" do output.puts "in hours of bitterness i imagine balls of sapphire, of metal" end end hooks = Pry::Hooks.new.add_hook(:before_eval, :quirk) do |code, _pry| code.replace(":little_duck") end redirect_pry_io( InputTester.new( "how-do-you-like-your-blue-eyed-boy-now-mister-death", "exit-all" ), out = StringIO.new ) do Pry.start(self, hooks: hooks, commands: commands) end expect(out.string).to match( /in hours of bitterness i imagine balls of sapphire, of metal/ ) expect(out.string).not_to match(/little_duck/) end end end describe "exceptions" do before do Pry.config.hooks.add_hook(:after_eval, :baddums) { raise "Baddums" } Pry.config.hooks.add_hook(:after_eval, :simbads) { raise "Simbads" } end after do Pry.config.hooks.delete_hook(:after_eval, :baddums) Pry.config.hooks.delete_hook(:after_eval, :simbads) end it "should not raise exceptions" do expect { mock_pry("1", "2", "3") }.to_not raise_error end it "should print out a notice for each exception raised" do expect(mock_pry("1")).to match( /after_eval\shook\sfailed:\sRuntimeError:\sBaddums\n .*after_eval\shook\sfailed:\sRuntimeError:\sSimbads/xm ) end end end end describe "anonymous hooks" do it 'should allow adding of hook without a name' do @hooks.add_hook(:test_hook, nil) {} expect(@hooks.hook_count(:test_hook)).to eq 1 end it 'should only allow one anonymous hook to exist' do @hooks.add_hook(:test_hook, nil) {} @hooks.add_hook(:test_hook, nil) {} expect(@hooks.hook_count(:test_hook)).to eq 1 end it 'should execute most recently added anonymous hook' do x = nil y = nil @hooks.add_hook(:test_hook, nil) { y = 1 } @hooks.add_hook(:test_hook, nil) { x = 2 } @hooks.exec_hook(:test_hook) expect(y).to eq nil expect(x).to eq 2 end end end pry-0.13.1/spec/indent_spec.rb000066400000000000000000000156361364454223300162010ustar00rootroot00000000000000# frozen_string_literal: true # Please keep in mind that any hash signs ("#") in the heredoc strings are # placed on purpose. Without these editors might remove the whitespace on empty # lines. describe Pry::Indent do before do @indent = Pry::Indent.new(Pry.new) end it 'should indent an array' do input = "array = [\n10,\n15\n]" output = "array = [\n 10,\n 15\n]" expect(@indent.indent(input)).to eq output end it 'should indent a hash' do input = "hash = {\n:name => 'Ruby'\n}" output = "hash = {\n :name => 'Ruby'\n}" expect(@indent.indent(input)).to eq output end it 'should indent a function' do input = "def\nreturn 10\nend" output = "def\n return 10\nend" expect(@indent.indent(input)).to eq output end it 'should indent a module and class' do input = "module Foo\n# Hello world\nend" output = "module Foo\n # Hello world\nend" input_class = "class Foo\n# Hello world\nend" output_class = "class Foo\n # Hello world\nend" expect(@indent.indent(input)).to eq output expect(@indent.indent(input_class)).to eq output_class end it 'should indent separate lines' do expect(@indent.indent('def foo')).to eq 'def foo' expect(@indent.indent('return 10')).to eq ' return 10' expect(@indent.indent('end')).to eq 'end' end it 'should not indent single line statements' do input = < do" do input = "while 5 do\n5\nend" expect(@indent.indent(input)).to eq "while 5 do\n 5\nend" end it "should ident case statements" do input = <\n[]}]\n]")).to eq( "[[{\n [] =>\n []}]\n]" ) end it "should not indent single-line ifs" do expect(@indent.indent("foo if bar\n#")).to eq "foo if bar\n#" expect(@indent.reset.indent("foo() if bar\n#")).to eq "foo() if bar\n#" expect(@indent.reset.indent("foo 'hi' if bar\n#")).to eq "foo 'hi' if bar\n#" expect(@indent.reset.indent("foo 1 while bar\n#")).to eq "foo 1 while bar\n#" expect(@indent.reset.indent("$foo if false\n#")).to eq "$foo if false\n#" expect(@indent.reset.indent("@foo if false\n#")).to eq "@foo if false\n#" expect(@indent.reset.indent("@@foo if false\n#")).to eq "@@foo if false\n#" expect(@indent.reset.indent("super if true\n#")).to eq "super if true\n#" expect(@indent.reset.indent("true if false\n#")).to eq "true if false\n#" expect(@indent.reset.indent("String if false\n#")).to eq "String if false\n#" end it "should indent cunningly disguised ifs" do expect(@indent.indent("{1 => if bar\n#")).to eq "{1 => if bar\n #" expect(@indent.reset.indent("foo(if bar\n#")).to eq "foo(if bar\n #" expect(@indent.reset.indent("bar(baz, if bar\n#")).to eq "bar(baz, if bar\n #" expect(@indent.reset.indent("[if bar\n#")).to eq "[if bar\n #" expect(@indent.reset.indent("true; while bar\n#")).to eq "true; while bar\n #" end it "should differentiate single/multi-line unless" do expect(@indent.indent("foo unless bar\nunless foo\nbar\nend")).to eq( "foo unless bar\nunless foo\n bar\nend" ) end it "should not indent single/multi-line until" do expect(@indent.indent("%w{baz} until bar\nuntil foo\nbar\nend")).to eq( "%w{baz} until bar\nuntil foo\n bar\nend" ) end it "should indent begin rescue end" do input = < :wrong rescue => e doit :right end INPUT output = < :wrong rescue => e doit :right end OUTPUT expect(@indent.indent(input)).to eq output end it "should not indent single-line rescue" do input = <(out) { out.strip.sub("\e[0G", "") } end context "ARGV forwarding" do let(:code) { "p(ARGV) and exit".shellescape } it "forwards ARGV as an empty array when - is passed without following arguments" do out = clean_output.call(`#{ruby} -I#{pry_dir} bin/pry -e #{code} -`) expect(out).to eq([].inspect) end it "forwards ARGV as an empty array when -- is passed without following arguments" do out = clean_output.call(`#{ruby} -I#{pry_dir} bin/pry -e #{code} --`) expect(out).to eq([].inspect) end it "forwards its remaining arguments as ARGV when - is passed" do out = clean_output.call( `#{ruby} -I#{pry_dir} bin/pry -e #{code} - 1 -foo --bar --baz daz` ) expect(out).to eq(%w[1 -foo --bar --baz daz].inspect) end it "forwards its remaining arguments as ARGV when -- is passed" do out = clean_output.call( `#{ruby} -I#{pry_dir} bin/pry -e #{code} -- 1 -foo --bar --baz daz` ) expect(out).to eq(%w[1 -foo --bar --baz daz].inspect) end end context '-I path' do it 'adds an additional path to $LOAD_PATH' do code = 'p($LOAD_PATH) and exit' out = clean_output.call( `#{ruby} -I#{pry_dir} bin/pry -I /added/at/cli -e '#{code}'` ) expect(out).to include('/added/at/cli') end it 'adds multiple additional paths to $LOAD_PATH' do code = 'p($LOAD_PATH) and exit' out = clean_output.call( # rubocop:disable Metrics/LineLength `#{ruby} -I#{pry_dir} bin/pry -I /added-1/at/cli -I /added/at/cli/also -e '#{code}'` # rubocop:enable Metrics/LineLength ) expect(out).to include('/added-1/at/cli') expect(out).to include('/added/at/cli/also') end end end pry-0.13.1/spec/integration/hanami_spec.rb000066400000000000000000000020671364454223300204720ustar00rootroot00000000000000# frozen_string_literal: true require "shellwords" require 'rbconfig' RSpec.describe "Hanami integration" do before :all do @ruby = RbConfig.ruby.shellescape @pry_dir = File.expand_path(File.join(__FILE__, '../../../lib')).shellescape end it "does not enter an infinite loop (#1471, #1621)" do if RUBY_VERSION.start_with? "1.9" skip "prepend is not supported on this version of Ruby" end code = <<-RUBY require "pry" require "timeout" module Prepend1 def call(arg) super end end module Prepend2 def call(arg) super end end class Action prepend Prepend1 prepend Prepend2 def call(arg) binding.pry input: StringIO.new("exit"), output: StringIO.new end end Timeout.timeout(1) { Action.new.call("define prison, in the abstract sense") } exit 42 RUBY `#{@ruby} -I#{@pry_dir} -e'#{code}'` expect($CHILD_STATUS.exitstatus).to eq(42) end end pry-0.13.1/spec/integration/readline_spec.rb000066400000000000000000000021561364454223300210170ustar00rootroot00000000000000# frozen_string_literal: true # These specs ensure that Pry doesn't require readline until the first time a # REPL is started. require "shellwords" require 'rbconfig' RSpec.describe "Readline" do before :all do @ruby = RbConfig.ruby.shellescape @pry_dir = File.expand_path(File.join(__FILE__, '../../../lib')).shellescape end it "is not loaded on requiring 'pry'" do code = <<-RUBY require "pry" p defined?(Readline) RUBY expect(`#{@ruby} -I #{@pry_dir} -e '#{code}'`).to eq("nil\n") end it "is loaded on invoking 'pry'" do code = <<-RUBY require "pry" Pry.start self, input: StringIO.new("exit-all"), output: StringIO.new puts defined?(Readline) RUBY expect(`#{@ruby} -I #{@pry_dir} -e '#{code}'`.end_with?("constant\n")).to eq(true) end it "is not loaded on invoking 'pry' if Pry.input is set" do code = <<-RUBY require "pry" Pry.input = StringIO.new("exit-all") Pry.start self, output: StringIO.new p defined?(Readline) RUBY expect(`#{@ruby} -I #{@pry_dir} -e '#{code}'`.end_with?("nil\n")).to eq(true) end end pry-0.13.1/spec/method/000077500000000000000000000000001364454223300146265ustar00rootroot00000000000000pry-0.13.1/spec/method/patcher_spec.rb000066400000000000000000000022501364454223300176120ustar00rootroot00000000000000# frozen_string_literal: true describe Pry::Method::Patcher do # rubocop:disable Style/SingleLineMethods before do @x = Object.new def @x.test; :before; end @method = Pry::Method(@x.method(:test)) end # rubocop:enable Style/SingleLineMethods it "should change the behaviour of the method" do expect(@x.test).to eq :before @method.redefine "def @x.test; :after; end\n" expect(@x.test).to eq :after end it "should return a new method with new source" do expect(@method.source.strip).to eq "def @x.test; :before; end" expect(@method.redefine("def @x.test; :after; end\n") .source.strip).to eq "def @x.test; :after; end" end it "should change the source of new Pry::Method objects" do @method.redefine "def @x.test; :after; end\n" expect(Pry::Method(@x.method(:test)).source.strip).to eq "def @x.test; :after; end" end it "should preserve visibility" do class << @x private :test # rubocop:disable Style/AccessModifierDeclarations end expect(@method.visibility).to eq :private @method.redefine "def @x.test; :after; end\n" expect(Pry::Method(@x.method(:test)).visibility).to eq :private end end pry-0.13.1/spec/method_spec.rb000066400000000000000000000542461364454223300162000ustar00rootroot00000000000000# frozen_string_literal: true require 'set' describe Pry::Method do it "should use String names for compatibility" do klass = Class.new { def hello; end } expect(Pry::Method.new(klass.instance_method(:hello)).name).to eq "hello" end describe ".from_str" do it 'looks up instance methods if no methods available and no options provided' do klass = Class.new { def hello; end } meth = Pry::Method.from_str(:hello, Pry.binding_for(klass)) expect(meth).to eq klass.instance_method(:hello) end it 'looks up methods if no instance methods available and no options provided' do klass = Class.new { def self.hello; end } meth = Pry::Method.from_str(:hello, Pry.binding_for(klass)) expect(meth).to eq klass.method(:hello) end it( 'looks up instance methods first even if methods available and no ' \ 'options provided' ) do klass = Class.new do def hello; end def self.hello; end end meth = Pry::Method.from_str(:hello, Pry.binding_for(klass)) expect(meth).to eq klass.instance_method(:hello) end it 'should look up instance methods if "instance-methods" option provided' do klass = Class.new do def hello; end def self.hello; end end meth = Pry::Method.from_str( :hello, Pry.binding_for(klass), "instance-methods" => true ) expect(meth).to eq klass.instance_method(:hello) end it 'should look up methods if :methods option provided' do klass = Class.new do def hello; end def self.hello; end end meth = Pry::Method.from_str(:hello, Pry.binding_for(klass), methods: true) expect(meth).to eq klass.method(:hello) end it 'should look up instance methods using the Class#method syntax' do klass = Class.new do def hello; end def self.hello; end end meth = Pry::Method.from_str("klass#hello", Pry.binding_for(binding)) expect(meth).to eq klass.instance_method(:hello) end it 'should look up methods using the object.method syntax' do klass = Class.new do def hello; end def self.hello; end end meth = Pry::Method.from_str("klass.hello", Pry.binding_for(binding)) expect(meth).to eq klass.method(:hello) end it( 'should NOT look up instance methods using the Class#method syntax if ' \ 'no instance methods defined' ) do _klass = Class.new { def self.hello; end } meth = Pry::Method.from_str("_klass#hello", Pry.binding_for(binding)) expect(meth).to eq nil end it( 'should NOT look up methods using the object.method syntax if no ' \ 'methods defined' ) do _klass = Class.new { def hello; end } meth = Pry::Method.from_str("_klass.hello", Pry.binding_for(binding)) expect(meth).to eq nil end it 'should look up methods using klass.new.method syntax' do _klass = Class.new { def hello; :hello; end } meth = Pry::Method.from_str("_klass.new.hello", Pry.binding_for(binding)) expect(meth.name).to eq "hello" end it 'should take care of corner cases like mongo[] e.g Foo::Bar.new[]- issue 998' do _klass = Class.new { def []; :hello; end } meth = Pry::Method.from_str("_klass.new[]", Pry.binding_for(binding)) expect(meth.name).to eq "[]" end it 'should take care of cases like $ mongo[] - issue 998' do f = Class.new { def []; :hello; end }.new meth = Pry::Method.from_str("f[]", Pry.binding_for(binding)) expect(meth).to eq f.method(:[]) end it 'should look up instance methods using klass.meth#method syntax' do _klass = Class.new { def self.meth; Class.new; end } meth = Pry::Method.from_str("_klass.meth#initialize", Pry.binding_for(binding)) expect(meth.name).to eq "initialize" end it 'should look up methods using instance::bar syntax' do _klass = Class.new { def self.meth; Class.new; end } meth = Pry::Method.from_str("_klass::meth", Pry.binding_for(binding)) expect(meth.name).to eq "meth" end it 'should not raise an exception if receiver does not exist' do expect { Pry::Method.from_str("random_klass.meth", Pry.binding_for(binding)) } .to_not raise_error end end describe '.from_binding' do it 'should be able to pick a method out of a binding' do klass = Class.new { def self.foo; binding; end }.foo expect(Pry::Method.from_binding(klass).name).to eq('foo') end it 'should NOT find a method from the toplevel binding' do expect(Pry::Method.from_binding(TOPLEVEL_BINDING)).to eq nil end it "should find methods that have been undef'd" do c = Class.new do def self.bar class << self; undef bar; end binding end end m = Pry::Method.from_binding(c.bar) expect(m.name).to eq "bar" end it 'should find the super method correctly' do # rubocop:disable Layout/EmptyLineBetweenDefs a = Class.new { def gag33; binding; end; def self.line; __LINE__; end } # rubocop:enable Layout/EmptyLineBetweenDefs b = Class.new(a) { def gag33; super; end } g = b.new.gag33 m = Pry::Method.from_binding(g) expect(m.owner).to eq a expect(m.source_line).to eq a.line expect(m.name).to eq "gag33" end it 'should find the right method if a super method exists' do a = Class.new { def gag; binding; end; } # rubocop:disable Layout/EmptyLineBetweenDefs b = Class.new(a) { def gag; super; binding; end; def self.line; __LINE__; end } # rubocop:enable Layout/EmptyLineBetweenDefs m = Pry::Method.from_binding(b.new.gag) expect(m.owner).to eq b expect(m.source_line).to eq b.line expect(m.name).to eq "gag" end it "should find the right method from a BasicObject" do # rubocop:disable Layout/EmptyLineBetweenDefs a = Class.new(BasicObject) do def gag; ::Kernel.binding; end; def self.line; __LINE__; end end # rubocop:enable Layout/EmptyLineBetweenDefs m = Pry::Method.from_binding(a.new.gag) expect(m.owner).to eq a expect(m.source_file).to eq __FILE__ expect(m.source_line).to eq a.line end it 'should find the right method even if it was renamed and replaced' do o = Object.new class << o def borscht @nips = "nips" binding end alias_method :paella, :borscht def borscht() paella end end m = Pry::Method.from_binding(o.borscht) expect(m.source).to eq Pry::Method(o.method(:paella)).source end end describe 'super' do it 'should be able to find the super method on a bound method' do a = Class.new { def rar; 4; end } b = Class.new(a) { def rar; super; end } obj = b.new zuper = Pry::Method(obj.method(:rar)).super expect(zuper.owner).to eq a expect(zuper.receiver).to eq obj end it 'should be able to find the super method of an unbound method' do a = Class.new { def rar; 4; end } b = Class.new(a) { def rar; super; end } zuper = Pry::Method(b.instance_method(:rar)).super expect(zuper.owner).to eq a end it 'should return nil if no super method exists' do a = Class.new { def rar; super; end } expect(Pry::Method(a.instance_method(:rar)).super).to eq nil end it 'should be able to find super methods defined on modules' do m = Module.new { def rar; 4; end } a = Class.new { def rar; super; end; include m } zuper = Pry::Method(a.new.method(:rar)).super expect(zuper.owner).to eq m end it( 'should be able to find super methods defined on super-classes when ' \ 'there are modules in the way' ) do a = Class.new { def rar; 4; end } m = Module.new { def mooo; 4; end } b = Class.new(a) { def rar; super; end; include m } zuper = Pry::Method(b.new.method(:rar)).super expect(zuper.owner).to eq a end it 'jumps up multiple levels of bound method, even through modules' do a = Class.new { def rar; 4; end } m = Module.new { def rar; 4; end } b = Class.new(a) { def rar; super; end; include m } zuper = Pry::Method(b.new.method(:rar)).super expect(zuper.owner).to eq m expect(zuper.super.owner).to eq a end end describe 'all_from_class' do def should_find_method(name) expect(Pry::Method.all_from_class(@class).map(&:name)).to include name end it 'should be able to find public instance methods defined in a class' do @class = Class.new { def meth; 1; end } should_find_method('meth') end it 'finds private and protected instance methods defined in a class' do @class = Class.new do protected def prot; 1; end private def priv; 1; end end should_find_method('priv') should_find_method('prot') end it 'should find methods all the way up to Kernel' do @class = Class.new should_find_method('exit!') end it 'should be able to find instance methods defined in a super-class' do @class = Class.new(Class.new { def meth; 1; end }) {} should_find_method('meth') end it 'finds instance methods defined in modules included into this class' do @class = Class.new do include(Module.new { def meth; 1; end }) end should_find_method('meth') end it 'finds instance methods defined in modules included into super-classes' do super_class = Class.new do include(Module.new { def meth; 1; end }) end @class = Class.new(super_class) should_find_method('meth') end it 'should attribute overridden methods to the sub-class' do super_class = Class.new do include(Module.new { def meth; 1; end }) end @class = Class.new(super_class) { def meth; 2; end } expect(Pry::Method.all_from_class(@class).detect { |x| x.name == 'meth' }.owner) .to eq @class end it 'should be able to find methods defined on a singleton class' do @class = (class << Object.new; def meth; 1; end; self; end) should_find_method('meth') end it 'should be able to find methods on super-classes when given a singleton class' do @class = (class << Class.new { def meth; 1; end }.new; self; end) should_find_method('meth') end end describe 'all_from_obj' do describe 'on normal objects' do def should_find_method(name) expect(Pry::Method.all_from_obj(@obj).map(&:name)).to include name end it "should find methods defined in the object's class" do @obj = Class.new { def meth; 1; end }.new should_find_method('meth') end it "should find methods defined in modules included into the object's class" do @obj = Class.new do include(Module.new { def meth; 1; end }) end.new should_find_method('meth') end it "should find methods defined in the object's singleton class" do @obj = Object.new class << @obj; def meth; 1; end; end should_find_method('meth') end it "should find methods in modules included into the object's singleton class" do @obj = Object.new @obj.extend(Module.new { def meth; 1; end }) should_find_method('meth') end it "should find methods all the way up to Kernel" do @obj = Object.new should_find_method('exit!') end it "should not find methods defined on the classes singleton class" do @obj = Class.new { class << self; def meth; 1; end; end }.new expect(Pry::Method.all_from_obj(@obj).map(&:name)).not_to include 'meth' end it "should work in the face of an overridden send" do @obj = Class.new do def meth; 1; end def send; raise EOFError; end end.new should_find_method('meth') end end describe 'on classes' do def should_find_method(name) expect(Pry::Method.all_from_obj(@class).map(&:name)).to include name end it "should find methods defined in the class' singleton class" do @class = Class.new { class << self; def meth; 1; end; end } should_find_method('meth') end it "should find methods defined on modules extended into the class" do @class = Class.new do extend(Module.new { def meth; 1; end }) end should_find_method('meth') end it "should find methods defined on the singleton class of super-classes" do @class = Class.new(Class.new { class << self; def meth; 1; end; end }) should_find_method('meth') end it "should not find methods defined within the class" do @class = Class.new { def meth; 1; end } expect(Pry::Method.all_from_obj(@class).map(&:name)).not_to include 'meth' end it "should find methods defined on Class" do @class = Class.new should_find_method('allocate') end it "should find methods defined on Kernel" do @class = Class.new should_find_method('exit!') end it "should attribute overridden methods to the sub-class' singleton class" do @class = Class.new(Class.new { class << self; def meth; 1; end; end }) do class << self; def meth; 1; end; end end expect(Pry::Method.all_from_obj(@class).detect { |x| x.name == 'meth' }.owner) .to eq(class << @class; self; end) end it "should attrbute overridden methods to the class not the module" do @class = Class.new do class << self def meth; 1; end end extend(Module.new { def meth; 1; end }) end expect(Pry::Method.all_from_obj(@class).detect { |x| x.name == 'meth' }.owner) .to eq(class << @class; self; end) end it( "attributes overridden methods to the relevant singleton class in " \ "preference to Class" ) do @class = Class.new { class << self; def allocate; 1; end; end } expect(Pry::Method.all_from_obj(@class).detect { |x| x.name == 'allocate' }.owner) .to eq(class << @class; self; end) end end describe 'method resolution order' do module LS class Top; end class Next < Top; end module M; end module N; include M; end module O; include M; end module P; end class Low < Next; include N; include P; end class Lower < Low; extend N; end class Bottom < Lower; extend O; end end def eigen_class(obj); class << obj; self; end; end it "should look at a class and then its superclass" do expect(Pry::Method.instance_resolution_order(LS::Next)) .to eq [LS::Next] + Pry::Method.instance_resolution_order(LS::Top) end it "should include the included modules between a class and its superclass" do expect(Pry::Method.instance_resolution_order(LS::Low)).to eq( [LS::Low, LS::P, LS::N, LS::M] + Pry::Method.instance_resolution_order(LS::Next) ) end it "should not include modules extended into the class" do expect(Pry::Method.instance_resolution_order(LS::Bottom)).to eq( [LS::Bottom] + Pry::Method.instance_resolution_order(LS::Lower) ) end it "should include included modules for Modules" do expect(Pry::Method.instance_resolution_order(LS::O)).to eq [LS::O, LS::M] end it "should include the singleton class of objects" do obj = LS::Low.new expect(Pry::Method.resolution_order(obj)).to eq( [eigen_class(obj)] + Pry::Method.instance_resolution_order(LS::Low) ) end it "should not include singleton classes of numbers" do target_class = 4.class expect(Pry::Method.resolution_order(4)).to eq( Pry::Method.instance_resolution_order(target_class) ) end it "should include singleton classes for classes" do expect(Pry::Method.resolution_order(LS::Low)).to eq( [eigen_class(LS::Low)] + Pry::Method.resolution_order(LS::Next) ) end it "should include modules included into singleton classes" do expect(Pry::Method.resolution_order(LS::Lower)).to eq( [eigen_class(LS::Lower), LS::N, LS::M] + Pry::Method.resolution_order(LS::Low) ) end it "should include modules at most once" do expect(Pry::Method.resolution_order(LS::Bottom).count(LS::M)).to eq 1 end it "should include modules at the point which they would be reached" do expect(Pry::Method.resolution_order(LS::Bottom)).to eq( [eigen_class(LS::Bottom), LS::O] + Pry::Method.resolution_order(LS::Lower) ) end it( "includes the Pry::Method.instance_resolution_order of Class after " \ "the singleton classes" ) do singleton_classes = [ eigen_class(LS::Top), eigen_class(Object), eigen_class(BasicObject), *Pry::Method.instance_resolution_order(Class) ] expect(Pry::Method.resolution_order(LS::Top)).to eq(singleton_classes) end end end describe 'method_name_from_first_line' do it 'should work in all simple cases' do meth = Pry::Method.new(nil) expect(meth.send(:method_name_from_first_line, "def x")).to eq "x" expect(meth.send(:method_name_from_first_line, "def self.x")).to eq "x" expect(meth.send(:method_name_from_first_line, "def ClassName.x")).to eq "x" expect(meth.send(:method_name_from_first_line, "def obj_name.x")).to eq "x" end end describe 'method aliases' do before do @class = Class.new do def eat; end alias_method :fress, :eat alias_method :omnomnom, :fress def eruct; end end end it 'should be able to find method aliases' do meth = Pry::Method(@class.new.method(:eat)) aliases = Set.new(meth.aliases) expect(aliases).to eq Set.new(%w[fress omnomnom]) end it 'should return an empty Array if cannot find aliases' do meth = Pry::Method(@class.new.method(:eruct)) expect(meth.aliases).to be_empty end it 'should not include the own name in the list of aliases' do meth = Pry::Method(@class.new.method(:eat)) expect(meth.aliases).not_to include "eat" end it 'should find aliases for top-level methods' do # top-level methods get added as private instance methods on Object class Object private def my_top_level_method; end alias my_other_top_level_method my_top_level_method end meth = Pry::Method.new(method(:my_top_level_method)) expect(meth.aliases).to include 'my_other_top_level_method' class Object remove_method :my_top_level_method end end it 'should be able to find aliases for methods implemented in C' do meth = Pry::Method({}.method(:key?)) aliases = Set.new(meth.aliases) expect(aliases).to eq Set.new(["include?", "member?", "has_key?"]) end end describe '.signature' do before do @class = Class.new do def self.standard_arg(arg) end def self.block_arg(&block) end def self.rest(*splat) end def self.optional(option = nil) end end end it 'should print the name of regular args' do signature = Pry::Method.new(@class.method(:standard_arg)).signature expect(signature).to eq("standard_arg(arg)") end it 'should print the name of block args, with an & label' do signature = Pry::Method.new(@class.method(:block_arg)).signature expect(signature).to eq("block_arg(&block)") end it 'should print the name of additional args, with an * label' do signature = Pry::Method.new(@class.method(:rest)).signature expect(signature).to eq("rest(*splat)") end it 'should print the name of optional args, with =? after the arg name' do signature = Pry::Method.new(@class.method(:optional)).signature expect(signature).to eq("optional(option=?)") end # keyword args are only on >= Ruby 2.1 if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.1") it 'should print the name of keyword args, with :? after the arg name' do eval <<-RUBY, binding, __FILE__, __LINE__ + 1 def @class.keyword(keyword_arg: '') end RUBY signature = Pry::Method.new(@class.method(:keyword)).signature expect(signature).to eq("keyword(keyword_arg:?)") end it 'should print the name of keyword args, with : after the arg name' do eval <<-RUBY, binding, __FILE__, __LINE__ + 1 def @class.required_keyword(required_key:) end RUBY signature = Pry::Method.new(@class.method(:required_keyword)).signature expect(signature).to eq("required_keyword(required_key:)") end end end describe "#owner" do context "when it is overriden in Object" do before do module OwnerMod def owner :fail end end Object.__send__(:include, OwnerMod) end after { Object.remove_const(:OwnerMod) } it "correctly reports the owner" do method = described_class.new(method(:puts)) expect(method.owner).not_to eq(:fail) end end end describe "#parameters" do context "when it is overriden in Object" do before do module ParametersMod def parameters :fail end end Object.__send__(:include, ParametersMod) end after { Object.remove_const(:ParametersMod) } it "correctly reports the parameters" do method = described_class.new(method(:puts)) expect(method.parameters).not_to eq(:fail) end end end describe "#receiver" do context "when it is overriden in Object" do before do module ReceiverMod def receiver :fail end end Object.__send__(:include, ReceiverMod) end after { Object.remove_const(:ReceiverMod) } it "correctly reports the receiver" do method = described_class.new(method(:puts)) expect(method.receiver).not_to eq(:fail) end end end end pry-0.13.1/spec/output_spec.rb000066400000000000000000000243351364454223300162540ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe Pry::Output do let(:output) { StringIO.new } let(:pry_instance) { Pry.new(output: output) } subject { described_class.new(pry_instance) } describe "#puts" do it "returns nil" do expect(subject.puts).to be_nil end context "when given an empty array" do it "prints a newline" do subject.puts([]) expect(output.string).to eq("\n") end end context "when given multiple empty arrays" do it "prints multiple newline" do subject.puts([], [], []) expect(output.string).to eq("\n\n\n") end end context "when given convertible to array objects" do let(:obj) do Object.new.tap { |o| o.define_singleton_method(:to_ary) { [1] } } end let(:other_obj) do Object.new.tap { |o| o.define_singleton_method(:to_ary) { [2] } } end it "prints the converted objects" do subject.puts([obj, other_obj]) expect(output.string).to eq("1\n2\n") end end context "when given non-convertible to array objects" do let(:obj) { Object.new } let(:other_obj) { Object.new } it "prints the non-converted objects in its string form" do subject.puts([obj, other_obj]) expect(output.string).to match(/\A#\n#\n\z/) end context "and when the object's #to_s has a newline" do let(:obj) do Object.new.tap { |o| o.define_singleton_method(:to_s) { "abc\n" } } end it "doesn't print a double newline" do subject.puts(obj) expect(output.string).to eq("abc\n") end end end context "when the given pry instance has 'color' enabled" do let(:pry_instance) { Pry.new(output: output, color: true) } it "doesn't decolorize output" do subject.puts("\e[30mhi\e[0m") expect(output.string).to eq("\e[30mhi\e[0m\n") end end context "when the given pry instance has 'color' disabled" do let(:pry_instance) { Pry.new(output: output, color: false) } it "decolorizes output" do subject.puts("\e[30mhi\e[0m") expect(output.string).to eq("hi\n") end end end describe "#print" do it "returns nil" do expect(subject.print).to be_nil end context "when the given pry instance has 'color' enabled" do let(:pry_instance) { Pry.new(output: output, color: true) } it "doesn't decolorize output" do subject.print("\e[30mhi\e[0m") expect(output.string).to eq("\e[30mhi\e[0m") end end context "when the given pry instance has 'color' disabled" do let(:pry_instance) { Pry.new(output: output, color: false) } it "decolorizes output" do subject.print("\e[30mhi\e[0m") expect(output.string).to eq('hi') end end end describe "#<<" do specify { expect(subject.method(:<<)).to eq(subject.method(:print)) } end describe "#write" do specify { expect(subject.method(:write)).to eq(subject.method(:print)) } end describe "#tty?" do context "when the output responds to #tty? and is a TTY" do before { expect(output).to receive(:tty?).and_return(true) } it "returns true" do expect(subject).to be_tty end end context "when the output responds to #tty? and is not a TTY" do before do expect(output).to receive(:respond_to?).with(:tty?).and_return(true) expect(output).to receive(:tty?).and_return(false) end it "returns false" do expect(subject).not_to be_tty end end context "when the output doesn't respond to #tty?" do before do expect(output).to receive(:respond_to?).with(:tty?).and_return(false) end it "returns false" do expect(subject).not_to be_tty end end end describe "#method_missing" do context "when the output responds to the given method name" do it "forwards the method to the output" do expect(output).to receive(:abcd) subject.abcd end end context "when the output doesn't respond to the given method name" do it "raises NoMethodError" do expect { subject.abcd }.to raise_error(NoMethodError) end end end describe "#respond_to_missing?" do context "when the output responds to the given method name" do before { output.define_singleton_method(:test_method) {} } it "finds the method that is not defined on self" do expect(subject).to respond_to(:test_method) expect(subject.method(:test_method)).to be_a(Method) end end context "when the output doesn't respond to the given method name" do it "doesn't find the method" do expect(subject).not_to respond_to(:test_method) expect { subject.method(:test_method) }.to raise_error(NameError) end end end describe "#decolorize_maybe" do context "when the given pry instance has 'color' enabled" do let(:pry_instance) { Pry.new(output: output, color: true) } it "returns the given string without modifications" do str = "\e[30mhi\e[0m" expect(subject.decolorize_maybe(str)).to eql(str) end end context "when the given pry instance has 'color' disabled" do let(:pry_instance) { Pry.new(output: output, color: false) } it "returns decolorized string" do expect(subject.decolorize_maybe("\e[30mhi\e[0m")).to eq('hi') end end end describe "#size" do context "when the output is a tty and responds to winsize" do before do skip("io/console doesn't support JRuby") if Pry::Helpers::Platform.jruby? expect(output).to receive(:tty?).and_return(true) expect(output).to receive(:winsize).and_return([1, 1]) end it "returns the io/console winsize" do expect(subject.size).to eq([1, 1]) end end context "when the output is not a tty" do before do skip("io/console doesn't support JRuby") if Pry::Helpers::Platform.jruby? expect(output).to receive(:tty?).and_return(false) allow(Pry::Env).to receive(:[]) end context "and ENV has size info in ROWS and COLUMNS" do before do expect(Pry::Env).to receive(:[]).with('ROWS').and_return(2) expect(Pry::Env).to receive(:[]).with('COLUMNS').and_return(2) end it "returns the ENV variable winsize" do expect(subject.size).to eq([2, 2]) end end context "and ENV has size info in LINES and COLUMNS" do before do expect(Pry::Env).to receive(:[]).with('LINES').and_return(3) expect(Pry::Env).to receive(:[]).with('COLUMNS').and_return(2) end it "returns ENV variable winsize" do expect(subject.size).to eq([3, 2]) end end end context "when the output is not a tty and no info in ENV" do let(:readline) { Object.new } before do unless Pry::Helpers::Platform.jruby? expect(output).to receive(:tty?).and_return(false) end allow(Pry::Env).to receive(:[]) stub_const('Readline', readline) end context "when Readline's size has no zeroes" do before do expect(readline).to receive(:get_screen_size).and_return([1, 1]) end it "returns the Readline winsize" do expect(subject.size).to eq([1, 1]) end end context "when Readline's size has zero column" do before do expect(readline).to receive(:get_screen_size).and_return([1, 0]) end it "returns the default size" do expect(subject.size).to eq([27, 80]) end end end context "when the output is not a tty, and no info in ENV and no Readline info" do let(:readline) { Object.new } before do unless Pry::Helpers::Platform.jruby? expect(output).to receive(:tty?).and_return(false) end allow(Pry::Env).to receive(:[]) stub_const('Readline', readline) expect(readline).to receive(:respond_to?) .with(:get_screen_size).and_return(false) end context "and when there's ANSICON ENV variable" do context "and when it can be matched" do context "and when the size consists of positive integers" do before do expect(Pry::Env).to receive(:[]).with('ANSICON').and_return('(5x5)') end it "returns the ansicon winsize" do expect(subject.size).to eq([5, 5]) end end context "and when the size has a zero column" do before do expect(Pry::Env).to receive(:[]).with('ANSICON').and_return('(0x0)') end it "returns the default winsize" do expect(subject.size).to eq([27, 80]) end end end context "and when it cannot be matched" do before do expect(Pry::Env).to receive(:[]).with('ANSICON').and_return('5x5') end it "returns the default winsize" do expect(subject.size).to eq([27, 80]) end end end context "and when there's no ANSICON ENV variable" do it "returns the default winsize" do expect(subject.size).to eq([27, 80]) end end end end describe "#width" do let(:readline) { Object.new } before do unless Pry::Helpers::Platform.jruby? expect(output).to receive(:tty?).and_return(false) end allow(Pry::Env).to receive(:[]) stub_const('Readline', readline) expect(readline).to receive(:respond_to?) .with(:get_screen_size).and_return(false) end it "returns the number of columns" do expect(subject.width).to eq(80) end end describe "#height" do let(:readline) { Object.new } before do unless Pry::Helpers::Platform.jruby? expect(output).to receive(:tty?).and_return(false) end allow(Pry::Env).to receive(:[]) stub_const('Readline', readline) expect(readline).to receive(:respond_to?) .with(:get_screen_size).and_return(false) end it "returns the number of rows" do expect(subject.height).to eq(27) end end end pry-0.13.1/spec/pager_spec.rb000066400000000000000000000032371364454223300160100ustar00rootroot00000000000000# frozen_string_literal: true describe "Pry::Pager" do describe "PageTracker" do before do @pt = Pry::Pager::PageTracker.new(10, 10) end def record_short_line @pt.record "012345678\n" end def record_long_line @pt.record "0123456789012\n" end def record_multiline @pt.record "0123456789012\n01\n" end def record_string_without_newline @pt.record "0123456789" end def record_string_with_color_codes @pt.record(Pry::SyntaxHighlighter.highlight('0123456789') + "\n") end it "records short lines that don't add up to a page" do 9.times { record_short_line } expect(@pt.page?).to equal false end it "records short lines that do add up to a page" do 10.times { record_short_line } expect(@pt.page?).to equal true end it "treats a long line as taking up more than one row" do 4.times { record_long_line } expect(@pt.page?).to equal false record_long_line expect(@pt.page?).to equal true end it "records a string with an embedded newline" do 3.times { record_multiline } expect(@pt.page?).to equal false record_short_line expect(@pt.page?).to equal true end it "doesn't count a line until it ends" do 12.times { record_string_without_newline } expect(@pt.page?).to equal false record_short_line expect(@pt.page?).to equal true end it "doesn't count ansi color codes towards length" do 9.times { record_string_with_color_codes } expect(@pt.page?).to equal false record_string_with_color_codes expect(@pt.page?).to equal true end end end pry-0.13.1/spec/prompt_spec.rb000066400000000000000000000055261364454223300162360ustar00rootroot00000000000000# frozen_string_literal: true describe Pry::Prompt do describe ".[]" do it "accesses prompts" do expect(described_class[:default]).not_to be_nil end end describe ".all" do it "returns a hash with prompts" do expect(described_class.all).to be_a(Hash) end it "returns a duplicate of original prompts" do described_class.all['foobar'] = Object.new expect(described_class['foobar']).to be_nil end end describe ".add" do after { described_class.instance_variable_get(:@prompts).delete('my_prompt') } it "adds a new prompt" do described_class.add(:my_prompt) expect(described_class[:my_prompt]).to be_a(described_class) end it "raises error when separators.size != 2" do expect { described_class.add(:my_prompt, '', [1, 2, 3]) } .to raise_error(ArgumentError, /separators size must be 2/) end it "raises error on adding a prompt with the same name" do described_class.add(:my_prompt) expect { described_class.add(:my_prompt) } .to raise_error(ArgumentError, /the 'my_prompt' prompt was already added/) end it "returns nil" do expect(described_class.add(:my_prompt)).to be_nil end end describe "#name" do it "returns name" do prompt = described_class.new(:test, '', Array.new(2) { proc { '' } }) expect(prompt.name).to eq(:test) end end describe "#description" do it "returns description" do prompt = described_class.new(:test, 'descr', Array.new(2) { proc { '' } }) expect(prompt.description).to eq('descr') end end describe "#prompt_procs" do it "returns the proc array" do prompt_procs = [proc { '>' }, proc { '*' }] prompt = described_class.new(:test, 'descr', prompt_procs) expect(prompt.prompt_procs).to eq(prompt_procs) end end describe "#wait_proc" do it "returns the first proc" do prompt_procs = [proc { '>' }, proc { '*' }] prompt = described_class.new(:test, '', prompt_procs) expect(prompt.wait_proc).to eq(prompt_procs.first) end end describe "#incomplete_proc" do it "returns the second proc" do prompt_procs = [proc { '>' }, proc { '*' }] prompt = described_class.new(:test, '', prompt_procs) expect(prompt.incomplete_proc).to eq(prompt_procs.last) end end describe "prompt invocation" do let(:pry) { Pry.new } let(:enum) do Enumerator.new do |y| range = ('a'..'z').to_enum loop { y << range.next } end end it "computes prompt name dynamically" do proc = described_class[:default].wait_proc pry.config.prompt_name = Pry::Config::LazyValue.new { enum.next } expect(proc.call(Object.new, 1, pry, '>')).to eq('[1] a(#):1> ') expect(proc.call(Object.new, 1, pry, '>')).to eq('[1] b(#):1> ') end end end pry-0.13.1/spec/pry_defaults_spec.rb000066400000000000000000000336331364454223300174160ustar00rootroot00000000000000# frozen_string_literal: true _version = 1 describe "test Pry defaults" do before do @str_output = StringIO.new end after do Pry.reset_defaults Pry.config.color = false end describe "input" do # Silence deprecation warnings. before { allow(Kernel).to receive(:warn) } it 'should set the input default, and the default should be overridable' do Pry.config.input = InputTester.new("5") Pry.config.output = @str_output Object.new.pry expect(@str_output.string).to match(/5/) Pry.config.output = @str_output Object.new.pry input: InputTester.new("6") expect(@str_output.string).to match(/6/) end it 'should pass in the prompt if readline arity is 1' do Pry.prompt = proc { "A" } arity_one_input = Class.new do attr_accessor :prompt def readline(prompt) @prompt = prompt "exit-all" end end.new Pry.start(self, input: arity_one_input, output: StringIO.new) expect(arity_one_input.prompt).to eq Pry.prompt.call end it 'should not pass in the prompt if the arity is 0' do Pry.prompt = proc { "A" } arity_zero_input = Class.new do def readline "exit-all" end end.new expect { Pry.start(self, input: arity_zero_input, output: StringIO.new) } .to_not raise_error end it 'should not pass in the prompt if the arity is -1' do Pry.prompt = proc { "A" } arity_multi_input = Class.new do attr_accessor :prompt def readline(*args) @prompt = args.first "exit-all" end end.new Pry.start(self, input: arity_multi_input, output: StringIO.new) expect(arity_multi_input.prompt).to eq nil end end it 'should set the output default, and the default should be overridable' do Pry.config.output = @str_output Pry.config.input = InputTester.new("5") Object.new.pry expect(@str_output.string).to match(/5/) Pry.config.input = InputTester.new("6") Object.new.pry expect(@str_output.string).to match(/5\n.*6/) Pry.config.input = InputTester.new("7") @str_output = StringIO.new Object.new.pry output: @str_output expect(@str_output.string).not_to match(/5\n.*6/) expect(@str_output.string).to match(/7/) end it "should set the print default, and the default should be overridable" do new_print = proc { |out, _value| out.puts "=> LOL" } Pry.config.print = new_print expect(Pry.new.print).to eq Pry.config.print Object.new.pry input: InputTester.new("\"test\""), output: @str_output expect(@str_output.string).to eq "=> LOL\n" @str_output = StringIO.new Object.new.pry input: InputTester.new("\"test\""), output: @str_output, print: proc { |out, value| out.puts value.reverse } expect(@str_output.string).to eq "tset\n" expect(Pry.new.print).to eq Pry.config.print @str_output = StringIO.new Object.new.pry input: InputTester.new("\"test\""), output: @str_output expect(@str_output.string).to eq "=> LOL\n" end describe "pry return values" do it 'should return nil' do expect(Pry.start(self, input: StringIO.new("exit-all"), output: StringIO.new)) .to eq nil end it 'should return the parameter given to exit-all' do expect(Pry.start(self, input: StringIO.new("exit-all 10"), output: StringIO.new)) .to eq 10 end it 'should return the parameter (multi word string) given to exit-all' do input = StringIO.new("exit-all \"john mair\"") expect(Pry.start(self, input: input, output: StringIO.new)).to eq "john mair" end it 'should return the parameter (function call) given to exit-all' do input = StringIO.new("exit-all 'abc'.reverse") expect(Pry.start(self, input: input, output: StringIO.new)).to eq 'cba' end it 'should return the parameter (self) given to exit-all' do pry = Pry.start( "carl", input: StringIO.new("exit-all self"), output: StringIO.new ) expect(pry).to eq "carl" end end describe "prompts" do before do Pry.config.output = StringIO.new end def get_prompts(pry) a = pry.select_prompt pry.eval "[" b = pry.select_prompt pry.eval "]" [a, b] end it 'sets the prompt default, and the default should be overridable (single prompt)' do Pry.prompt = Pry::Prompt.new(:test, '', Array.new(2) { proc { '>' } }) new_prompt = Pry::Prompt.new(:new_test, '', Array.new(2) { proc { 'A' } }) pry = Pry.new expect(pry.prompt).to eq Pry.prompt expect(get_prompts(pry)).to eq(%w[> >]) pry = Pry.new(prompt: new_prompt) expect(pry.prompt).to eq(new_prompt) expect(get_prompts(pry)).to eq(%w[A A]) pry = Pry.new expect(pry.prompt).to eq Pry.prompt expect(get_prompts(pry)).to eq(%w[> >]) end it 'sets the prompt default, and the default should be overridable (multi prompt)' do Pry.prompt = Pry::Prompt.new(:test, '', [proc { '>' }, proc { '*' }]) new_prompt = Pry::Prompt.new(:new_test, '', [proc { 'A' }, proc { 'B' }]) pry = Pry.new expect(pry.prompt).to eq Pry.prompt expect(get_prompts(pry)).to eq(%w[> *]) pry = Pry.new(prompt: new_prompt) expect(pry.prompt).to eq(new_prompt) expect(get_prompts(pry)).to eq(%w[A B]) pry = Pry.new expect(pry.prompt).to eq(Pry.prompt) expect(get_prompts(pry)).to eq(%w[> *]) end describe 'storing and restoring the prompt' do let(:prompt1) { Pry::Prompt.new(:test1, '', Array.new(2) { proc { '' } }) } let(:prompt2) { Pry::Prompt.new(:test2, '', Array.new(2) { proc { '' } }) } let(:prompt3) { Pry::Prompt.new(:test3, '', Array.new(2) { proc { '' } }) } let(:pry) { Pry.new(prompt: prompt1) } it 'should have a prompt stack' do pry.push_prompt(prompt2) pry.push_prompt(prompt3) expect(pry.prompt).to eq(prompt3) pry.pop_prompt expect(pry.prompt).to match(prompt2) pry.pop_prompt expect(pry.prompt).to eq(prompt1) end it 'should restore overridden prompts when returning from shell-mode' do pry = Pry.new( prompt: Pry::Prompt.new(:test, '', Array.new(2) { proc { 'P>' } }) ) expect(pry.select_prompt).to eq('P>') pry.process_command('shell-mode') expect(pry.select_prompt).to match(/\Apry .* \$ \z/) pry.process_command('shell-mode') expect(pry.select_prompt).to eq('P>') end it '#pop_prompt should return the popped prompt' do pry.push_prompt(prompt2) pry.push_prompt(prompt3) expect(pry.pop_prompt).to eq(prompt3) expect(pry.pop_prompt).to eq(prompt2) end it 'should not pop the last prompt' do pry.push_prompt(prompt2) expect(pry.pop_prompt).to eq(prompt2) expect(pry.pop_prompt).to eq(prompt1) expect(pry.pop_prompt).to eq(prompt1) expect(pry.prompt).to eq(prompt1) end describe '#prompt= should replace the current prompt with the new prompt' do it 'when only one prompt on the stack' do pry.prompt = prompt2 expect(pry.prompt).to eq(prompt2) expect(pry.pop_prompt).to eq(prompt2) expect(pry.pop_prompt).to eq(prompt2) end it 'when several prompts on the stack' do pry.push_prompt(prompt2) pry.prompt = prompt3 expect(pry.pop_prompt).to eq(prompt3) expect(pry.pop_prompt).to eq(prompt1) end end end end describe "view_clip used for displaying an object in a truncated format" do before do stub_const('DEFAULT_OPTIONS', max_length: 60) stub_const('MAX_LENGTH', 60) end describe "given an object with an #inspect string" do it "returns the #<> format of the object (never use inspect)" do o = Object.new def o.inspect "a" * MAX_LENGTH end expect(Pry.view_clip(o, DEFAULT_OPTIONS)).to match(/# format of the special-cased immediate object if " \ "#inspect is longer than maximum" ) do o = "o" * (MAX_LENGTH + 1) expect(Pry.view_clip(o, DEFAULT_OPTIONS)).to match(/# format of the object (never use inspect)" do o = Object.new def o.inspect "a" * DEFAULT_OPTIONS end expect(Pry.view_clip(o, DEFAULT_OPTIONS)).to match(/# format" do o = Object.new def o.inspect "a" * (DEFAULT_OPTIONS + 1) end expect(Pry.view_clip(o, DEFAULT_OPTIONS)).to match(/# format" do c = Class.new m = Module.new expect(Pry.view_clip(c, DEFAULT_OPTIONS)).to match(/# format" do c = Class.new m = Module.new def c.name "a" * (MAX_LENGTH + 1) end def m.name "a" * (MAX_LENGTH + 1) end expect(Pry.view_clip(c, DEFAULT_OPTIONS)).to match(/# format" do c = Class.new m = Module.new def c.name "a" * MAX_LENGTH end def m.name "a" * MAX_LENGTH end expect(Pry.view_clip(c, DEFAULT_OPTIONS)).to eq c.name expect(Pry.view_clip(m, DEFAULT_OPTIONS)).to eq m.name end end end end end describe 'quiet' do it 'should show whereami by default' do Pry.start( binding, input: InputTester.new("1", "exit-all"), output: @str_output, hooks: Pry::Config.new.hooks ) expect(@str_output.string).to match(/[w]hereami by default/) end it 'should hide whereami if quiet is set' do Pry.new( input: InputTester.new('exit-all'), output: @str_output, quiet: true, hooks: Pry::Config.new.hooks ) expect(@str_output.string).to eq "" end end describe 'toplevel_binding' do it 'should be devoid of local variables' do expect(pry_eval(Pry.toplevel_binding, "ls -l")).not_to match(/_version/) end it 'should have self the same as TOPLEVEL_BINDING' do expect(Pry.toplevel_binding.eval('self')).to equal(TOPLEVEL_BINDING.eval('self')) end it 'should define private methods on Object' do TOPLEVEL_BINDING.eval 'def gooey_fooey; end' expect(method(:gooey_fooey).owner).to eq Object expect(Pry::Method(method(:gooey_fooey)).visibility).to eq :private end end it 'should set the hooks default, and the default should be overridable' do Pry.config.input = InputTester.new("exit-all") Pry.config.hooks = Pry::Hooks.new .add_hook(:before_session, :my_name) { |out, _, _| out.puts "HELLO" } .add_hook(:after_session, :my_name) { |out, _, _| out.puts "BYE" } Object.new.pry output: @str_output expect(@str_output.string).to match(/HELLO/) expect(@str_output.string).to match(/BYE/) Pry.config.input.rewind @str_output = StringIO.new hooks = Pry::Hooks.new .add_hook(:before_session, :my_name) { |out, _, _| out.puts "MORNING" } .add_hook(:after_session, :my_name) { |out, _, _| out.puts "EVENING" } Object.new.pry(output: @str_output, hooks: hooks) expect(@str_output.string).to match(/MORNING/) expect(@str_output.string).to match(/EVENING/) # try below with just defining one hook Pry.config.input.rewind @str_output = StringIO.new hooks = Pry::Hooks.new .add_hook(:before_session, :my_name) { |out, _, _| out.puts "OPEN" } Object.new.pry(output: @str_output, hooks: hooks) expect(@str_output.string).to match(/OPEN/) Pry.config.input.rewind @str_output = StringIO.new hooks = Pry::Hooks.new .add_hook(:after_session, :my_name) { |out, _, _| out.puts "CLOSE" } Object.new.pry(output: @str_output, hooks: hooks) expect(@str_output.string).to match(/CLOSE/) Pry.reset_defaults Pry.config.color = false end end pry-0.13.1/spec/pry_output_spec.rb000066400000000000000000000070721364454223300171450ustar00rootroot00000000000000# frozen_string_literal: true describe Pry do describe "output failsafe" do after { Pry.config.print = Pry::Config.new.print } it "should catch serialization exceptions" do Pry.config.print = proc { raise "catch-22" } expect { mock_pry("1") }.to_not raise_error end it "should display serialization exceptions" do Pry.config.print = proc { raise "catch-22" } expect(mock_pry("1")).to match(/\(pry\) output error: #/) end it "should catch errors serializing exceptions" do Pry.config.print = proc do ex = Exception.new("catch-22") class << ex def inspect raise ex end end raise ex end expect(mock_pry("1")).to match(/\(pry\) output error: failed to show result/) end end describe "default print" do it "should output the right thing" do expect(mock_pry("[1]")).to match(/^=> \[1\]/) end it "should include the =>" do pry = Pry.new accumulator = StringIO.new pry.config.output = accumulator pry.config.print.call(accumulator, [1], pry) expect(accumulator.string).to eq("=> \[1\]\n") end it "should not be phased by un-inspectable things" do expect(mock_pry("class NastyClass; undef pretty_inspect; end", "NastyClass.new")) .to match(/#<.*NastyClass:0x.*?>/) end it "doesn't leak colour for object literals" do expect(mock_pry("Object.new")).to match(/=> #\n/) end end describe "output_prefix" do it "should be able to change output_prefix" do pry = Pry.new accumulator = StringIO.new pry.config.output = accumulator pry.config.output_prefix = "-> " pry.config.print.call(accumulator, [1], pry) expect(accumulator.string).to eq("-> \[1\]\n") end end describe "color" do before do Pry.config.color = true end after do Pry.config.color = false end it "should colorize strings as though they were ruby" do pry = Pry.new accumulator = StringIO.new colorized = Pry::SyntaxHighlighter.highlight('[1]') pry.config.output = accumulator pry.config.print.call(accumulator, [1], pry) expect(accumulator.string).to eq("=> #{colorized}\n") end it "should not colorize strings that already include color" do pry = Pry.new f = Object.new def f.inspect "\e[1;31mFoo\e[0m" end accumulator = StringIO.new pry.config.output = accumulator pry.config.print.call(accumulator, f, pry) # We add an extra \e[0m to prevent color leak expect(accumulator.string).to eq("=> \e[1;31mFoo\e[0m\e[0m\n") end end describe "output suppression" do before do @t = pry_tester end it "should normally output the result" do expect(mock_pry("1 + 2")).to eq("=> 3\n") end it "should not output anything if the input ends with a semicolon" do expect(mock_pry("1 + 2;")).to eq("") end it "should output something if the input ends with a comment" do expect(mock_pry("1 + 2 # basic addition")).to eq("=> 3\n") end it "should not output something if the input is only a comment" do expect(mock_pry("# basic addition")).to eq("") end end describe "custom non-IO object as $stdout" do it "does not crash pry" do old_stdout = $stdout pry_eval = pry_tester(binding) expect(pry_eval.eval("$stdout = Class.new { def write(*) end }.new", ":ok")) .to eq(:ok) $stdout = old_stdout end end end pry-0.13.1/spec/pry_repl_spec.rb000066400000000000000000000063241364454223300165460ustar00rootroot00000000000000# frozen_string_literal: true describe Pry::REPL do it "should let you run commands in the middle of multiline expressions" do ReplTester.start do input 'def a' input '!' output(/^Input buffer cleared/) input '5' output '=> 5' end end it "should rescue exceptions" do ReplTester.start do input 'raise "lorum"' output(/^RuntimeError: lorum/) if defined?(java) input 'raise java.lang.Exception.new("foo")' output(/Exception: foo/) input 'raise java.io.IOException.new("bar")' output(/IOException: bar/) end end end describe "eval_string and binding_stack" do it "shouldn't break if we start a nested REPL" do ReplTester.start do input 'Pry::REPL.new(pry_instance, :target => 10).start' output '' prompt(/10.*> $/) input 'self' output '=> 10' input nil # Ctrl-D output '' input 'self' output '=> main' end end it "shouldn't break if we start a nested instance" do ReplTester.start do input 'Pry.start(10)' output '' prompt(/10.*> $/) input 'self' output '=> 10' input nil # Ctrl-D output '=> nil' # return value of Pry session input 'self' output '=> main' end end it "shouldn't break if we pop bindings in Ruby" do ReplTester.start do input 'cd 10' output '' prompt(/10.*> $/) input 'pry_instance.binding_stack.pop' output(/^=> # $/) input 'pry_instance.binding_stack.pop' output(/^=> # "hello"' prompt(/> $/) end end end describe "space prefix" do describe "with 1 space" do it "it prioritizes variables over commands" do expect(pry_eval(' ls = 2+2', ' ls')).to eq(4) end end describe "with more than 1 space" do it "prioritizes commands over variables" do expect(mock_pry(' ls = 2+2')).to match(/SyntaxError.+unexpected '='/) end end end describe "#piping?" do it "returns false when $stdout is a non-IO object" do repl = described_class.new(Pry.new, {}) old_stdout = $stdout $stdout = Class.new { def write(*) end }.new expect(repl.send(:piping?)).to eq(false) $stdout = old_stdout end end describe "autoindent" do it "should raise no exception when indented with a tab" do ReplTester.start(correct_indent: true, auto_indent: true) do output = @pry.config.output def output.tty? true end input < nil") end end end end pry-0.13.1/spec/pry_spec.rb000066400000000000000000000337111364454223300155240ustar00rootroot00000000000000# frozen_string_literal: true describe Pry do before do @str_output = StringIO.new end describe ".configure" do it "yields a block with Pry.config as its argument" do Pry.config.foo = nil Pry.configure do |config| config.foo = "bar" end expect(Pry.config.foo).to eq("bar") end end describe "Exotic object support" do # regression test for exotic object support it "Should not error when return value is a BasicObject instance" do ReplTester.start do expect(input('BasicObject.new')).to match(/^=> #')).to match(%r{/\n\n/}) end end describe "newline from an empty heredoc" do it "works" do expect(mock_pry('< { TOPLEVEL_BINDING.eval('self') } expect(Pry.binding_for(main.call).is_a?(Binding)).to eq true expect(Pry.binding_for(main.call)).to eq TOPLEVEL_BINDING expect(Pry.binding_for(main.call)).to eq Pry.binding_for(main.call) end end end end describe 'setting custom options' do it 'does not raise for unrecognized options' do expect { Pry.new(custom_option: 'custom value') }.to_not raise_error end it 'correctly handles the :quiet option (#1261)' do instance = Pry.new(quiet: true) expect(instance.quiet?).to eq true end end describe "a fresh instance" do it "should use `caller` as its backtrace" do location = "#{__FILE__}:#{__LINE__ + 1}"[1..-1] # omit leading . backtrace = Pry.new.backtrace expect(backtrace).not_to equal nil expect(backtrace.any? { |l| l.include?(location) }).to equal true end end end pry-0.13.1/spec/pryrc_spec.rb000066400000000000000000000070121364454223300160440ustar00rootroot00000000000000# frozen_string_literal: true describe Pry do describe 'loading rc files' do before do Pry.config.rc_file = 'spec/fixtures/testrc' stub_const('Pry::LOCAL_RC_FILE', 'spec/fixtures/testrc/../testrc') Pry.instance_variable_set(:@initial_session, true) Pry.config.should_load_rc = true Pry.config.should_load_local_rc = true end after do Pry.config.should_load_rc = false Object.remove_const(:TEST_RC) if defined?(TEST_RC) end it "should never run the rc file twice" do Pry.start(self, input: StringIO.new("exit-all\n"), output: StringIO.new) expect(TEST_RC).to eq [0] Pry.start(self, input: StringIO.new("exit-all\n"), output: StringIO.new) expect(TEST_RC).to eq [0] end # Resolving symlinks doesn't work on jruby 1.9 [jruby issue #538] unless Pry::Helpers::Platform.jruby_19? it "should not load the rc file twice if it's symlinked differently" do Pry.config.rc_file = 'spec/fixtures/testrc' stub_const('Pry::LOCAL_RC_FILE', 'spec/fixtures/testlinkrc') Pry.start(self, input: StringIO.new("exit-all\n"), output: StringIO.new) expect(TEST_RC).to eq [0] end end it "should not load the pryrc if pryrc's directory permissions do not allow this" do Dir.mktmpdir do |dir| File.chmod 0o000, dir stub_const('Pry::LOCAL_RC_FILE', File.join(dir, '.pryrc')) Pry.config.should_load_rc = true expect do Pry.start(self, input: StringIO.new("exit-all\n"), output: StringIO.new) end.to_not raise_error File.chmod 0o777, dir end end it "should not load the pryrc if it cannot expand ENV[HOME]" do old_home = ENV['HOME'] ENV['HOME'] = nil Pry.config.should_load_rc = true expect do Pry.start(self, input: StringIO.new("exit-all\n"), output: StringIO.new) end.to_not raise_error ENV['HOME'] = old_home end it "should not run the rc file at all if Pry.config.should_load_rc is false" do Pry.config.should_load_rc = false Pry.config.should_load_local_rc = false Pry.start(self, input: StringIO.new("exit-all\n"), output: StringIO.new) expect(Object.const_defined?(:TEST_RC)).to eq false end describe "that raise exceptions" do before do Pry.config.rc_file = 'spec/fixtures/testrcbad' Pry.config.should_load_local_rc = false putsed = nil # YUCK! horrible hack to get round the fact that output is not configured # at the point this message is printed. (class << Pry; self; end).send(:define_method, :puts) do |str| putsed = str end @doing_it = lambda { input = StringIO.new("Object::TEST_AFTER_RAISE=1\nexit-all\n") Pry.start(self, input: input, output: StringIO.new) putsed } end after do Object.remove_const(:TEST_BEFORE_RAISE) Object.remove_const(:TEST_AFTER_RAISE) (class << Pry; undef_method :puts; end) end it "should not raise exceptions" do expect(&@doing_it).to_not raise_error end it "should continue to run pry" do @doing_it[] expect(Object.const_defined?(:TEST_BEFORE_RAISE)).to eq true expect(Object.const_defined?(:TEST_AFTER_RAISE)).to eq true end it "should output an error" do expect(@doing_it.call.split("\n").first).to match( %r{Error loading .*spec/fixtures/testrcbad: messin with ya} ) end end end end pry-0.13.1/spec/ring_spec.rb000066400000000000000000000053611364454223300156510ustar00rootroot00000000000000# frozen_string_literal: true describe Pry::Ring do let(:ring) { described_class.new(3) } describe "#<<" do it "adds elements as is when the ring is not full" do ring << 1 << 2 << 3 expect(ring.to_a).to eq([1, 2, 3]) end it "overwrites elements when the ring is full" do ring << 1 << 2 << 3 << 4 << 5 expect(ring.to_a).to eq([3, 4, 5]) end it "keeps duplicate elements" do ring << 1 << 1 << 1 << 1 expect(ring.to_a).to eq([1, 1, 1]) end end describe "#[]" do context "when the ring is empty" do it "returns nil" do expect(ring[0]).to be_nil end end context "when the ring is not full" do before { ring << 1 << 2 << 3 } it "reads elements" do expect(ring[0]).to eq(1) expect(ring[1]).to eq(2) expect(ring[2]).to eq(3) expect(ring[-1]).to eq(3) expect(ring[-2]).to eq(2) expect(ring[-3]).to eq(1) end it "reads elements via range" do expect(ring[1..2]).to eq([2, 3]) expect(ring[-2..-1]).to eq([2, 3]) end end context "when the ring is full" do before { ring << 1 << 2 << 3 << 4 << 5 } it "reads elements" do expect(ring[0]).to eq(3) expect(ring[1]).to eq(4) expect(ring[2]).to eq(5) expect(ring[-1]).to eq(5) expect(ring[-2]).to eq(4) expect(ring[-3]).to eq(3) end it "returns the first element when accessed through 0..0" do expect(ring[0..0]).to eq([3]) end it "reads elements via inclusive range" do expect(ring[1..2]).to eq([4, 5]) expect(ring[-2..-1]).to eq([4, 5]) expect(ring[-2..3]).to eq([4, 5]) expect(ring[0..-1]).to eq([3, 4, 5]) expect(ring[2..-1]).to eq([5]) expect(ring[-1..10]).to eq([5]) expect(ring[-1..0]).to eq([]) expect(ring[-1..1]).to eq([]) end it "reads elements via exclusive range" do expect(ring[1...2]).to eq([4]) expect(ring[-2...-1]).to eq([4]) expect(ring[-2...3]).to eq([4, 5]) expect(ring[0...-1]).to eq([3, 4]) expect(ring[2...-1]).to eq([]) expect(ring[-1...10]).to eq([5]) expect(ring[-1...0]).to eq([]) expect(ring[-1...1]).to eq([]) end end end describe "#to_a" do it "returns a duplicate of internal buffer" do array = ring.to_a ring << 1 expect(array.count).to eq(0) expect(ring.count).to eq(1) end end describe "#clear" do it "resets ring to initial state" do ring << 1 expect(ring.count).to eq(1) expect(ring.to_a).to eq([1]) ring.clear expect(ring.count).to eq(0) expect(ring.to_a).to eq([]) end end end pry-0.13.1/spec/run_command_spec.rb000066400000000000000000000011571364454223300172130ustar00rootroot00000000000000# frozen_string_literal: true describe "Pry.run_command" do before do o = Object.new def o.drum "roken is dodelijk" end @context = Pry.binding_for(o) end it 'performs a simple ls' do @context.eval("hokey_pokey = 10") Pry.run_command "ls", context: @context, output: out = StringIO.new expect(out.string).to match(/hokey_pokey/) end # This is a regression test as 0.9.11 broke this behaviour it 'can perform a show-source' do Pry.run_command "show-source drum", context: @context, output: out = StringIO.new expect(out.string).to match(/roken is dodelijk/) end end pry-0.13.1/spec/spec_helper.rb000066400000000000000000000022371364454223300161700ustar00rootroot00000000000000# frozen_string_literal: true if ENV['COVERAGE'] require 'simplecov' SimpleCov.start end require 'pry' require 'rspec' require 'pry/testable' require 'English' require 'stringio' require 'ostruct' Dir['./spec/support/**/*.rb'].map do |file| require file end class Module # False positive: https://github.com/rubocop-hq/rubocop/issues/5953 # rubocop:disable Style/AccessModifierDeclarations public :remove_const public :remove_method # rubocop:enable Style/AccessModifierDeclarations end Pad = OpenStruct.new # to help with tracking down bugs that cause an infinite loop in the test suite if ENV["SET_TRACE_FUNC"] set_trace_func( proc { |event, file, line, id, _binding, classname| STDERR.printf("%8s %s:%-2d %10s %8s\n", event, file, line, id, classname) } ) end RSpec.configure do |config| config.order = 'random' config.before(:each) do Pry::Testable.set_testenv_variables end config.after(:each) do Pry::Testable.unset_testenv_variables Pry.reset_defaults end config.include Pry::Testable::Mockable config.include Pry::Testable::Utility include Pry::Testable::Evalable include Pry::Testable::Variables end pry-0.13.1/spec/sticky_locals_spec.rb000066400000000000000000000130171364454223300175520ustar00rootroot00000000000000# frozen_string_literal: true describe "Sticky locals (_file_ and friends)" do it 'locals should all exist upon initialization' do expect { pry_eval '_file_', '_dir_', '_ex_', 'pry_instance', '_' } .to_not raise_error end it 'locals should still exist after cd-ing into a new context' do expect { pry_eval('cd 0', '_file_', '_dir_', '_ex_', 'pry_instance', '_') } .to_not raise_error end it 'locals should keep value after cd-ing (pry_instance)' do pry_tester.tap do |t| pry = t.eval 'pry_instance' t.eval 'cd 0' expect(t.eval('pry_instance')).to eq(pry) end end describe '_ex_' do it 'returns the last exception without wrapping it in a LastException' do ReplTester.start do input 'raise "halp"' input '_ex_.message == "halp"' output '=> true' input 'Kernel.instance_method(:class).bind(_ex_).call' output '=> RuntimeError' end end it 'keeps its value after cd-ing' do ReplTester.start do input 'error blah' input '$x = _ex_' input 'cd 0' input '_ex_ == $x' output '=> true' end end end it 'locals should keep value after cd-ing (_file_ and _dir_)' do Pry.config.commands.command "file-and-dir-test" do set_file_and_dir_locals("/blah/ostrich.rb") end expect(pry_eval('file-and-dir-test', 'cd 0', '_file_')) .to match(%r{/blah/ostrich\.rb}) expect(pry_eval('file-and-dir-test', 'cd 0', '_dir_')) .to match(%r{/blah}) Pry.config.commands.delete "file-and-dir-test" end it 'locals should return last result (_)' do pry_tester.tap do |t| lam = t.eval 'lambda { |_foo| }' expect(t.eval('_')).to eq(lam) end end it 'locals should return second last result (__)' do pry_tester.tap do |t| lam = t.eval 'lambda { |_foo| }' t.eval 'num = 1' expect(t.eval('__')).to eq(lam) end end describe "User defined sticky locals" do describe "setting as Pry.config option" do it 'should define a new sticky local for the session (normal value)' do Pry.config.extra_sticky_locals[:test_local] = :john o = Object.new redirect_pry_io(InputTester.new("@value = test_local", "exit-all")) do Pry.start(o) end expect(o.instance_variable_get(:@value)).to eq :john Pry.config.extra_sticky_locals = {} end it 'should define a new sticky local for the session (proc)' do Pry.config.extra_sticky_locals[:test_local] = proc { :john } o = Object.new redirect_pry_io(InputTester.new("@value = test_local", "exit-all")) do Pry.start(o) end expect(o.instance_variable_get(:@value)).to eq :john Pry.config.extra_sticky_locals = {} end end describe "passing in as hash option when creating pry instance" do it 'should define a new sticky local for the session (normal value)' do o = Object.new redirect_pry_io(InputTester.new("@value = test_local", "exit-all")) do Pry.start(o, extra_sticky_locals: { test_local: :john }) end expect(o.instance_variable_get(:@value)).to eq :john end it 'should define multiple sticky locals' do o = Object.new redirect_pry_io( InputTester.new( "@value1 = test_local1", "@value2 = test_local2", "exit-all" ) ) do Pry.start( o, extra_sticky_locals: { test_local1: :john, test_local2: :carl } ) end expect(o.instance_variable_get(:@value1)).to eq :john expect(o.instance_variable_get(:@value2)).to eq :carl end it 'should define a new sticky local for the session (as Proc)' do o = Object.new redirect_pry_io(InputTester.new("@value = test_local", "exit-all")) do Pry.start(o, extra_sticky_locals: { test_local: proc { :john } }) end expect(o.instance_variable_get(:@value)).to eq :john end end describe "hash option value should override config value" do it 'should define a new sticky local for the session (normal value)' do Pry.config.extra_sticky_locals[:test_local] = :john o = Object.new redirect_pry_io(InputTester.new("@value = test_local", "exit-all")) do Pry.start(o, extra_sticky_locals: { test_local: :carl }) end expect(o.instance_variable_get(:@value)).to eq :carl Pry.config.extra_sticky_locals = {} end end it 'should create a new sticky local' do t = pry_tester t.eval "pry_instance.add_sticky_local(:test_local){ :test_value }" expect(t.eval("test_local")).to eq(:test_value) end it 'should still exist after cd-ing into new binding' do t = pry_tester t.eval "pry_instance.add_sticky_local(:test_local){ :test_value }" t.eval "cd Object.new" expect(t.eval("test_local")).to eq(:test_value) end it 'should provide different values for successive block invocations' do pry = Pry.new pry.push_binding binding pry.add_sticky_local(:test_local) { rand } value1 = pry.evaluate_ruby 'test_local' value2 = pry.evaluate_ruby 'test_local' expect(value1).not_to eq(value2) end end end pry-0.13.1/spec/support/000077500000000000000000000000001364454223300150625ustar00rootroot00000000000000pry-0.13.1/spec/support/mock_pry.rb000066400000000000000000000015361364454223300172370ustar00rootroot00000000000000# frozen_string_literal: true def mock_pry(*args) args.flatten! binding = args.first.is_a?(Binding) ? args.shift : binding() options = args.last.is_a?(Hash) ? args.pop : {} input = InputTester.new(*args) output = StringIO.new redirect_pry_io(input, output) do binding.pry(options) end output.string end # Set I/O streams. Out defaults to an anonymous StringIO. def redirect_pry_io(new_in, new_out = StringIO.new) old_in = Pry.config.input old_out = Pry.config.output Pry.config.input = new_in Pry.config.output = new_out begin yield ensure Pry.config.input = old_in Pry.config.output = old_out end end class InputTester def initialize(*actions) @orig_actions = actions.dup @actions = actions end def readline(*) @actions.shift end def rewind @actions = @orig_actions.dup end end pry-0.13.1/spec/support/repl_tester.rb000066400000000000000000000046231364454223300177440ustar00rootroot00000000000000# frozen_string_literal: true require 'delegate' # This is for super-high-level integration testing. class ReplTester class Input def initialize(tester_mailbox) @tester_mailbox = tester_mailbox end def readline(prompt) @tester_mailbox.push prompt mailbox.pop end def mailbox Thread.current[:mailbox] end end class Output < SimpleDelegator def clear __setobj__(StringIO.new) end end def self.start(options = {}, &block) Thread.current[:mailbox] = Queue.new instance = nil input = Input.new(Thread.current[:mailbox]) output = Output.new(StringIO.new) redirect_pry_io input, output do instance = new(options) instance.instance_eval(&block) instance.ensure_exit end ensure instance.thread.kill if instance && instance.thread && instance.thread.alive? end include RSpec::Matchers attr_accessor :thread, :mailbox, :last_prompt def initialize(options = {}) @pry = Pry.new(options) @repl = Pry::REPL.new(@pry) @mailbox = Thread.current[:mailbox] @thread = Thread.new do begin Thread.current[:mailbox] = Queue.new @repl.start ensure Thread.current[:session_ended] = true mailbox.push nil end end @should_exit_naturally = false wait # wait until the instance reaches its first readline end # Accept a line of input, as if entered by a user. def input(input) reset_output repl_mailbox.push input wait @pry.output.string end # Assert that the current prompt matches the given string or regex. def prompt(pattern) expect(last_prompt).to match pattern end # Assert that the most recent output (since the last time input was called) # matches the given string or regex. def output(pattern) expect(@pry.output.string.chomp).to match pattern end # Assert that the Pry session ended naturally after the last input. def assert_exited @should_exit_naturally = true end # @private def ensure_exit if @should_exit_naturally raise "Session was not ended!" unless @thread[:session_ended].equal?(true) else input "exit-all" raise "REPL didn't die" unless @thread[:session_ended] end end private def reset_output @pry.output.clear end def repl_mailbox @thread[:mailbox] end def wait @last_prompt = mailbox.pop end end pry-0.13.1/spec/syntax_checking_spec.rb000066400000000000000000000060131364454223300200660ustar00rootroot00000000000000# frozen_string_literal: true describe Pry do before do @str_output = StringIO.new end [ ["p = '", "'"], ["def", "a", "(); end"], ["p = <'val'}.to_json"] ].compact.each do |foo| it "should raise an error on invalid syntax like #{foo.inspect}" do redirect_pry_io(InputTester.new(*foo), @str_output) do Pry.start end expect(@str_output.string).to match(/SyntaxError/) end it "should display correct number of errors on invalid syntax like #{foo.inspect}" do begin # rubocop:disable Security/Eval eval(foo.join("\n")) # rubocop:enable Security/Eval rescue SyntaxError => e error_count = e.message.scan(/syntax error/).count end expect(error_count).not_to be_nil pry_output = mock_pry(*foo) expect(pry_output.scan(/SyntaxError/).count).to eq(error_count) end end it "should not intefere with syntax errors explicitly raised" do input_tester = InputTester.new('raise SyntaxError, "unexpected $end"') redirect_pry_io(input_tester, @str_output) do Pry.start end expect(@str_output.string).to match(/SyntaxError/) end it "should allow trailing , to continue the line" do expect(Pry::Code.complete_expression?("puts 1, 2,")).to eq false end it "should complete an expression that contains a line ending with a ," do expect(Pry::Code.complete_expression?("puts 1, 2,\n3")).to eq true end it "should not suppress the error output if the line ends in ;" do expect(mock_pry("raise RuntimeError, 'foo';")).to match(/RuntimeError/) end it "should not clobber _ex_ on a SyntaxError in the repl" do output = mock_pry( "raise RuntimeError, 'foo'", "puts foo)", "_ex_.is_a?(RuntimeError)" ) expect(output).to match(/^RuntimeError.*\nSyntaxError.*\n=> true/m) end it "should allow whitespace delimeted strings" do expect(mock_pry('"%s" % % foo ')).to match(/"foo"/) end it "should allow newline delimeted strings" do expect(mock_pry('"%s" % %', 'foo')).to match(/"foo"/) end it "should allow whitespace delimeted strings ending on the first char of a line" do expect(mock_pry('"%s" % % ', ' #done!')).to match(/"\\n"/) end end pry-0.13.1/spec/system_command_handler_spec.rb000066400000000000000000000017631364454223300214330ustar00rootroot00000000000000# frozen_string_literal: true require 'stringio' RSpec.describe Pry::SystemCommandHandler do describe ".default" do let(:output) { StringIO.new } let(:pry_instance) { Pry.new } before { allow(Kernel).to receive(:system) } context "when command exists" do before do expect(Kernel).to receive(:system).with('test-command').and_return(true) end it "executes the command without printing the warning" do described_class.default(output, 'test-command', pry_instance) expect(output.string).to be_empty end end context "when doesn't exist" do before do allow(Kernel).to receive(:system).with('test-command').and_return(nil) end it "executes the command without printing the warning" do described_class.default(output, 'test-command', pry_instance) expect(output.string).to eq( "Error: there was a problem executing system command: test-command\n" ) end end end end pry-0.13.1/spec/unrescued_exceptions_spec.rb000066400000000000000000000014201364454223300211400ustar00rootroot00000000000000# frozen_string_literal: true describe "Pry.config.unrescued_exceptions" do before do @str_output = StringIO.new end it 'should rescue all exceptions NOT specified on unrescued_exceptions' do expect(Pry.config.unrescued_exceptions.include?(NameError)).to eq false expect do Pry.start(self, input: StringIO.new("raise NameError\nexit"), output: @str_output) end.not_to raise_error end it 'should NOT rescue exceptions specified on unrescued_exceptions' do old_allowlist = Pry.config.unrescued_exceptions Pry.config.unrescued_exceptions = [NameError] expect do Pry.start(self, input: StringIO.new("raise NameError"), output: @str_output) end.to raise_error NameError Pry.config.unrescued_exceptions = old_allowlist end end pry-0.13.1/spec/warning_spec.rb000066400000000000000000000004331364454223300163520ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe Pry::Warning do describe "#warn" do it "prints message with file and line of the calling frame" do expect(Kernel).to receive(:warn).with(/.+\.rb:\d+: warning: foo bar/) described_class.warn('foo bar') end end end pry-0.13.1/spec/wrapped_module_spec.rb000066400000000000000000000222301364454223300177130ustar00rootroot00000000000000# frozen_string_literal: true describe Pry::WrappedModule do describe "#initialize" do it "should raise an exception when a non-module is passed" do expect { Pry::WrappedModule.new(nil) }.to raise_error ArgumentError end end describe "candidates" do class Host %w[spec/fixtures/candidate_helper1.rb spec/fixtures/candidate_helper2.rb].each do |file| binding.eval(File.read(file), file, 1) # rubocop:disable Security/Eval end # rank 2 class CandidateTest def test6; end end class PitifullyBlank DEFAULT_TEST = CandidateTest end FOREVER_ALONE_LINE = __LINE__ + 1 class ForeverAlone class DoublyNested # nested docs class TriplyNested def nested_method; end end end end end describe "number_of_candidates" do it 'should return the correct number of candidates' do expect(Pry::WrappedModule(Host::CandidateTest).number_of_candidates).to eq 3 end it 'should return 0 candidates for a class with no nested modules or methods' do expect(Pry::WrappedModule(Host::PitifullyBlank).number_of_candidates).to eq 0 end it 'should return 1 candidate for a class with a nested module with methods' do expect(Pry::WrappedModule(Host::ForeverAlone).number_of_candidates).to eq 1 end end describe "ordering of candidates" do it 'should return class with largest number of methods as primary candidate' do expect(Pry::WrappedModule(Host::CandidateTest).candidate(0).file) .to match(/helper1/) end it( 'returns class with second largest number of methods as second ranked candidate' ) do expect(Pry::WrappedModule(Host::CandidateTest).candidate(1).file) .to match(/helper2/) end it 'returns class with third largest number of methods as third ranked candidate' do expect(Pry::WrappedModule(Host::CandidateTest).candidate(2).file) .to match(/#{__FILE__}/) end it 'should raise when trying to access non-existent candidate' do expect { Pry::WrappedModule(Host::CandidateTest).candidate(3) } .to raise_error Pry::CommandError end end describe "source_location" do it 'should return primary candidates source_location by default' do wm = Pry::WrappedModule(Host::CandidateTest) expect(wm.source_location).to eq wm.candidate(0).source_location end it 'returns the location of the outer module if an inner module has methods' do wm = Pry::WrappedModule(Host::ForeverAlone) expect(File.expand_path(wm.source_location.first)) .to eq File.expand_path(__FILE__) expect(wm.source_location.last).to eq Host::FOREVER_ALONE_LINE end it 'should return nil if no source_location can be found' do expect(Pry::WrappedModule(Host::PitifullyBlank).source_location).to eq nil end end describe "source" do it 'should return primary candidates source by default' do wm = Pry::WrappedModule(Host::CandidateTest) expect(wm.source).to eq wm.candidate(0).source end it 'should return source for highest ranked candidate' do expect(Pry::WrappedModule(Host::CandidateTest).candidate(0).source) .to match(/test1/) end it 'should return source for second ranked candidate' do expect(Pry::WrappedModule(Host::CandidateTest).candidate(1).source) .to match(/test4/) end it 'should return source for third ranked candidate' do expect(Pry::WrappedModule(Host::CandidateTest).candidate(2).source) .to match(/test6/) end it 'should return source for deeply nested class' do expect(Pry::WrappedModule(Host::ForeverAlone::DoublyNested::TriplyNested).source) .to match(/nested_method/) mod = Pry::WrappedModule(Host::ForeverAlone::DoublyNested::TriplyNested) expect(mod.source.lines.count).to eq(3) end end describe "doc" do it 'should return primary candidates doc by default' do wm = Pry::WrappedModule(Host::CandidateTest) expect(wm.doc).to eq wm.candidate(0).doc end it 'should return doc for highest ranked candidate' do expect(Pry::WrappedModule(Host::CandidateTest).candidate(0).doc) .to match(/rank 0/) end it 'should return doc for second ranked candidate' do expect(Pry::WrappedModule(Host::CandidateTest).candidate(1).doc) .to match(/rank 1/) end it 'should return doc for third ranked candidate' do expect(Pry::WrappedModule(Host::CandidateTest).candidate(2).doc) .to match(/rank 2/) end it 'should return docs for deeply nested class' do expect(Pry::WrappedModule(Host::ForeverAlone::DoublyNested::TriplyNested).doc) .to match(/nested docs/) end end end describe ".method_prefix" do before do Foo = Class.new @foo = Foo.new end after do Object.remove_const(:Foo) end it "should return Foo# for normal classes" do expect(Pry::WrappedModule.new(Foo).method_prefix).to eq "Foo#" end it "should return Bar# for modules" do expect(Pry::WrappedModule.new(Kernel).method_prefix).to eq "Kernel#" end it "should return Foo. for singleton classes of classes" do expect(Pry::WrappedModule.new(class << Foo; self; end).method_prefix).to eq "Foo." end example "of singleton classes of objects" do expect(Pry::WrappedModule.new(class << @foo; self; end).method_prefix).to eq "self." end example "of anonymous classes should not be empty" do expect(Pry::WrappedModule.new(Class.new).method_prefix).to match(/##/) end example "of singleton classes of anonymous classes should not be empty" do expect(Pry::WrappedModule.new(class << Class.new; self; end).method_prefix) .to match(/#./) end end describe ".singleton_class?" do it "should be true for singleton classes" do mod = Pry::WrappedModule.new(class << Object.new; self; end) expect(mod).to be_singleton_class end it "should be false for normal classes" do expect(Pry::WrappedModule.new(Class.new).singleton_class?).to eq false end it "should be false for modules" do expect(Pry::WrappedModule.new(Module.new).singleton_class?).to eq false end end describe ".singleton_instance" do it "should raise an exception when called on a non-singleton-class" do expect { Pry::WrappedModule.new(Class).singleton_instance } .to raise_error ArgumentError end it "should return the attached object" do instance = Object.new mod = class << instance; self; end expect(Pry::WrappedModule.new(mod).singleton_instance).to eq(instance) klass = class << Object; self; end expect(Pry::WrappedModule.new(klass).singleton_instance).to equal(Object) end end describe ".super" do describe "receiver is a class" do before do @a = Class.new @m = Module.new @b = Class.new(@a) @b.send(:include, @m) @c = Class.new(@b) end it 'should return superclass for a wrapped class' do expect(Pry::WrappedModule(@c).super.wrapped).to eq @b end it 'should return nth superclass for a wrapped class' do d = Class.new(@c) expect(Pry::WrappedModule(d).super(2).wrapped).to eq @b end it 'should ignore modules when retrieving nth superclass' do expect(Pry::WrappedModule(@c).super(2).wrapped).to eq @a end it 'should return nil when no nth superclass exists' do expect(Pry::WrappedModule(@c).super(10)).to eq nil end it 'should return self when .super(0) is used' do c = Pry::WrappedModule(@c) expect(c.super(0)).to eq c end end describe "receiver is a module" do before do @m1 = Module.new @m2 = Module.new.tap { |v| v.send(:include, @m1) } @m3 = Module.new.tap { |v| v.send(:include, @m2) } end it 'should not ignore modules when retrieving supers' do expect(Pry::WrappedModule(@m3).super.wrapped).to eq @m2 end it 'should retrieve nth super' do expect(Pry::WrappedModule(@m3).super(2).wrapped).to eq @m1 end it 'should return self when .super(0) is used' do m = Pry::WrappedModule(@m1) expect(m.super(0)).to eq m end end end describe ".from_str" do before do class Namespace remove_const :Value if defined? Value Value = Class.new end end it 'should lookup a constant' do m = Pry::WrappedModule.from_str("Namespace::Value", binding) expect(m.wrapped).to eq Namespace::Value end it 'should lookup a local' do local = Namespace::Value m = Pry::WrappedModule.from_str("local", binding) expect(m.wrapped).to eq local end it 'should lookup an ivar' do @ivar = Namespace::Value m = Pry::WrappedModule.from_str("@ivar", binding) expect(m.wrapped).to eq Namespace::Value end end end