2014-03-14 00:31:24 -04:00
|
|
|
require_relative 'helper'
|
2014-01-21 10:32:03 -05:00
|
|
|
describe Pry::Config do
|
2014-01-27 04:06:06 -05:00
|
|
|
describe "reserved keys" do
|
|
|
|
it "raises an ArgumentError on assignment of a reserved key" do
|
2014-03-14 04:13:55 -04:00
|
|
|
local = Pry::Config.new
|
2014-01-29 19:41:56 -05:00
|
|
|
Pry::Config::RESERVED_KEYS.each do |key|
|
Switch test suite to RSpec
Removes Bacon and Mocha
Reasoning explained in this comment: https://github.com/pry/pry/issues/277#issuecomment-51708712
Mostly this went smoothly. There were a few errors that I fixed along
the way, e.g. tests that were failing but for various reasons still
passed. Should have documented them, but didn't think about it until
very near the end. But generaly, I remember 2 reasons this would happen:
`lambda { raise "omg" }.should.raise(RuntimeError, /not-omg/)` will pass
because the second argument is ignored by Bacon. And `1.should == 2`
will return false instead of raising an error when it is not in an it
block (e.g. if stuck in a describe block, that would just return false)
The only one that I felt unsure about was spec/helpers/table_spec.rb
`Pry::Helpers.tablify_or_one_line('head', %w(ing)).should == 'head: ing'`
This is wrong, but was not failing because it was in a describe block
instead of an it block. In reality, it returns `"head: ing\n"`,
I updated the test to reflect this, though I don't know for sure
this is the right thing to do
This will fail on master until https://github.com/pry/pry/pull/1281 is merged.
This makes https://github.com/pry/pry/pull/1278 unnecessary.
2014-08-10 18:26:47 -04:00
|
|
|
expect { local[key] = 1 }.to raise_error ArgumentError
|
2014-01-29 19:41:56 -05:00
|
|
|
end
|
2014-01-27 04:06:06 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-01-30 20:39:23 -05:00
|
|
|
describe "traversal to parent" do
|
|
|
|
it "traverses back to the parent when a local key is not found" do
|
2014-01-30 20:43:13 -05:00
|
|
|
local = Pry::Config.new Pry::Config.from_hash(foo: 1)
|
2015-03-10 16:49:29 -04:00
|
|
|
expect(local.foo).to eq(1)
|
2014-01-21 10:32:03 -05:00
|
|
|
end
|
|
|
|
|
2014-01-30 20:39:23 -05:00
|
|
|
it "stores a local key and prevents traversal to the parent" do
|
2014-01-30 20:43:13 -05:00
|
|
|
local = Pry::Config.new Pry::Config.from_hash(foo: 1)
|
|
|
|
local.foo = 2
|
2015-03-10 16:49:29 -04:00
|
|
|
expect(local.foo).to eq(2)
|
2014-01-21 10:32:03 -05:00
|
|
|
end
|
|
|
|
|
2014-01-31 08:12:04 -05:00
|
|
|
it "traverses through a chain of parents" do
|
|
|
|
root = Pry::Config.from_hash({foo: 21})
|
|
|
|
local1 = Pry::Config.new(root)
|
|
|
|
local2 = Pry::Config.new(local1)
|
|
|
|
local3 = Pry::Config.new(local2)
|
2015-03-10 16:49:29 -04:00
|
|
|
expect(local3.foo).to eq(21)
|
2014-01-31 08:12:04 -05:00
|
|
|
end
|
Fix Pry.config.hooks # => parent's hooks
PROBLEM DESCRIPTION
===================
The child caches a dup of the parent's hooks,
so it does the correct thing after being accessed the first time.
But the first time it is called, it doesn't return its cached duped value,
it returns the parent's value. This causes 2 bugs:
1. The first hook declared will get "lost", because it is declared
to the parent hooks, not the child's hooks. And after that first
accessing, only the child's hooks will be used
2. The parent's hooks (which are probably the ones from
[the default](https://github.com/pry/pry/blob/0ff6fa61d4197feace48b422b5e3d3fb5948cbdd/lib/pry/config/default.rb#L35-37))
are going to get hooks declared on them that shouldn't be
(well, presumably shouldn't be, given the explicit check for
this case and duping to avoid it).
So if another config object was initialized with the same parent,
it would wind up with hooks that it didn't want/expect.
I assume all the other keys work fine because they are value objects,
so if anything wanted to change the value, it would reassign the key
to the config object rather than mutating the returned value.
Mutability *sigh* yallknowwhatimsayin
SOLUTION DESCRIPTION
====================
Return the cached duped child value instead of the parent value.
EXAMPLE
=======
This code from
[the docs](https://github.com/pry/pry/blob/0ff6fa61d4197feace48b422b5e3d3fb5948cbdd/lib/pry/hooks.rb#L8-11)
will not print "hello", because it's being declared to the parent hook.
```ruby
Pry.config.hooks.add_hook(:before_session, :say_hi) { puts "hello" }
```
This code will, b/c it's being declared to the cached, duped child hook.
```ruby
Pry.config.hooks
Pry.config.hooks.add_hook(:before_session, :say_hi) { puts "hello" }
```
ISSUE IMPACT
============
Fixes #1254
Might fix or have something to do with these issues:
* #1244 Totally thought I got it, but then couldn't reproduce again later
and just don't understand how JRuby builds, so might just be a problem
with how I was trying to reproduce it.
* #1171 Maybe, depending on if Eclipse integrates with it using the
hooks, alternatively, could be the same issue as the one above,
they might both still be broken. Or both fixed and I can't figure out
how to prove it. Dunno.
OTHER THOUGHTS
==============
I'm writing Github flavoured markdown,
but I'm not actually sure if it will get rendered as such in the PR.
Esp since git considers leading octothorpes as comments and not headers,
and removes them...
Guess we'll see... bottoms up, friends!
2014-07-20 18:04:51 -04:00
|
|
|
|
|
|
|
it "stores a local copy of the parent's hooks upon accessing them" do
|
|
|
|
parent = Pry::Config.from_hash(hooks: "parent_hooks")
|
|
|
|
local = Pry::Config.new parent
|
|
|
|
local.hooks.gsub! 'parent', 'local'
|
2015-03-10 16:49:29 -04:00
|
|
|
expect(local.hooks).to eq 'local_hooks'
|
|
|
|
expect(parent.hooks).to eq('parent_hooks')
|
Fix Pry.config.hooks # => parent's hooks
PROBLEM DESCRIPTION
===================
The child caches a dup of the parent's hooks,
so it does the correct thing after being accessed the first time.
But the first time it is called, it doesn't return its cached duped value,
it returns the parent's value. This causes 2 bugs:
1. The first hook declared will get "lost", because it is declared
to the parent hooks, not the child's hooks. And after that first
accessing, only the child's hooks will be used
2. The parent's hooks (which are probably the ones from
[the default](https://github.com/pry/pry/blob/0ff6fa61d4197feace48b422b5e3d3fb5948cbdd/lib/pry/config/default.rb#L35-37))
are going to get hooks declared on them that shouldn't be
(well, presumably shouldn't be, given the explicit check for
this case and duping to avoid it).
So if another config object was initialized with the same parent,
it would wind up with hooks that it didn't want/expect.
I assume all the other keys work fine because they are value objects,
so if anything wanted to change the value, it would reassign the key
to the config object rather than mutating the returned value.
Mutability *sigh* yallknowwhatimsayin
SOLUTION DESCRIPTION
====================
Return the cached duped child value instead of the parent value.
EXAMPLE
=======
This code from
[the docs](https://github.com/pry/pry/blob/0ff6fa61d4197feace48b422b5e3d3fb5948cbdd/lib/pry/hooks.rb#L8-11)
will not print "hello", because it's being declared to the parent hook.
```ruby
Pry.config.hooks.add_hook(:before_session, :say_hi) { puts "hello" }
```
This code will, b/c it's being declared to the cached, duped child hook.
```ruby
Pry.config.hooks
Pry.config.hooks.add_hook(:before_session, :say_hi) { puts "hello" }
```
ISSUE IMPACT
============
Fixes #1254
Might fix or have something to do with these issues:
* #1244 Totally thought I got it, but then couldn't reproduce again later
and just don't understand how JRuby builds, so might just be a problem
with how I was trying to reproduce it.
* #1171 Maybe, depending on if Eclipse integrates with it using the
hooks, alternatively, could be the same issue as the one above,
they might both still be broken. Or both fixed and I can't figure out
how to prove it. Dunno.
OTHER THOUGHTS
==============
I'm writing Github flavoured markdown,
but I'm not actually sure if it will get rendered as such in the PR.
Esp since git considers leading octothorpes as comments and not headers,
and removes them...
Guess we'll see... bottoms up, friends!
2014-07-20 18:04:51 -04:00
|
|
|
end
|
2014-01-21 10:32:03 -05:00
|
|
|
end
|
|
|
|
|
2014-12-13 10:50:33 -05:00
|
|
|
describe "collision" do
|
|
|
|
# Testcase GH-1277
|
|
|
|
it "doesn't collide" do
|
|
|
|
local = Pry::Config.from_hash(output: 'foobar')
|
|
|
|
local.extend Module.new { def output(); 'broken'; end }
|
|
|
|
expect(local.output).to eq('foobar')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-02-02 08:52:55 -05:00
|
|
|
describe ".from_hash" do
|
2014-03-14 00:40:09 -04:00
|
|
|
it "returns an object without a default" do
|
2014-02-02 08:52:55 -05:00
|
|
|
local = Pry::Config.from_hash({})
|
2015-03-10 16:49:29 -04:00
|
|
|
expect(local.default).to eq(nil)
|
2014-02-02 08:52:55 -05:00
|
|
|
end
|
|
|
|
|
2014-03-14 00:40:09 -04:00
|
|
|
it "returns an object with a default" do
|
2014-02-02 08:52:55 -05:00
|
|
|
default = Pry::Config.new(nil)
|
|
|
|
local = Pry::Config.from_hash({}, default)
|
2015-03-10 16:49:29 -04:00
|
|
|
expect(local.default).to eq(local)
|
2014-02-02 08:52:55 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2014-03-23 05:16:13 -04:00
|
|
|
describe "#respond_to_missing?" do
|
|
|
|
before do
|
|
|
|
@config = Pry::Config.new(nil)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns a Method object for a dynamic key" do
|
|
|
|
@config["key"] = 1
|
|
|
|
method_obj = @config.method(:key)
|
2015-03-10 16:49:29 -04:00
|
|
|
expect(method_obj.name).to eq :key
|
|
|
|
expect(method_obj.call).to eq(1)
|
2014-03-23 05:16:13 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "#respond_to?" do
|
|
|
|
before do
|
|
|
|
@config = Pry::Config.new(nil)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns true for a local key" do
|
|
|
|
@config.zzfoo = 1
|
2015-03-10 16:49:29 -04:00
|
|
|
expect(@config.respond_to?(:zzfoo)).to eq(true)
|
2014-03-23 05:16:13 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it "returns false for an unknown key" do
|
2015-03-10 16:49:29 -04:00
|
|
|
expect(@config.respond_to?(:blahblah)).to eq(false)
|
2014-03-23 05:16:13 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-03-14 00:40:09 -04:00
|
|
|
describe "#default" do
|
|
|
|
it "returns nil" do
|
|
|
|
local = Pry::Config.new(nil)
|
2015-03-10 16:49:29 -04:00
|
|
|
expect(local.default).to eq(nil)
|
2014-03-14 00:40:09 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it "returns the default" do
|
|
|
|
default = Pry::Config.new(nil)
|
|
|
|
local = Pry::Config.new(default)
|
2015-03-10 16:49:29 -04:00
|
|
|
expect(local.default).to eq(default)
|
2014-03-14 00:40:09 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-02-02 17:23:23 -05:00
|
|
|
describe "#keys" do
|
|
|
|
it "returns an array of local keys" do
|
|
|
|
root = Pry::Config.from_hash({zoo: "boo"}, nil)
|
|
|
|
local = Pry::Config.from_hash({foo: "bar"}, root)
|
2015-03-10 16:49:29 -04:00
|
|
|
expect(local.keys).to eq(["foo"])
|
2014-02-02 17:23:23 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-02-02 08:52:55 -05:00
|
|
|
describe "#==" do
|
|
|
|
it "compares equality through the underlying lookup table" do
|
|
|
|
local1 = Pry::Config.new(nil)
|
|
|
|
local2 = Pry::Config.new(nil)
|
|
|
|
local1.foo = "hi"
|
|
|
|
local2.foo = "hi"
|
2015-03-10 16:49:29 -04:00
|
|
|
expect(local1).to eq(local2)
|
2014-02-02 08:52:55 -05:00
|
|
|
end
|
2014-02-02 09:01:32 -05:00
|
|
|
|
|
|
|
it "compares equality against an object who does not implement #to_hash" do
|
|
|
|
local1 = Pry::Config.new(nil)
|
2015-03-10 16:49:29 -04:00
|
|
|
expect(local1).not_to eq(Object.new)
|
2014-02-02 09:01:32 -05:00
|
|
|
end
|
2014-02-02 08:52:55 -05:00
|
|
|
end
|
|
|
|
|
2014-01-31 08:18:32 -05:00
|
|
|
describe "#forget" do
|
|
|
|
it "forgets a local key" do
|
|
|
|
local = Pry::Config.new Pry::Config.from_hash(foo: 1)
|
|
|
|
local.foo = 2
|
2015-03-10 16:49:29 -04:00
|
|
|
expect(local.foo).to eq 2
|
2014-01-31 08:18:32 -05:00
|
|
|
local.forget(:foo)
|
2015-03-10 16:49:29 -04:00
|
|
|
expect(local.foo).to eq(1)
|
2014-01-31 08:18:32 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "#to_hash" do
|
|
|
|
it "provides a copy of local key & value pairs as a Hash" do
|
|
|
|
local = Pry::Config.new Pry::Config.from_hash(bar: true)
|
|
|
|
local.foo = "21"
|
2015-03-10 16:49:29 -04:00
|
|
|
expect(local.to_hash).to eq({ "foo" => "21" })
|
2014-01-31 08:18:32 -05:00
|
|
|
end
|
2014-02-02 09:34:35 -05:00
|
|
|
|
|
|
|
it "returns a duplicate of the lookup table" do
|
2014-02-02 09:58:10 -05:00
|
|
|
local = Pry::Config.new(nil)
|
2014-02-02 09:34:35 -05:00
|
|
|
local.to_hash.merge!("foo" => 42)
|
2015-03-10 16:49:29 -04:00
|
|
|
expect(local.foo).not_to eq(42)
|
2014-02-02 09:34:35 -05:00
|
|
|
end
|
2014-01-31 08:18:32 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
describe "#merge!" do
|
2014-03-23 03:26:40 -04:00
|
|
|
before do
|
|
|
|
@config = Pry::Config.new(nil)
|
2014-01-31 08:18:32 -05:00
|
|
|
end
|
|
|
|
|
2014-03-23 03:26:40 -04:00
|
|
|
it "merges an object who returns a Hash through #to_hash" do
|
|
|
|
obj = Class.new { def to_hash() {epoch: 1} end }.new
|
|
|
|
@config.merge!(obj)
|
2015-03-10 16:49:29 -04:00
|
|
|
expect(@config.epoch).to eq(1)
|
2014-03-23 03:26:40 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it "merges an object who returns a Hash through #to_h" do
|
|
|
|
obj = Class.new { def to_h() {epoch: 2} end }.new
|
|
|
|
@config.merge!(obj)
|
2015-03-10 16:49:29 -04:00
|
|
|
expect(@config.epoch).to eq(2)
|
2014-03-23 03:26:40 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it "merges a Hash" do
|
|
|
|
@config.merge!(epoch: 420)
|
2015-03-10 16:49:29 -04:00
|
|
|
expect(@config.epoch).to eq(420)
|
2014-03-23 03:26:40 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it "raises a TypeError for objects who can't become a Hash" do
|
Switch test suite to RSpec
Removes Bacon and Mocha
Reasoning explained in this comment: https://github.com/pry/pry/issues/277#issuecomment-51708712
Mostly this went smoothly. There were a few errors that I fixed along
the way, e.g. tests that were failing but for various reasons still
passed. Should have documented them, but didn't think about it until
very near the end. But generaly, I remember 2 reasons this would happen:
`lambda { raise "omg" }.should.raise(RuntimeError, /not-omg/)` will pass
because the second argument is ignored by Bacon. And `1.should == 2`
will return false instead of raising an error when it is not in an it
block (e.g. if stuck in a describe block, that would just return false)
The only one that I felt unsure about was spec/helpers/table_spec.rb
`Pry::Helpers.tablify_or_one_line('head', %w(ing)).should == 'head: ing'`
This is wrong, but was not failing because it was in a describe block
instead of an it block. In reality, it returns `"head: ing\n"`,
I updated the test to reflect this, though I don't know for sure
this is the right thing to do
This will fail on master until https://github.com/pry/pry/pull/1281 is merged.
This makes https://github.com/pry/pry/pull/1278 unnecessary.
2014-08-10 18:26:47 -04:00
|
|
|
expect { @config.merge!(Object.new) }.to raise_error TypeError
|
2014-01-31 08:18:32 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-03-14 22:05:19 -04:00
|
|
|
describe "#clear" do
|
|
|
|
before do
|
|
|
|
@local = Pry::Config.new(nil)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns true" do
|
2015-03-10 16:49:29 -04:00
|
|
|
expect(@local.clear).to eq(true)
|
2014-03-14 22:05:19 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it "clears local assignments" do
|
|
|
|
@local.foo = 1
|
|
|
|
@local.clear
|
2015-03-10 16:49:29 -04:00
|
|
|
expect(@local.to_hash).to eq({})
|
2014-03-14 22:05:19 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it "is aliased as #refresh" do
|
2015-03-10 16:49:29 -04:00
|
|
|
expect(@local.method(:clear)).to eq(@local.method(:refresh))
|
2014-03-14 22:05:19 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-01-30 20:39:23 -05:00
|
|
|
describe "#[]=" do
|
|
|
|
it "stores keys as strings" do
|
|
|
|
local = Pry::Config.from_hash({})
|
|
|
|
local[:zoo] = "hello"
|
2015-03-10 16:49:29 -04:00
|
|
|
expect(local.to_hash).to eq({ "zoo" => "hello" })
|
2014-01-21 10:32:03 -05:00
|
|
|
end
|
|
|
|
end
|
2014-12-13 09:25:03 -05:00
|
|
|
|
|
|
|
describe "#[]" do
|
|
|
|
it "traverses back to a default" do
|
|
|
|
default = Pry::Config.from_hash({k: 1})
|
|
|
|
local = Pry::Config.new(default)
|
|
|
|
expect(local['k']).to eq(1)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "traverses back to a default (2 deep)" do
|
|
|
|
default1 = Pry::Config.from_hash({k: 1})
|
|
|
|
default2 = Pry::Config.from_hash({}, default1)
|
|
|
|
local = Pry::Config.new(default2)
|
|
|
|
expect(local['k']).to eq(1)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "traverses back to a default that doesn't exist, and returns nil" do
|
|
|
|
local = Pry::Config.from_hash({}, nil)
|
|
|
|
expect(local['output']).to eq(nil)
|
|
|
|
end
|
|
|
|
end
|
2014-01-21 10:32:03 -05:00
|
|
|
end
|