fabrique 0.3.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -2
- data/Gemfile +4 -0
- data/README.md +2 -155
- data/fabrique.gemspec +3 -3
- data/features/bean_factory.feature +194 -3
- data/features/step_definitions/bean_factory_steps.rb +41 -3
- data/features/support/fabrique.rb +1 -0
- data/fixtures/local_only/.gitignore +9 -0
- data/fixtures/local_only/Gemfile +4 -0
- data/fixtures/local_only/README.md +36 -0
- data/fixtures/local_only/Rakefile +2 -0
- data/fixtures/local_only/bin/console +14 -0
- data/fixtures/local_only/bin/setup +8 -0
- data/fixtures/local_only/lib/local_only/version.rb +3 -0
- data/fixtures/local_only/lib/local_only.rb +7 -0
- data/fixtures/local_only/local_only.gemspec +31 -0
- data/fixtures/local_only-0.1.0.gem +0 -0
- data/fixtures/sample/.gitignore +9 -0
- data/fixtures/sample/Gemfile +4 -0
- data/fixtures/sample/README.md +36 -0
- data/fixtures/sample/Rakefile +2 -0
- data/fixtures/sample/bin/console +14 -0
- data/fixtures/sample/bin/setup +8 -0
- data/fixtures/sample/lib/sample/version.rb +3 -0
- data/fixtures/sample/lib/sample.rb +9 -0
- data/fixtures/sample/sample.gemspec +23 -0
- data/lib/fabrique/bean_definition.rb +4 -4
- data/lib/fabrique/bean_definition_registry.rb +11 -5
- data/lib/fabrique/bean_factory.rb +34 -13
- data/lib/fabrique/bean_property_reference.rb +9 -1
- data/lib/fabrique/data_bean.rb +62 -0
- data/lib/fabrique/gem_definition.rb +15 -0
- data/lib/fabrique/gem_dependency_error.rb +6 -0
- data/lib/fabrique/gem_loader.rb +25 -0
- data/lib/fabrique/test/fixtures/constructors.rb +12 -1
- data/lib/fabrique/version.rb +1 -1
- data/lib/fabrique.rb +0 -3
- data/spec/fabrique/data_bean_spec.rb +129 -0
- metadata +32 -38
- data/features/plugin_registry.feature +0 -79
- data/features/step_definitions/plugin_registry_steps.rb +0 -207
- data/lib/fabrique/argument_adaptor/keyword.rb +0 -19
- data/lib/fabrique/argument_adaptor/positional.rb +0 -76
- data/lib/fabrique/construction/as_is.rb +0 -16
- data/lib/fabrique/construction/builder_method.rb +0 -21
- data/lib/fabrique/construction/default.rb +0 -17
- data/lib/fabrique/construction/keyword_argument.rb +0 -16
- data/lib/fabrique/construction/positional_argument.rb +0 -40
- data/lib/fabrique/construction/properties_hash.rb +0 -19
- data/lib/fabrique/constructor/identity.rb +0 -10
- data/lib/fabrique/plugin_registry.rb +0 -56
- data/spec/fabrique/argument_adaptor/keyword_spec.rb +0 -50
- data/spec/fabrique/argument_adaptor/positional_spec.rb +0 -166
- data/spec/fabrique/construction/as_is_spec.rb +0 -23
- data/spec/fabrique/construction/builder_method_spec.rb +0 -29
- data/spec/fabrique/construction/default_spec.rb +0 -19
- data/spec/fabrique/construction/positional_argument_spec.rb +0 -61
- data/spec/fabrique/construction/properties_hash_spec.rb +0 -36
- data/spec/fabrique/constructor/identity_spec.rb +0 -4
- data/spec/fabrique/plugin_registry_spec.rb +0 -78
@@ -0,0 +1,36 @@
|
|
1
|
+
# Sample
|
2
|
+
|
3
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/sample`. To experiment with that code, run `bin/console` for an interactive prompt.
|
4
|
+
|
5
|
+
TODO: Delete this and the text above, and describe your gem
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'sample'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install sample
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
TODO: Write usage instructions here
|
26
|
+
|
27
|
+
## Development
|
28
|
+
|
29
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
30
|
+
|
31
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
32
|
+
|
33
|
+
## Contributing
|
34
|
+
|
35
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/sample.
|
36
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "sample"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'sample/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "sample"
|
8
|
+
spec.version = Sample::VERSION
|
9
|
+
spec.authors = ["Sheldon Hearn"]
|
10
|
+
spec.email = ["sheldonh@starjuice.net"]
|
11
|
+
|
12
|
+
spec.summary = %q{Sample gem}
|
13
|
+
spec.description = %q{Sample gem for testing software that uses gems}
|
14
|
+
spec.homepage = "https://github.com/starjuice/fabrique"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.bindir = "exe"
|
18
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.12"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
end
|
@@ -1,12 +1,12 @@
|
|
1
1
|
module Fabrique
|
2
2
|
|
3
3
|
class BeanDefinition
|
4
|
-
attr_reader :constructor_args, :factory_method, :id, :properties, :type
|
4
|
+
attr_reader :constructor_args, :factory_method, :gem, :id, :properties, :type
|
5
5
|
|
6
6
|
def initialize(attrs = {})
|
7
7
|
@id = attrs["id"]
|
8
|
-
|
9
|
-
@
|
8
|
+
@type = attrs["class"]
|
9
|
+
@gem = GemDefinition.new(attrs["gem"]) if attrs["gem"]
|
10
10
|
@constructor_args = attrs["constructor_args"] || []
|
11
11
|
@constructor_args = keywordify(@constructor_args) if @constructor_args.is_a?(Hash)
|
12
12
|
@properties = attrs["properties"] || {}
|
@@ -15,7 +15,7 @@ module Fabrique
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def dependencies
|
18
|
-
(accumulate_dependencies(@constructor_args) + accumulate_dependencies(@properties)).uniq
|
18
|
+
(accumulate_dependencies(@type) + accumulate_dependencies(@constructor_args) + accumulate_dependencies(@properties)).uniq
|
19
19
|
end
|
20
20
|
|
21
21
|
def singleton?
|
@@ -15,12 +15,18 @@ module Fabrique
|
|
15
15
|
@defs.detect { |d| d.id == bean_name }
|
16
16
|
end
|
17
17
|
|
18
|
+
def get_definitions
|
19
|
+
@defs
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_gem_definitions
|
23
|
+
@defs.collect(&:gem).compact
|
24
|
+
end
|
25
|
+
|
18
26
|
def validate!
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
raise CyclicBeanDependencyError.new(e.message.gsub(/topological sort failed/, "cyclic bean dependency error"))
|
23
|
-
end
|
27
|
+
tsort
|
28
|
+
rescue TSort::Cyclic => e
|
29
|
+
raise CyclicBeanDependencyError.new(e.message.gsub(/topological sort failed/, "cyclic bean dependency error"))
|
24
30
|
end
|
25
31
|
|
26
32
|
private
|
@@ -8,6 +8,7 @@ module Fabrique
|
|
8
8
|
def initialize(registry)
|
9
9
|
@registry = registry
|
10
10
|
@registry.validate!
|
11
|
+
@gem_loader = GemLoader.new(@registry.get_gem_definitions)
|
11
12
|
@singletons = {}
|
12
13
|
@semaphore = Mutex.new
|
13
14
|
end
|
@@ -18,6 +19,16 @@ module Fabrique
|
|
18
19
|
end
|
19
20
|
end
|
20
21
|
|
22
|
+
def load_gem_dependencies
|
23
|
+
@gem_loader.load_gems
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_h
|
27
|
+
@semaphore.synchronize do
|
28
|
+
@registry.get_definitions.map { |defn| [defn.id, get_bean_unsynchronized(defn.id)] }.to_h
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
21
32
|
private
|
22
33
|
|
23
34
|
def get_bean_unsynchronized(bean_name)
|
@@ -27,38 +38,47 @@ module Fabrique
|
|
27
38
|
return singleton
|
28
39
|
end
|
29
40
|
|
30
|
-
get_bean_by_definition(defn)
|
31
|
-
|
32
|
-
|
33
|
-
end
|
41
|
+
bean = get_bean_by_definition(defn)
|
42
|
+
if defn.singleton?
|
43
|
+
@singletons[bean_name] = bean
|
34
44
|
end
|
45
|
+
bean
|
35
46
|
end
|
36
47
|
|
37
48
|
def get_bean_by_definition(defn)
|
38
49
|
if defn.factory_method == "itself"
|
39
|
-
|
40
|
-
return defn.type
|
50
|
+
return get_factory(defn)
|
41
51
|
end
|
42
52
|
|
43
53
|
bean = constructor_injection(defn)
|
44
54
|
property_injection(bean, defn)
|
45
55
|
end
|
46
56
|
|
57
|
+
def get_factory(defn)
|
58
|
+
if defn.type.is_a?(BeanReference)
|
59
|
+
get_bean_unsynchronized(defn.type.bean)
|
60
|
+
elsif defn.type.is_a?(Module)
|
61
|
+
defn.type
|
62
|
+
else
|
63
|
+
Module.const_get(defn.type)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
47
67
|
def constructor_injection(defn)
|
48
68
|
args = resolve_bean_references(defn.constructor_args)
|
69
|
+
factory = get_factory(defn)
|
49
70
|
if args.respond_to?(:keys)
|
50
|
-
bean =
|
71
|
+
bean = factory.send(defn.factory_method, args)
|
51
72
|
else
|
52
|
-
bean =
|
73
|
+
bean = factory.send(defn.factory_method, *args)
|
53
74
|
end
|
54
75
|
end
|
55
76
|
|
56
77
|
def property_injection(bean, defn)
|
57
|
-
|
58
|
-
|
59
|
-
b.send("#{k}=", resolve_bean_references(v))
|
60
|
-
end
|
78
|
+
defn.properties.each do |k, v|
|
79
|
+
bean.send("#{k}=", resolve_bean_references(v))
|
61
80
|
end
|
81
|
+
bean
|
62
82
|
end
|
63
83
|
|
64
84
|
def resolve_bean_references(data)
|
@@ -76,11 +96,12 @@ module Fabrique
|
|
76
96
|
elsif data.is_a?(BeanReference)
|
77
97
|
get_bean_unsynchronized(data.bean)
|
78
98
|
elsif data.is_a?(BeanPropertyReference)
|
79
|
-
get_bean_unsynchronized(data.bean)
|
99
|
+
data.resolve(get_bean_unsynchronized(data.bean))
|
80
100
|
else
|
81
101
|
data
|
82
102
|
end
|
83
103
|
end
|
104
|
+
|
84
105
|
end
|
85
106
|
|
86
107
|
end
|
@@ -4,8 +4,16 @@ module Fabrique
|
|
4
4
|
attr :bean, :property
|
5
5
|
|
6
6
|
def initialize(bean_property)
|
7
|
-
|
7
|
+
chain = bean_property.split('.')
|
8
|
+
@bean = chain.first
|
9
|
+
@property_chain = chain.drop(1)
|
10
|
+
@property = @property_chain.join('.')
|
8
11
|
end
|
12
|
+
|
13
|
+
def resolve(bean)
|
14
|
+
@property_chain.inject(bean) { |acc, property| acc.send(property) }
|
15
|
+
end
|
16
|
+
|
9
17
|
end
|
10
18
|
|
11
19
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# TODO underscore method names to reduce collision
|
2
|
+
|
3
|
+
module Fabrique
|
4
|
+
|
5
|
+
class DataBean < BasicObject
|
6
|
+
|
7
|
+
def initialize(hash, name = nil)
|
8
|
+
@hash = hash
|
9
|
+
@name = name
|
10
|
+
end
|
11
|
+
|
12
|
+
def method_missing(sym, *args)
|
13
|
+
key = include?(sym)
|
14
|
+
|
15
|
+
raise_no_method_error(sym) if key.nil?
|
16
|
+
raise_argument_error(args) unless args.empty?
|
17
|
+
|
18
|
+
glide(sym, fetch(sym))
|
19
|
+
end
|
20
|
+
|
21
|
+
alias_method :send, :method_missing
|
22
|
+
|
23
|
+
def to_s
|
24
|
+
@name or ::Kernel.sprintf("0x%014x", __id__)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def glide(sym, v)
|
30
|
+
v.is_a?(::Hash) ? ::Fabrique::DataBean.new(v, "#{to_s}.#{sym}") : v
|
31
|
+
end
|
32
|
+
|
33
|
+
# TODO investigate possible optimization with Ruby 2.3 frozen strings
|
34
|
+
def fetch(sym)
|
35
|
+
if @hash.include?(sym)
|
36
|
+
@hash[sym]
|
37
|
+
elsif @hash.include?(sym.to_s)
|
38
|
+
@hash[sym.to_s]
|
39
|
+
else
|
40
|
+
raise_no_method_error(sym)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def include?(sym)
|
45
|
+
if @hash.include?(sym)
|
46
|
+
sym
|
47
|
+
elsif @hash.include?(sym.to_s)
|
48
|
+
sym.to_s
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def raise_no_method_error(sym)
|
53
|
+
::Kernel.raise ::NoMethodError.new("undefined method `#{sym}' for #<Fabrique::DataBean:#{to_s}>")
|
54
|
+
end
|
55
|
+
|
56
|
+
def raise_argument_error(args)
|
57
|
+
::Kernel.raise ::ArgumentError.new("wrong number of arguments (given #{args.size}, expected 0)")
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Fabrique
|
2
|
+
|
3
|
+
class GemDefinition
|
4
|
+
|
5
|
+
attr_reader :dependency, :required_as
|
6
|
+
|
7
|
+
def initialize(defn)
|
8
|
+
@dependency = Gem::Dependency.new(defn["name"], defn["version"] || Gem::Requirement.default)
|
9
|
+
@required_as = defn["require"] || defn["name"]
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Fabrique
|
2
|
+
|
3
|
+
class GemLoader
|
4
|
+
|
5
|
+
def initialize(gem_definitions)
|
6
|
+
@gem_defs = gem_definitions
|
7
|
+
deps = @gem_defs.collect(&:dependency).reject { |x| not x.matching_specs.empty? }
|
8
|
+
@gem_set = Gem::RequestSet.new(*deps)
|
9
|
+
end
|
10
|
+
|
11
|
+
def load_gems
|
12
|
+
require "rubygems/dependency_installer"
|
13
|
+
@gem_set.resolve
|
14
|
+
specs = @gem_set.install(Gem::DependencyInstaller::DEFAULT_OPTIONS.merge(document: []))
|
15
|
+
specs.each do |spec|
|
16
|
+
spec.activate
|
17
|
+
end
|
18
|
+
@gem_defs.collect(&:required_as).each { |x| require x }
|
19
|
+
rescue Gem::DependencyError => e
|
20
|
+
raise GemDependencyError.new(e.message)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -6,13 +6,24 @@ module Fabrique
|
|
6
6
|
|
7
7
|
module Constructors
|
8
8
|
|
9
|
+
class FactoryWithCreateMethod
|
10
|
+
|
11
|
+
def initialize(*args)
|
12
|
+
end
|
13
|
+
|
14
|
+
def create
|
15
|
+
ClassWithPositionalArgumentConstructor.new("factory size", "factory color", "factory shape")
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
9
20
|
class ClassWithProperties
|
10
21
|
|
11
22
|
DEFAULT_SIZE = "default size" unless defined?(DEFAULT_SIZE)
|
12
23
|
DEFAULT_COLOR = "default color" unless defined?(DEFAULT_COLOR)
|
13
24
|
DEFAULT_SHAPE = "default shape" unless defined?(DEFAULT_SHAPE)
|
14
25
|
|
15
|
-
attr_accessor :size, :color, :shape
|
26
|
+
attr_accessor :size, :color, :shape, :object
|
16
27
|
|
17
28
|
end
|
18
29
|
|
data/lib/fabrique/version.rb
CHANGED
data/lib/fabrique.rb
CHANGED
@@ -1,7 +1,4 @@
|
|
1
1
|
Dir[File.join(File.dirname(__FILE__), "fabrique", "*.rb")].each { |f| require_relative f }
|
2
|
-
Dir[File.join(File.dirname(__FILE__), "fabrique", "construction", "*.rb")].each { |f| require_relative f }
|
3
|
-
Dir[File.join(File.dirname(__FILE__), "fabrique", "constructor", "*.rb")].each { |f| require_relative f }
|
4
|
-
Dir[File.join(File.dirname(__FILE__), "fabrique", "argument_adaptor", "*.rb")].each { |f| require_relative f }
|
5
2
|
|
6
3
|
module Fabrique
|
7
4
|
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Fabrique::DataBean do
|
6
|
+
|
7
|
+
context "over a string-keyed hash" do
|
8
|
+
|
9
|
+
let(:wrapped) do
|
10
|
+
{
|
11
|
+
"string" => "string value",
|
12
|
+
"number" => 42,
|
13
|
+
"array" => %w[element1 element2 element3].freeze,
|
14
|
+
"hash" => {"key" => "value"}.freeze
|
15
|
+
}.freeze
|
16
|
+
end
|
17
|
+
|
18
|
+
subject { described_class.new(wrapped) }
|
19
|
+
|
20
|
+
it "provides direct method access to the values of top-level scalar keys" do
|
21
|
+
expect(subject.string).to eql wrapped["string"]
|
22
|
+
expect(subject.number).to eql wrapped["number"]
|
23
|
+
expect(subject.array).to eql wrapped["array"]
|
24
|
+
end
|
25
|
+
|
26
|
+
it "provides daisy-chained method access to the values of nested hash keys" do
|
27
|
+
expect(subject.hash.key).to eql wrapped["hash"]["key"]
|
28
|
+
end
|
29
|
+
|
30
|
+
it "raises NoMethodError for access to non-existent top-level keys" do
|
31
|
+
expect { subject.nosuchmethod }.to raise_error(NoMethodError, /undefined method `nosuchmethod' for /)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "raises NoMethodError for access to non-existent nested keys" do
|
35
|
+
expect { subject.hash.nosuchnestedmethod }.to raise_error(NoMethodError, /undefined method `nosuchnestedmethod' for/)
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
context "over a symbol-keyed hash" do
|
41
|
+
|
42
|
+
let(:wrapped) do
|
43
|
+
{
|
44
|
+
string: "string value",
|
45
|
+
number: 42,
|
46
|
+
array: %w[element1 element2 element3].freeze,
|
47
|
+
hash: {key: "value"}.freeze
|
48
|
+
}.freeze
|
49
|
+
end
|
50
|
+
|
51
|
+
subject { described_class.new(wrapped) }
|
52
|
+
|
53
|
+
it "provides direct method access to the values of top-level scalar keys" do
|
54
|
+
expect(subject.string).to eql wrapped[:string]
|
55
|
+
expect(subject.number).to eql wrapped[:number]
|
56
|
+
expect(subject.array).to eql wrapped[:array]
|
57
|
+
end
|
58
|
+
|
59
|
+
it "provides daisy-chained method access to the values of nested hash keys" do
|
60
|
+
expect(subject.hash.key).to eql wrapped[:hash][:key]
|
61
|
+
end
|
62
|
+
|
63
|
+
it "raises NoMethodError for access to non-existent top-level keys" do
|
64
|
+
expect { subject.nosuchmethod }.to raise_error(NoMethodError, /undefined method `nosuchmethod' for /)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "raises NoMethodError for access to non-existent nested keys" do
|
68
|
+
expect { subject.hash.nosuchnestedmethod }.to raise_error(NoMethodError, /undefined method `nosuchnestedmethod' for/)
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
context "regardless of wrapped hash" do
|
74
|
+
|
75
|
+
let(:wrapped) do
|
76
|
+
{
|
77
|
+
"meaning_of_life" => 42,
|
78
|
+
"object_id" => "The best object ever!",
|
79
|
+
"hash" => "A breakfast food type",
|
80
|
+
"top" => {middle: {"bottom" => {key: "value"}}}
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
subject { described_class.new(wrapped) }
|
85
|
+
|
86
|
+
it "goes out of its way to avoid method name collision" do
|
87
|
+
expect(subject.object_id).to eql wrapped["object_id"]
|
88
|
+
expect(subject.hash).to eql wrapped["hash"]
|
89
|
+
end
|
90
|
+
|
91
|
+
it "raises ArgumentError on known method access with arguments" do
|
92
|
+
expect { subject.meaning_of_life(42) }.to raise_error(ArgumentError, /wrong number of arguments \(given 1, expected 0\)/)
|
93
|
+
end
|
94
|
+
|
95
|
+
it "raises NoMethodError on unknown method access with arguments" do
|
96
|
+
expect { subject.nosuchmethod(42) }.to raise_error(NoMethodError, /undefined method `nosuchmethod' for /)
|
97
|
+
end
|
98
|
+
|
99
|
+
context "without a name" do
|
100
|
+
|
101
|
+
subject { described_class.new(wrapped) }
|
102
|
+
|
103
|
+
it "includes its string representation in NoMethodError messages" do
|
104
|
+
expect { subject.nosuchmethod }.to raise_error(/ for #<#{described_class}:#{subject.to_s}>/)
|
105
|
+
end
|
106
|
+
|
107
|
+
it "includes its string representation and nested keys daisy-chained in NoMethodError messages" do
|
108
|
+
expect { subject.top.middle.bottom.nosuchnestedmethod }.to raise_error(/ for #<#{described_class}:#{subject.to_s}.top.middle.bottom>/)
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
context "with a name" do
|
114
|
+
|
115
|
+
subject { described_class.new(wrapped, "config") }
|
116
|
+
|
117
|
+
it "includes its name in NoMethodError messages" do
|
118
|
+
expect { subject.nosuchmethod }.to raise_error(NoMethodError, / for #<#{described_class}:config>/)
|
119
|
+
end
|
120
|
+
|
121
|
+
it "includes its name and nested keys daisy-chained in NoMethodError messages" do
|
122
|
+
expect { subject.top.middle.bottom.nosuchnestedmethod }.to raise_error(/ for #<#{described_class}:config.top.middle.bottom>/)
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|