diff options
author | Kyrylo Silin <silin@kyrylo.org> | 2019-03-25 02:40:05 +0200 |
---|---|---|
committer | Kyrylo Silin <silin@kyrylo.org> | 2019-05-02 00:10:37 +0300 |
commit | e5556a2be8627ec3fe594c738f10422d5a1f5d43 (patch) | |
tree | a1758a1f39915a4eb076793a36d485cb7145d638 | |
parent | 03afca9eafe4f2981edad1cfe4346a643fb64d72 (diff) | |
download | pry-e5556a2be8627ec3fe594c738f10422d5a1f5d43.tar.gz |
Refactor Config
Fixes #1843 (Rework the Pry config)
There are a few breaking changes. They are mostly minor, so I decided not to
indroduce deprecations because it will considerable slow things down.
Key changes:
* `Pry.lazy` was replaced with `Pry::Configuration::LazyValue`
The config accepts three values now `LazyValue`, `MemoizedValue` and simply
`Value`. The main difference is that:
- `Value` is any value, including procs (so that an option returns a raw
proc)
- `LazyValue` is a proc that is call on every invocation of an option
- `MemoizedValue` is a value that is called only once (and then the option
always returns the return value of the )
* `Pry.config.history` was a meta-option that held suboptions. However, the new
config doesn't permit that (unless you know what you do)
Instead, we introduce a few options. For example:
- `Pry.config.history.histignore` becomes `Pry.config.history_ignorelist`
- `Pry.config.history.file` becomes `Pry.config.history_file`
- and so on
This was done so we can simplify configuration merging. Inlining option makes
configuration implementation simpler, without losing much. The rule is that
you want to keep your options under your prefix (if you are a
plugin). Therefore, say, `Pry.config.pry_rescue.*` should be
`Pry.config.pry_rescue_*` if you need merging.
The rest should behave in a similar fashion (and I rely heavily on our test
suite to claim so).
31 files changed, 571 insertions, 851 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index e7c238ac..d259460c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ * 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)) #### API changes @@ -65,6 +68,8 @@ ([#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 @@ -45,9 +45,10 @@ require 'pry/commands/ls/ls_entity' require 'pry/commands/ls/methods_helper' require 'pry/commands/ls' -require 'pry/config/convenience' -require 'pry/config/lazy' -require 'pry/config/behavior' +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' diff --git a/lib/pry/commands/ls/formatter.rb b/lib/pry/commands/ls/formatter.rb index 147e86ec..8b0ae6b6 100644 --- a/lib/pry/commands/ls/formatter.rb +++ b/lib/pry/commands/ls/formatter.rb @@ -20,7 +20,7 @@ class Pry private def color(type, str) - Pry::Helpers::Text.send pry_instance.config.ls["#{type}_color"], str + Pry::Helpers::Text.send pry_instance.config.ls.send("#{type}_color"), str end # Add a new section to the output. diff --git a/lib/pry/config.rb b/lib/pry/config.rb index 194535cc..dea97447 100644 --- a/lib/pry/config.rb +++ b/lib/pry/config.rb @@ -1,37 +1,180 @@ +require 'ostruct' + class Pry - # The Pry config. - # @api public - class Config < Pry::BasicObject - include Behavior - - # @return [Pry::Config] - # An object who implements the default configuration for all - # Pry sessions. - def self.defaults - defaults = from_hash( - input: Pry.lazy { lazy_readline(defaults) }, + # @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<Object>] 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<String,Regexp>] + attribute :history_ignorelist + + # @return [Array<String>] Ruby files to be required + attribute :requires + + # @return [Integer] how many input/output lines to keep in memory + attribute :memory_size + + # @return [Proc] + attribute :control_d_handler + + # @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 + + def initialize + merge!( + input: MemoizedValue.new { lazy_readline }, output: $stdout.tap { |out| out.sync = true }, commands: Pry::Commands, - prompt_name: Pry::Prompt::DEFAULT_NAME, + prompt_name: 'pry', prompt: Pry::Prompt[:default], - prompt_safe_contexts: Pry::Prompt::SAFE_CONTEXTS, + 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: Pry.lazy do - defaults.output.puts( + exception_whitelist: MemoizedValue.new do + output.puts( '[warning] Pry.config.exception_whitelist is deprecated, ' \ 'please use Pry.config.unrescued_exceptions instead.' ) - [::SystemExit, ::SignalException, Pry::TooSafeException] + unrescued_exceptions end, - # The default hooks - display messages when beginning and ending Pry - # sessions. hooks: Pry::Hooks.default, pager: true, system: Pry::SystemCommandHandler.method(:default), @@ -42,11 +185,11 @@ class Pry should_load_local_rc: true, should_trap_interrupts: Pry::Helpers::Platform.jruby?, disable_auto_reload: false, - command_prefix: "", + command_prefix: '', auto_indent: Pry::Helpers::BaseHelpers.use_ansi_codes?, correct_indent: true, collision_warning: false, - output_prefix: "=> ", + output_prefix: '=> ', requires: [], should_load_requires: true, should_load_plugins: true, @@ -54,29 +197,72 @@ class Pry control_d_handler: Pry::ControlDHandler.method(:default), memory_size: 100, extra_sticky_locals: {}, - command_completions: proc { defaults.commands.keys }, - file_completions: proc { Dir["."] }, - ls: Pry::Config.from_hash(Pry::Command::Ls::DEFAULT_OPTIONS), + command_completions: proc { commands.keys }, + file_completions: proc { Dir['.'] }, + ls: OpenStruct.new(Pry::Command::Ls::DEFAULT_OPTIONS), completer: Pry::InputCompleter, - history: { - should_save: true, - should_load: true, - file: Pry::History.default_file - }, - exec_string: "" + 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 self.shortcuts - Convenience::SHORTCUTS + def [](attr) + @custom_attrs[attr.to_s].call end - # @api private - def self.lazy_readline(defaults) + 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] + else + super + end + end + + 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 + + private + + def lazy_readline require 'readline' ::Readline rescue LoadError - defaults.output.puts( + 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" \ @@ -85,6 +271,5 @@ class Pry ) raise end - private_class_method :lazy_readline end end diff --git a/lib/pry/config/attributable.rb b/lib/pry/config/attributable.rb new file mode 100644 index 00000000..4ff4e7cb --- /dev/null +++ b/lib/pry/config/attributable.rb @@ -0,0 +1,20 @@ +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 ?.?.? + # @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 diff --git a/lib/pry/config/behavior.rb b/lib/pry/config/behavior.rb deleted file mode 100644 index 1fb51f99..00000000 --- a/lib/pry/config/behavior.rb +++ /dev/null @@ -1,377 +0,0 @@ -class Pry - class Config < Pry::BasicObject - # - # {Pry::Config::Behavior} is a module who can be included by classes who - # wish to behave similar to an OpenStruct object: - # - # ```ruby - # class Store - # include Pry::Config::Behavior - # end - # store = Store.from_hash(number: 300) - # store.number # => 300 - # store[:number] # => 300 - # store['number'] # => 300 - # ``` - # - # Classes who include {Pry::Config::Behavior} can be linked to each other - # to provide a default in case a key does not exist locally: - # - # ```ruby - # class Store - # include Pry::Config::Behavior - # end - # store = Store.from_hash({}, Store.from_hash(greeting: 'hello')) - # store.greeting # => 'hello' - # ``` - # - # When an object is read from a default like in the example above, a copy - # of the object is created to avoid a mutation changing its value: - # - # ```ruby - # default = Store.from_hash(greeting: 'hello') - # store = Store.from_hash({}, default) - # store.greeting # => 'hello' - # default.greeting.sub! 'hello', 'goodbye' - # store.greeting # => 'hello' - # ``` - # - module Behavior - ASSIGNMENT = "=".freeze - - NODUP = [ - TrueClass, FalseClass, NilClass, Symbol, Numeric, Module, Proc, Method, - Pry::Prompt, Pry::Config::Lazy - ].freeze - - INSPECT_REGEXP = /#{Regexp.escape "default=#<"}/.freeze - ReservedKeyError = Class.new(RuntimeError) - - # - # The instance methods of this module are available as singleton methods - # on classes who include {Pry::Config::Behavior}. The methods can be used - # to initialize a {Pry::Config::Behavior} object from a Hash object. - # - # @example - # class Store - # include Pry::Config::Behavior - # end - # obj1 = Store.assign(foo: 1, bar: 2) - # obj2 = Store.from_hash(foo: 1, bar: 2) - # [obj1.class, obj2.class] # => [Store, Store] - # - module Builder - # - # @example - # c = Pry::Config.assign(foo: {bar: {baz: 42}}) - # c.class # => Pry::Config - # c.foo.class # => Hash - # - # @param - # (see #from_hash) - # - # @return [Pry::Config::Behavior] - # An instance of an object that has included Pry::Config::Behavior. - # `attributes` is not visited using recursion. - # - def assign(attributes, default = nil) - new(default).tap do |behavior| - behavior.merge!(attributes) - end - end - - # - # @example - # c = Pry::Config.from_hash(foo: {bar: {baz: 42}}) - # c.foo.bar.class # => Pry::Config - # c.foo.bar.baz # => 42 - # - # @param [Hash] attributes - # - # @param [Pry::Config::Behavior, nil] default - # A default, or nil for none. - # - # @return [Pry::Config::Behavior] - # An instance of an object that has included Pry::Config::Behavior. - # `attributes` is visited using recursion. - # - def from_hash(attributes, default = nil) - new(default).tap do |config| - attributes.each do |key, value| - config[key] = if value.is_a?(Hash) - from_hash(value) - elsif value.is_a?(Array) - value.map { |v| v.is_a?(Hash) ? from_hash(v) : v } - else - value - end - end - end - end - end - - def self.included(klass) - klass.extend(Builder) - end - - # - # @example - # class Store - # include Pry::Config::Behavior - # end - # c = Store.new(Pry.config) - # c.input # => Readline - # - # @param [Pry::Config::Behavior, nil] default - # A default to query when a key is not found in self, or nil for none. - # - # - def initialize(default = Pry.config) - @default = default - @lookup = {} - @reserved_keys = methods.map(&:to_s).freeze - end - - # - # @return [Pry::Config::Behavior, nil] - # The object queried when a key is not found in self. - # - def default - @default - end - - # - # @param [#to_s] key - # - # @return [Object, BasicObject] - # An object - # - def [](key) - key = key.to_s - obj = key?(key) ? @lookup[key] : (@default && @default[key]) - obj.is_a?(Pry::Config::Lazy) ? obj.call : obj - end - - # - # Assigns a key/value pair. - # - # @param [#to_s] key - # - # @param [Object, BasicObject] value - # - # @raise [Pry::Config::ReservedKeyError] - # When `key` is a reserved key. - # - def []=(key, value) - key = key.to_s - if @reserved_keys.include?(key) - raise ReservedKeyError, - "It is not possible to use '#{key}' as a key name, please " \ - "choose a different key name." - end - - __push(key, value) - end - - # - # Removes `key` from self and allows the next lookup for `key` to - # traverse back to {#default}. - # - # @example - # pry_instance.config.prompt_name = 'foo' - # pry_instance.config.forget(:prompt_name) - # pry_instance.config.prompt_name # => 'pry' - # - # @param [#to_s] key - # - # @return [void] - # - def forget(key) - key = key.to_s - __remove(key) - default.forget(key) if default && default != last_default - end - - # - # @example - # c = Pry::Config.from_hash(foo: 1) - # c.merge!(bar: 2) - # c.merge!(Pry::Config.from_hash(baz: 3)) - # - # @param [Hash, #to_h, #to_hash] other - # An object to merge into self. - # - # @return [void] - # - def merge!(other) - other = __try_convert_to_hash(other) - raise TypeError, "unable to convert argument into a Hash" unless other - - other.each do |key, value| - self[key] = value - end - end - - # - # @example - # Pry::Config.from_hash(foo: 1) == {'foo' => 1} # => true - # Pry::Config.from_hash(foo: 1) == Pry::Config.from_hash(foo: 1) # => true - # - # @param [Hash, #to_h, #to_hash] other - # Compares `other` against self. - # - # @return [Boolean] - # True if self and `other` are considered `eql?`, otherwise false. - # - def ==(other) - return false unless other - - @lookup == __try_convert_to_hash(other) - end - alias eql? == - - # - # @example - # c = Pry::Config.from_hash(foo: 1) - # c.key?(:foo) # => true - # c.key?('foo') # => true - # - # @param [#to_s] key - # - # @return [Boolean] - # True if `key` is stored in self, otherwise false. - # - def key?(key) - key = key.to_s - @lookup.key?(key) - end - - # - # Clears the contents of self. - # - # @return [void] - # - def clear - @lookup.clear - true - end - - # - # @return [Array<String>] - # An array of keys being stored in self. - # - def keys - @lookup.keys - end - - # - # Eagerly loads keys into self directly from {#last_default}. - # - # @example - # [1] pry(main)> pry_instance.config.keys.size - # => 13 - # [2] pry(main)> pry_instance.config.eager_load!; - # [warning] Pry.config.exception_whitelist is deprecated, - # please use Pry.config.unrescued_exceptions instead. - # [3] pry(main)> pry_instance.config.keys.size - # => 40 - # - # @return [Array<String>, nil] - # An array of keys inserted into self, or nil if {#last_default} is nil. - # - def eager_load! - return unless last_default - - last_default.keys.each { |key| self[key] = public_send(key) } - end - - # - # @example - # # pry_instance.config -> Pry.config -> Pry::Config.defaults - # [1] pry(main)> pry_instance.config.last_default - # - # @return [Pry::Config::Behaviour] - # The last linked default, or nil if there is none. - # - def last_default - last = @default - last = last.default while last && last.default - last - end - - # - # @return [Hash] - # A duplicate copy of the Hash used by self. - # - def to_hash - @lookup.dup - end - alias to_h to_hash - - def inspect - key_str = keys.map { |key| "'#{key}'" }.join(",") - "#<#{__clip_inspect(self)} keys=[#{key_str}] default=#{@default.inspect}>" - end - - def pretty_print(queue) - queue.text(inspect[1..-1].gsub(INSPECT_REGEXP, "default=<")) - end - - # rubocop:disable Style/MethodMissingSuper - def method_missing(method_name, *args, &block) - name = method_name.to_s - if name[-1] == ASSIGNMENT - short_name = name[0..-2] - self[short_name] = args[0] - elsif key?(name) - self[name] - elsif @default.respond_to?(method_name) - value = @default.public_send(method_name, *args, &block) - self[name] = __dup(value) - end - end - # rubocop:enable Style/MethodMissingSuper - - def respond_to_missing?(key, include_all = false) - key = key.to_s.chomp(ASSIGNMENT) - key?(key) || @default.respond_to?(key) || super(key, include_all) - end - - private - - def __clip_inspect(obj) - format("#{obj.class}:0x%<id>x", id: obj.object_id) - end - - def __try_convert_to_hash(obj) - if obj.is_a?(Hash) - obj - elsif obj.respond_to?(:to_h) - obj.to_h - elsif obj.respond_to?(:to_hash) - obj.to_hash - end - end - - def __dup(value) - if NODUP.any? { |klass| value.is_a?(klass) } - value - else - value.dup - end - end - - def __push(key, value) - unless singleton_class.method_defined? key - define_singleton_method(key) { self[key] } - define_singleton_method("#{key}=") { |val| @lookup[key] = val } - end - @lookup[key] = value - end - - def __remove(key) - @lookup.delete(key) - end - end - end -end diff --git a/lib/pry/config/convenience.rb b/lib/pry/config/convenience.rb deleted file mode 100644 index 5560b82f..00000000 --- a/lib/pry/config/convenience.rb +++ /dev/null @@ -1,28 +0,0 @@ -class Pry - class Config < Pry::BasicObject - module Convenience - SHORTCUTS = [ - :input, - :output, - :commands, - :print, - :exception_handler, - :hooks, - :color, - :pager, - :editor, - :memory_size, - :extra_sticky_locals - ].freeze - - def config_shortcut(*names) - names.each do |name| - reader = name - setter = "#{name}=" - define_method(reader) { config.public_send(name) } - define_method(setter) { |value| config.public_send(setter, value) } - end - end - end - end -end diff --git a/lib/pry/config/lazy.rb b/lib/pry/config/lazy.rb deleted file mode 100644 index 9072a5e5..00000000 --- a/lib/pry/config/lazy.rb +++ /dev/null @@ -1,27 +0,0 @@ -class Pry - class Config < Pry::BasicObject - # The primary purpose for instances of this class is to be used as a - # configuration value that is computed upon each access, see {Pry.lazy} - # for more examples. - # - # @example - # num = 19 - # pry_instance.config.foo = Pry::Config::Lazy.new(&proc { num += 1 }) - # pry_instance.config.foo # => 20 - # pry_instance.config.foo # => 21 - # pry_instance.config.foo # => 22 - # - # @api private - # @since v0.12.0 - class Lazy - def initialize(&block) - @block = block - end - - # @return [Object] - def call - @block.call - end - end - end -end diff --git a/lib/pry/config/lazy_value.rb b/lib/pry/config/lazy_value.rb new file mode 100644 index 00000000..8c2330ac --- /dev/null +++ b/lib/pry/config/lazy_value.rb @@ -0,0 +1,27 @@ +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 ?.?.? + # @see Pry::Config::MemoizedValue + class LazyValue + def initialize(&block) + @block = block + end + + def call + @block.call + end + end + end +end diff --git a/lib/pry/config/memoized_value.rb b/lib/pry/config/memoized_value.rb new file mode 100644 index 00000000..a8f61d15 --- /dev/null +++ b/lib/pry/config/memoized_value.rb @@ -0,0 +1,28 @@ +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 ?.?.? + # @see Pry::Config::LazyValue + class MemoizedValue + def initialize(&block) + @block = block + @call = nil + end + + def call + @call ||= @block.call + end + end + end +end diff --git a/lib/pry/config/value.rb b/lib/pry/config/value.rb new file mode 100644 index 00000000..3afec386 --- /dev/null +++ b/lib/pry/config/value.rb @@ -0,0 +1,22 @@ +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 ?.?.? + # @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 diff --git a/lib/pry/history.rb b/lib/pry/history.rb index ad1c590d..17451bd1 100644 --- a/lib/pry/history.rb +++ b/lib/pry/history.rb @@ -55,7 +55,7 @@ class Pry return line if line == last_line @history << line - @saver.call(line) if !should_ignore?(line) && Pry.config.history.should_save + @saver.call(line) if !should_ignore?(line) && Pry.config.history_save line end @@ -95,17 +95,17 @@ class Pry private # Check if the line match any option in the histignore - # [Pry.config.history.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.histignore + 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.history.config.file`. + # The default loader. Yields lines from `Pry.config.history_file`. def read_from_file path = history_file_path @@ -114,7 +114,7 @@ class Pry warn "Unable to read history file: #{error.message}" end - # The default saver. Appends the given line to `Pry.history.config.file`. + # 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 @@ -137,7 +137,7 @@ class Pry end def history_file_path - File.expand_path(@file_path || Pry.config.history.file) + File.expand_path(@file_path || Pry.config.history_file) end def invalid_readline_line?(line) diff --git a/lib/pry/prompt.rb b/lib/pry/prompt.rb index 1792f59f..906a5f01 100644 --- a/lib/pry/prompt.rb +++ b/lib/pry/prompt.rb @@ -34,13 +34,6 @@ class Pry # @since v0.11.0 # @api public class Prompt - # @return [String] - DEFAULT_NAME = 'pry'.freeze - - # @return [Array<Object>] the list of objects that are known to have a - # 1-line #inspect output suitable for prompt - SAFE_CONTEXTS = [String, Numeric, Symbol, nil, true, false].freeze - # 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 = {} diff --git a/lib/pry/pry_class.rb b/lib/pry/pry_class.rb index a58e8bab..c00c6ae1 100644 --- a/lib/pry/pry_class.rb +++ b/lib/pry/pry_class.rb @@ -30,27 +30,13 @@ class Pry def_delegators :@plugin_manager, :plugins, :load_plugins, :locate_plugins - extend Pry::Config::Convenience - config_shortcut(*Pry::Config.shortcuts) - - def prompt=(value) - config.prompt = value - end - - def prompt - config.prompt - end - - def history - return @history if @history - - @history = - if defined?(Pry.config.input::HISTORY) - History.new(history: Pry.config.input::HISTORY) - else - History.new - end - end + 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 @@ -162,7 +148,7 @@ you can add "Pry.config.windows_console_warning = false" to your pryrc. @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.should_load + 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 @@ -342,7 +328,7 @@ Readline version #{Readline::VERSION} detected - will not auto_resize! correctly @initial_session = true @session_finalized = nil - self.config = Pry::Config.new Pry::Config.defaults + self.config = Pry::Config.new self.cli = false self.current_line = 1 self.line_buffer = [""] @@ -401,24 +387,6 @@ Readline version #{Readline::VERSION} detected - will not auto_resize! correctly ensure Thread.current[:pry_critical_section] -= 1 end - - # Wraps a block in a named block called `Pry::Config::Lazy`. This is used for - # dynamic config values, which are calculated every time - # {Pry::Config::Lazy#call} is called. - # - # @example - # # pryrc - # Pry.config.prompt_name = Pry.lazy { rand(100) } - # - # # Session - # [1] 96(main)> - # [2] 19(main)> - # [3] 80(main)> - # - # @return [#call] - def self.lazy(&block) - Pry::Config::Lazy.new(&block) - end end Pry.init diff --git a/lib/pry/pry_instance.rb b/lib/pry/pry_instance.rb index 03840142..a6f87a7f 100644 --- a/lib/pry/pry_instance.rb +++ b/lib/pry/pry_instance.rb @@ -25,6 +25,8 @@ require 'ostruct' # rubocop:disable Metrics/ClassLength class Pry + extend Pry::Forwardable + attr_accessor :binding_stack attr_accessor :custom_completions attr_accessor :eval_string @@ -45,8 +47,13 @@ class Pry attr_reader :config - extend Pry::Config::Convenience - config_shortcut(*Pry::Config.shortcuts) + 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. @@ -75,8 +82,7 @@ class Pry @eval_string = "" @backtrace = options.delete(:backtrace) || caller target = options.delete(:target) - @config = Pry::Config.new - config.merge!(options) + @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) diff --git a/lib/pry/testable.rb b/lib/pry/testable.rb index 1a16ec26..5dbb9269 100644 --- a/lib/pry/testable.rb +++ b/lib/pry/testable.rb @@ -34,20 +34,6 @@ class Pry end end - TEST_DEFAULTS = { - color: false, - pager: false, - should_load_rc: false, - should_load_local_rc: false, - correct_indent: false, - collison_warning: false, - history: { - should_load: false, - should_save: false - } - }.freeze - private_constant :TEST_DEFAULTS - # # Sets various configuration options that make Pry optimal for a test # environment, see source code for complete details. @@ -55,8 +41,17 @@ class Pry # @return [void] # def self.set_testenv_variables - Pry.config = Pry::Config.from_hash TEST_DEFAULTS, Pry::Config.defaults - Pry.config.hooks = Pry::Hooks.new + Pry.config = Pry::Config.new.merge( + color: false, + pager: false, + should_load_rc: false, + should_load_local_rc: false, + correct_indent: false, + collision_warning: false, + history_save: false, + history_load: false, + hooks: Pry::Hooks.new + ) end # @@ -65,7 +60,7 @@ class Pry # @return [void] # def self.unset_testenv_variables - Pry.config = Pry::Config.from_hash({}, Pry::Config.defaults) + Pry.config = Pry::Config.new end end end diff --git a/spec/command_spec.rb b/spec/command_spec.rb index b01cff07..56ee27b3 100644 --- a/spec/command_spec.rb +++ b/spec/command_spec.rb @@ -480,7 +480,7 @@ RSpec.describe Pry::Command do subject { Class.new(described_class).new(pry_instance: Pry.new) } - it "returns a state hash" do + it "returns a state object" do expect(subject.state).to be_an(OpenStruct) end diff --git a/spec/commands/edit_spec.rb b/spec/commands/edit_spec.rb index 114906f6..cc741053 100644 --- a/spec/commands/edit_spec.rb +++ b/spec/commands/edit_spec.rb @@ -195,7 +195,7 @@ describe "edit" do end it "should reload the file" do - Pry.config.editor = lambda { |file, _line| + @t.pry.config.editor = lambda { |file, _line| File.open(file, 'w') { |f| f << "FOO = 'BAR'" } nil } @@ -244,7 +244,7 @@ describe "edit" do describe "with --patch" do # Original source code must be untouched. it "should apply changes only in memory (monkey patching)" do - Pry.config.editor = lambda { |file, _line| + @t.pry.config.editor = lambda { |file, _line| File.open(file, 'w') { |f| f << "FOO3 = 'PIYO'" } @patched_def = File.open(file, 'r').read nil @@ -265,7 +265,7 @@ describe "edit" do describe "with --ex NUM" do before do - Pry.config.editor = proc do |file, line| + @t.pry.config.editor = proc do |file, line| @__ex_file__ = file @__ex_line__ = line nil @@ -341,7 +341,7 @@ describe "edit" do end it "should evaluate the expression" do - Pry.config.editor = lambda { |file, _line| + @t.pry.config.editor = lambda { |file, _line| File.open(file, 'w') { |f| f << "'FOO'\n" } nil } @@ -350,7 +350,7 @@ describe "edit" do end it "should ignore -n for tempfiles" do - Pry.config.editor = lambda { |file, _line| + @t.pry.config.editor = lambda { |file, _line| File.open(file, 'w') { |f| f << "'FOO'\n" } nil } @@ -359,7 +359,7 @@ describe "edit" do end it "should not evaluate a file with -n" do - Pry.config.editor = lambda { |file, _line| + @t.pry.config.editor = lambda { |file, _line| File.open(file, 'w') { |f| f << "'FOO'\n" } nil } @@ -374,7 +374,7 @@ describe "edit" do it "should write the evaluated command to history" do quote = 'history repeats itself, first as tradegy...' - Pry.config.editor = lambda { |file, _line| + @t.pry.config.editor = lambda { |file, _line| File.open(file, 'w') do |f| f << quote end diff --git a/spec/commands/hist_spec.rb b/spec/commands/hist_spec.rb index 29315cba..558b4f99 100644 --- a/spec/commands/hist_spec.rb +++ b/spec/commands/hist_spec.rb @@ -176,13 +176,13 @@ describe "hist" do describe "sessions" do before do - @old_file = Pry.config.history.file - Pry.config.history.file = File.expand_path('spec/fixtures/pry_history') + @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 + Pry.config.history_file = @old_file end it "displays history only for current session" do @@ -202,7 +202,7 @@ describe "hist" do end it "should not display histignore words in history" do - Pry.config.history.histignore = [ + Pry.config.history_ignorelist = [ "well", "hello", "beautiful", diff --git a/spec/commands/show_doc_spec.rb b/spec/commands/show_doc_spec.rb index 82e6b208..e0bba4da 100644 --- a/spec/commands/show_doc_spec.rb +++ b/spec/commands/show_doc_spec.rb @@ -178,14 +178,10 @@ describe "show-doc" do def decolumnize(output); end end - begin - t = pry_tester(binding) - Pry.config.color = true - expect(t.eval("show-doc _c#decolumnize")).to match(/ls -l \$HOME/) - expect(t.eval("show-doc _c#decolumnize")).not_to match(/`ls -l \$HOME`/) - ensure - Pry.config.color = false - end + t = pry_tester(binding) + t.pry.config.color = true + expect(t.eval("show-doc _c#decolumnize")).to match(/ls -l \$HOME/) + expect(t.eval("show-doc _c#decolumnize")).not_to match(/`ls -l \$HOME`/) end end diff --git a/spec/config/attributable_spec.rb b/spec/config/attributable_spec.rb new file mode 100644 index 00000000..c0c2b167 --- /dev/null +++ b/spec/config/attributable_spec.rb @@ -0,0 +1,27 @@ +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 diff --git a/spec/config/behavior_spec.rb b/spec/config/behavior_spec.rb deleted file mode 100644 index 9a789a16..00000000 --- a/spec/config/behavior_spec.rb +++ /dev/null @@ -1,21 +0,0 @@ -RSpec.describe Pry::Config::Behavior do - let(:behavior) do - Class.new do - include Pry::Config::Behavior - end - end - - describe "#last_default" do - it "returns the last default" do - last = behavior.from_hash({}, nil) - middle = behavior.from_hash({}, last) - expect(behavior.from_hash({}, middle).last_default).to be(last) - end - end - - describe "#eager_load!" do - it "returns nil when the default is nil" do - expect(behavior.from_hash({}, nil).eager_load!).to be(nil) - end - end -end diff --git a/spec/config/lazy_value_spec.rb b/spec/config/lazy_value_spec.rb new file mode 100644 index 00000000..dc2aaa1e --- /dev/null +++ b/spec/config/lazy_value_spec.rb @@ -0,0 +1,9 @@ +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 diff --git a/spec/config/memoized_value_spec.rb b/spec/config/memoized_value_spec.rb new file mode 100644 index 00000000..1601bff2 --- /dev/null +++ b/spec/config/memoized_value_spec.rb @@ -0,0 +1,9 @@ +RSpec.describe Pry::Config::MemoizedValue do + describe "#call" do + subject { described_class.new { rand } } + + it "memoizes the result of call" do + expect(subject.call).to eq(subject.call) + end + end +end diff --git a/spec/config/value_spec.rb b/spec/config/value_spec.rb new file mode 100644 index 00000000..efb8eca4 --- /dev/null +++ b/spec/config/value_spec.rb @@ -0,0 +1,37 @@ +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 diff --git a/spec/config_spec.rb b/spec/config_spec.rb index a4e09613..1cd9cdb2 100644 --- a/spec/config_spec.rb +++ b/spec/config_spec.rb @@ -1,289 +1,134 @@ RSpec.describe Pry::Config do - describe ".from_hash" do - it "returns an object without a default" do - local = described_class.from_hash({}) - expect(local.default).to eq(nil) - end - - it "returns an object with a default" do - default = described_class.new(nil) - local = described_class.from_hash({}, default) - expect(local.default).to eq(local) - end + 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 be_a(Method) } + 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) } - it "recursively walks a Hash" do - h = { 'foo1' => { 'foo2' => { 'foo3' => 'foobar' } } } - default = described_class.from_hash(h) - expect(default.foo1).to be_instance_of(described_class) - expect(default.foo1.foo2).to be_instance_of(described_class) - end + describe "#merge!" do + it "merges given hash with the config instance" do + subject.merge!(output_prefix: '~> ', exec_string: '!') - it "recursively walks an Array" do - c = described_class.from_hash(ary: [{ number: 2 }, Object]) - expect(c.ary[0].number).to eq(2) - expect(c.ary[1]).to eq(Object) + expect(subject.output_prefix).to eq('~> ') + expect(subject.exec_string).to eq('!') end - end - describe "bug #1552" do - specify( - "a local key has precendence over its default when the stored value is false" - ) do - local = described_class.from_hash({}, described_class.from_hash('color' => true)) - local.color = false - expect(local.color).to eq(false) + it "returns self" do + config = subject.merge!(output_prefix: '~> ') + expect(subject).to eql(config) end - end - describe "bug #1277" do - specify "a local key has precendence over an inherited method of the same name" do - local = described_class.from_hash(output: 'foobar') - local.extend( - Module.new do - def output - 'broken' - end - end - ) - expect(local.output).to eq('foobar') - end - 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) - describe "reserved keys" do - it "raises ReservedKeyError on assignment of a reserved key" do - local = described_class.new - local.instance_variable_get(:@reserved_keys).each do |key| - expect { local[key] = 1 }.to raise_error(described_class::ReservedKeyError) + expect(subject.new_option).to eq(1) + expect(subject.other_option).to eq(2) end end end - describe "traversal to parent" do - it "traverses back to the parent when a local key is not found" do - local = described_class.new described_class.from_hash(foo: 1) - expect(local.foo).to eq(1) + 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 "stores a local key and prevents traversal to the parent" do - local = described_class.new described_class.from_hash(foo: 1) - local.foo = 2 - expect(local.foo).to eq(2) - end + it "doesn't mutate the original config" do + subject.merge(new_option: 1, other_option: 2) - it "traverses through a chain of parents" do - root = described_class.from_hash(foo: 21) - local1 = described_class.new(root) - local2 = described_class.new(local1) - local3 = described_class.new(local2) - expect(local3.foo).to eq(21) - end - - it "stores a local copy of the parents hooks upon accessing them" do - parent = described_class.from_hash(hooks: "parent_hooks") - local = described_class.new parent - local.hooks.gsub! 'parent', 'local' - expect(local.hooks).to eq 'local_hooks' - expect(parent.hooks).to eq('parent_hooks') + expect(subject).not_to respond_to(:new_option) + expect(subject).not_to respond_to(:other_option) end end - describe "#respond_to_missing?" do - before do - @config = described_class.new(nil) + 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 - it "returns a Method object for a dynamic key" do - @config["key"] = 1 - method_obj = @config.method(:key) - expect(method_obj.name).to eq :key - expect(method_obj.call).to eq(1) + context "when invoked method is not an option" do + it "raises NoMethodError" do + expect { subject.foo }.to raise_error(NoMethodError) + end end - it "returns a Method object for a setter on a parent" do - config = described_class.from_hash({}, described_class.from_hash(foo: 1)) - expect(config.method(:foo=)).to be_an_instance_of(Method) + 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 - before do - @config = described_class.new(nil) - end - - it "returns true for a local key" do - @config.zzfoo = 1 - expect(@config.respond_to?(:zzfoo)).to eq(true) - end - - it "returns false for an unknown key" do - expect(@config.respond_to?(:blahblah)).to eq(false) - end - end - - describe "#default" do - it "returns nil" do - local = described_class.new(nil) - expect(local.default).to eq(nil) - end - - it "returns the default" do - default = described_class.new(nil) - local = described_class.new(default) - expect(local.default).to eq(default) - end - end - - describe "#keys" do - it "returns an array of local keys" do - root = described_class.from_hash({ zoo: "boo" }, nil) - local = described_class.from_hash({ foo: "bar" }, root) - expect(local.keys).to eq(["foo"]) - end - end - - describe "#==" do - it "compares equality through the underlying lookup table" do - local1 = described_class.new(nil) - local2 = described_class.new(nil) - local1.foo = "hi" - local2.foo = "hi" - expect(local1).to eq(local2) - end - - it "compares equality against an object who does not implement #to_hash" do - local1 = described_class.new(nil) - expect(local1).not_to eq(Object.new) - end - - it "returns false when compared against nil" do - # rubocop:disable Style/NilComparison - expect(described_class.new(nil) == nil).to eq(false) - # rubocop:enable Style/NilComparison - end - end - - describe '#forget' do - it 'restores a key to its default value' do - last_default = described_class.from_hash(a: 'c') - middle_default = described_class.from_hash({ a: 'b' }, last_default) - c = described_class.from_hash({ a: 'a' }, middle_default) - c.forget(:a) - expect(c.a).to eq('c') - end - end - - describe "#to_hash" do - it "provides a copy of local key & value pairs as a Hash" do - local = described_class.new described_class.from_hash(bar: true) - local.foo = "21" - expect(local.to_hash).to eq("foo" => "21") - end - - it "returns a duplicate of the lookup table" do - local = described_class.new(nil) - local.to_hash["foo"] = 42 - expect(local.foo).not_to eq(42) - end - end - - describe "#merge!" do - before do - @config = described_class.new(nil) - end - - it "merges an object who returns a Hash through #to_hash" do - obj = Class.new do - def to_hash - { epoch: 1 } - end - end.new - @config.merge!(obj) - expect(@config.epoch).to eq(1) - end - - it "merges an object who returns a Hash through #to_h" do - obj = Class.new do - def to_h - { epoch: 2 } - end - end.new - @config.merge!(obj) - expect(@config.epoch).to eq(2) - end - - it "merges a Hash" do - @config[:epoch] = 420 - expect(@config.epoch).to eq(420) - end - - it "raises a TypeError for objects who can't become a Hash" do - expect { @config.merge!(Object.new) }.to raise_error TypeError - end - end - - describe "#clear" do - before do - @local = described_class.new(nil) + context "when checking an undefined option" do + it "returns false" do + expect(subject.respond_to?(:foo)).to be(false) + end end - it "returns true" do - expect(@local.clear).to eq(true) - end + context "when checking a defined option" do + before { subject.foo = 1 } - it "clears local assignments" do - @local.foo = 1 - @local.clear - expect(@local.to_hash).to eq({}) - end - end + it "returns true for the reader" do + expect(subject.respond_to?(:foo)).to be(true) + end - describe "#[]=" do - it "stores keys as strings" do - local = described_class.from_hash({}) - local[:zoo] = "hello" - expect(local.to_hash).to eq("zoo" => "hello") + it "returns true for the writer" do + expect(subject.respond_to?(:foo=)).to be(true) + end end end describe "#[]" do - it "traverses back to a default" do - default = described_class.from_hash(k: 1) - local = described_class.new(default) - expect(local['k']).to eq(1) - end - - it "traverses back to a default (2 deep)" do - default1 = described_class.from_hash(k: 1) - default2 = described_class.from_hash({}, default1) - local = described_class.new(default2) - expect(local['k']).to eq(1) - end - - it "traverses back to a default that doesn't exist, and returns nil" do - local = described_class.from_hash({}, nil) - expect(local['output']).to eq(nil) + it "reads the config value" do + expect_any_instance_of(Pry::Config::Value).to receive(:call) + subject[:foo] = 1 + subject[:foo] end - context "when returning a Pry::Config::Lazy object" do - it "invokes #call on it" do - c = described_class.from_hash foo: Pry.lazy { 10 } - expect(c['foo']).to eq(10) - end - - it "invokes #call upon each access" do - c = described_class.from_hash foo: Pry.lazy { 'foo' } - expect(c['foo']).to_not equal(c['foo']) - end - end - end - - describe "#eager_load!" do - it "eagerly loads keys from the last default into self" do - last_default = described_class.from_hash(foo: 1, bar: 2, baz: 3) - c = described_class.from_hash({}, last_default) - expect(c.keys.size).to eq(0) - c.eager_load! - expect(c.keys.size).to eq(3) + it "returns the config value" do + subject[:foo] = 1 + expect(subject[:foo]).to eq(1) end end end diff --git a/spec/history_spec.rb b/spec/history_spec.rb index f69fea0c..0e3f4b6e 100644 --- a/spec/history_spec.rb +++ b/spec/history_spec.rb @@ -68,15 +68,15 @@ RSpec.describe Pry::History do describe "#clear" do before do - @old_file = Pry.config.history.file + @old_file = Pry.config.history_file @hist_file_path = File.expand_path('spec/fixtures/pry_history') - Pry.config.history.file = @hist_file_path + Pry.config.history_file = @hist_file_path Pry.history.clear Pry.load_history end after do - Pry.config.history.file = @old_file + Pry.config.history_file = @old_file end it "clears this session's history" do @@ -130,12 +130,12 @@ RSpec.describe Pry::History do before do @histfile = Tempfile.new(%w[pryhistory txt]) @history = Pry::History.new(file_path: @histfile.path) - Pry.config.history.should_save = true + Pry.config.history_save = true end after do @histfile.close(true) - Pry.config.history.should_save = false + Pry.config.history_save = false end it "saves lines to a file as they are written" do @@ -152,7 +152,7 @@ RSpec.describe Pry::History do end it "should not write histignore words to the history file" do - Pry.config.history.histignore = ["ls", /hist*/, 'exit'] + Pry.config.history_ignorelist = ["ls", /hist*/, 'exit'] @history.push "ls" @history.push "hist" @history.push "kakaroto" @@ -164,8 +164,8 @@ RSpec.describe Pry::History do end describe "expanding the history file path" do - before { Pry.config.history.should_save = true } - after { Pry.config.history.should_save = false } + 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 @@ -193,7 +193,7 @@ RSpec.describe Pry::History do end it "handles #{error_class} failure to write history" do - Pry.config.history.should_save = true + 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 diff --git a/spec/prompt_spec.rb b/spec/prompt_spec.rb index 9e57045f..58616cd5 100644 --- a/spec/prompt_spec.rb +++ b/spec/prompt_spec.rb @@ -90,7 +90,7 @@ describe Pry::Prompt do it "computes prompt name dynamically" do proc = described_class[:default].wait_proc - pry.config.prompt_name = Pry.lazy { enum.next } + pry.config.prompt_name = Pry::Config::LazyValue.new { enum.next } expect(proc.call(Object.new, 1, pry, '>')).to eq('[1] a(#<Object>):1> ') expect(proc.call(Object.new, 1, pry, '>')).to eq('[1] b(#<Object>):1> ') end diff --git a/spec/pry_defaults_spec.rb b/spec/pry_defaults_spec.rb index 133d9b02..24fa54c5 100644 --- a/spec/pry_defaults_spec.rb +++ b/spec/pry_defaults_spec.rb @@ -373,7 +373,7 @@ describe "test Pry defaults" do binding, input: InputTester.new("1", "exit-all"), output: @str_output, - hooks: Pry::Config.defaults.hooks + hooks: Pry::Config.new.hooks ) expect(@str_output.string).to match(/[w]hereami by default/) @@ -384,7 +384,7 @@ describe "test Pry defaults" do input: InputTester.new('exit-all'), output: @str_output, quiet: true, - hooks: Pry::Config.defaults.hooks + hooks: Pry::Config.new.hooks ) expect(@str_output.string).to eq "" diff --git a/spec/pry_output_spec.rb b/spec/pry_output_spec.rb index 2facc4f8..8f905d02 100644 --- a/spec/pry_output_spec.rb +++ b/spec/pry_output_spec.rb @@ -1,6 +1,6 @@ describe Pry do describe "output failsafe" do - after { Pry.config.print = Pry::Config.defaults.print } + after { Pry.config.print = Pry::Config.new.print } it "should catch serialization exceptions" do Pry.config.print = proc { raise "catch-22" } diff --git a/spec/pry_repl_spec.rb b/spec/pry_repl_spec.rb index 5430a0a7..f407240a 100644 --- a/spec/pry_repl_spec.rb +++ b/spec/pry_repl_spec.rb @@ -45,7 +45,7 @@ describe Pry::REPL do it "shouldn't break if we start a nested instance" do ReplTester.start do - input 'Pry.start(10, pry_instance.config)' + input 'Pry.start(10)' output '' prompt(/10.*> $/) |