summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKyrylo Silin <silin@kyrylo.org>2019-03-25 02:40:05 +0200
committerKyrylo Silin <silin@kyrylo.org>2019-05-02 00:10:37 +0300
commite5556a2be8627ec3fe594c738f10422d5a1f5d43 (patch)
treea1758a1f39915a4eb076793a36d485cb7145d638
parent03afca9eafe4f2981edad1cfe4346a643fb64d72 (diff)
downloadpry-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).
-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.*> $/)