fabrique 0.3.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|