summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md5
-rw-r--r--lib/pry.rb7
-rw-r--r--lib/pry/commands/ls/formatter.rb2
-rw-r--r--lib/pry/config.rb255
-rw-r--r--lib/pry/config/attributable.rb20
-rw-r--r--lib/pry/config/behavior.rb377
-rw-r--r--lib/pry/config/convenience.rb28
-rw-r--r--lib/pry/config/lazy.rb27
-rw-r--r--lib/pry/config/lazy_value.rb27
-rw-r--r--lib/pry/config/memoized_value.rb28
-rw-r--r--lib/pry/config/value.rb22
-rw-r--r--lib/pry/history.rb12
-rw-r--r--lib/pry/prompt.rb7
-rw-r--r--lib/pry/pry_class.rb50
-rw-r--r--lib/pry/pry_instance.rb14
-rw-r--r--lib/pry/testable.rb29
-rw-r--r--spec/command_spec.rb2
-rw-r--r--spec/commands/edit_spec.rb14
-rw-r--r--spec/commands/hist_spec.rb8
-rw-r--r--spec/commands/show_doc_spec.rb12
-rw-r--r--spec/config/attributable_spec.rb27
-rw-r--r--spec/config/behavior_spec.rb21
-rw-r--r--spec/config/lazy_value_spec.rb9
-rw-r--r--spec/config/memoized_value_spec.rb9
-rw-r--r--spec/config/value_spec.rb37
-rw-r--r--spec/config_spec.rb345
-rw-r--r--spec/history_spec.rb18
-rw-r--r--spec/prompt_spec.rb2
-rw-r--r--spec/pry_defaults_spec.rb4
-rw-r--r--spec/pry_output_spec.rb2
-rw-r--r--spec/pry_repl_spec.rb2
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
diff --git a/lib/pry.rb b/lib/pry.rb
index fbbaa2ab..623562c1 100644
--- a/lib/pry.rb
+++ b/lib/pry.rb
@@ -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.*> $/)