little_boxes 0.1.0 → 0.3.7

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.
@@ -0,0 +1,30 @@
1
+ module LittleBoxes
2
+ class Entry
3
+ attr_accessor :name, :memo, :box, :eager, :block, :configure, :then_block, :mutex
4
+
5
+ def initialize(name:, eager:, memo:, box:, block:, configure:, then_block:)
6
+ self.name = name
7
+ self.memo = memo
8
+ self.box = box
9
+ self.eager = eager
10
+ self.configure = configure
11
+ self.then_block = then_block
12
+ self.block = block
13
+ self.mutex = Mutex.new if @memo
14
+ end
15
+
16
+ def value
17
+ if @memo
18
+ @mutex.synchronize { @block.call(@box) }
19
+ else
20
+ @block.call(@box)
21
+ end
22
+ end
23
+
24
+ def block= block
25
+ @block = Strategy.for(
26
+ block, memo: @memo, configure: @configure, then_block: @then_block
27
+ )
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,28 @@
1
+ module LittleBoxes
2
+ class EntryDefinition
3
+ attr_accessor :name, :eager, :memo, :block, :configure
4
+
5
+ def initialize(name, eager: false, memo: false, configure: false, then_block: nil, &block)
6
+ self.name = name
7
+ self.memo = memo
8
+ self.eager = eager
9
+ self.configure = configure
10
+ self.block = block
11
+ end
12
+
13
+ def eager!
14
+ self.eager = true
15
+ end
16
+
17
+ def for(box)
18
+ Entry.new(
19
+ name: name, box: box, block: block, memo: memo,
20
+ configure: configure, eager: eager, then_block: @then_block
21
+ )
22
+ end
23
+
24
+ def then(&block)
25
+ @then_block = block
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,79 @@
1
+ module LittleBoxes
2
+ module Strategy
3
+ module_function
4
+
5
+ def for(block, memo: false, configure: false, then_block: nil)
6
+ case [memo, configure, !!then_block]
7
+ when [true, true, true]
8
+ memo_configure_then(block, then_block)
9
+ when [true, true, false]
10
+ memo_configure(block)
11
+ when [true, false, true]
12
+ memo_then(block, then_block)
13
+ when [true, false, false]
14
+ memo(block)
15
+ when [false, true, true]
16
+ configure_then(block, then_block)
17
+ when [false, true, false]
18
+ configure(block)
19
+ when [false, false, true]
20
+ then_block(block, then_block)
21
+ else
22
+ default(block)
23
+ end
24
+ end
25
+
26
+ def memo_configure(block)
27
+ value = nil
28
+ -> (bx) { value ||= do_configure(block.call(bx), bx) }
29
+ end
30
+
31
+ def memo_then(block, then_block)
32
+ value = nil
33
+ -> (bx) { value ||= block.call(bx).tap { |v| then_block.call v, bx } }
34
+ end
35
+
36
+ def memo_configure_then(block, then_block)
37
+ value = nil
38
+ -> (bx) do
39
+ value ||= do_configure(block.call(bx), bx)
40
+ .tap{ |v| then_block.call v, bx }
41
+ end
42
+ end
43
+
44
+ def memo(block)
45
+ value = nil
46
+ -> (bx) { value ||= block.call(bx) }
47
+ end
48
+
49
+ def configure(block)
50
+ -> (bx) { do_configure(block.call(bx), bx) }
51
+ end
52
+
53
+ def then_block(block, then_block)
54
+ -> (bx) { block.call(bx).tap { |v| then_block.call v, bx } }
55
+ end
56
+
57
+ def configure_then(block, then_block)
58
+ -> (bx) do
59
+ do_configure(block.call(bx), bx).tap{ |v| then_block.call v, bx }
60
+ end
61
+ end
62
+
63
+ def default(block)
64
+ block
65
+ end
66
+
67
+ def do_configure(subject, box)
68
+ config = {box: box}
69
+
70
+ config.default_proc = Proc.new do |h, name|
71
+ h[name] = h[:box][name]
72
+ end
73
+
74
+ subject.config = config
75
+
76
+ subject
77
+ end
78
+ end
79
+ end
@@ -1,3 +1,3 @@
1
1
  module LittleBoxes
2
- VERSION = "0.1.0"
2
+ VERSION = "0.3.7"
3
3
  end
@@ -22,6 +22,4 @@ Gem::Specification.new do |spec|
22
22
 
23
23
  spec.add_development_dependency "bundler", "~> 1.3"
24
24
  spec.add_development_dependency "rake"
25
- spec.add_runtime_dependency "forwarding_dsl"
26
- spec.add_runtime_dependency "mini_object"
27
25
  end
@@ -0,0 +1,162 @@
1
+ require_relative 'spec_helper'
2
+ require 'benchmark'
3
+ require'logger'
4
+
5
+ RSpec.describe 'Benchmark the speed', benchmark: true do
6
+ def define_class name, &block
7
+ stub_const(name, Class.new).tap do |c|
8
+ c.class_eval(&block)
9
+ end
10
+ end
11
+
12
+ def iterations
13
+ 100000
14
+ end
15
+
16
+ def test_name
17
+ @__inspect_output.
18
+ gsub(/^"/,'').
19
+ gsub(/" \([^\(]+\)/,'')
20
+ end
21
+
22
+ def measure
23
+ b = Benchmark.measure{ iterations.times { yield } }
24
+ @results << {name: test_name, time: b.real}
25
+ end
26
+
27
+ before :all do
28
+ @results = []
29
+ end
30
+
31
+ after :all do
32
+ File.open 'tmp/benchmarks.log', 'w' do |r|
33
+ @results.sort_by{|res| res[:time] }.each do |res|
34
+ r.puts "#{'%0.4f' % res[:time]}s #{res[:name]}"
35
+ end
36
+ end
37
+
38
+ puts File.read 'tmp/benchmarks.log' if ENV['BENCH_OUT']
39
+ end
40
+
41
+ it '.config -> @config -> .port -> @port' do
42
+ define_class 'Server' do
43
+ def port
44
+ config.port
45
+ end
46
+
47
+ def config
48
+ @config ||= Configuration.new
49
+ end
50
+ end
51
+
52
+ define_class 'Configuration' do
53
+ def port
54
+ @port ||= 80
55
+ end
56
+ end
57
+
58
+ server = Server.new
59
+
60
+ measure { server.port }
61
+ end
62
+
63
+ it '.config -> @config -> [:port]' do
64
+ define_class 'Server' do
65
+ def port
66
+ config[:port]
67
+ end
68
+
69
+ def config
70
+ @config ||= {port: 80}
71
+ end
72
+ end
73
+
74
+ server = Server.new
75
+
76
+ measure { server.port }
77
+ end
78
+
79
+ it '.port_proc -> @port_proc -> .call' do
80
+ define_class 'Server' do
81
+ def port
82
+ port_proc.call
83
+ end
84
+
85
+ def port_proc
86
+ @port_proc ||= lambda { 90 }
87
+ end
88
+ end
89
+
90
+ server = Server.new
91
+
92
+ measure { server.port }
93
+ end
94
+
95
+ it '.port -> @port' do
96
+ define_class 'Server' do
97
+ def port
98
+ the_port
99
+ end
100
+
101
+ def the_port
102
+ @port ||= 80
103
+ end
104
+ end
105
+
106
+ server = Server.new
107
+
108
+ measure { server.port }
109
+ end
110
+
111
+ it '@port' do
112
+ define_class 'Server' do
113
+ def initialize
114
+ @port = 80
115
+ end
116
+
117
+ def port
118
+ @port
119
+ end
120
+ end
121
+
122
+ server = Server.new
123
+
124
+ measure { server.port }
125
+ end
126
+
127
+ it 'Port' do
128
+ define_class 'Server' do
129
+ Port = 80
130
+
131
+ def port
132
+ Port
133
+ end
134
+ end
135
+
136
+ server = Server.new
137
+
138
+ measure { server.port }
139
+ end
140
+
141
+ it '@config[:port]' do
142
+ define_class 'Server' do
143
+ def port
144
+ @config[:port]
145
+ end
146
+
147
+ def initialize
148
+ @config = begin
149
+ Hash.new.tap do |h|
150
+ h.default_proc = lambda do |hash, key|
151
+ h[:port] = 80
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
157
+
158
+ server = Server.new
159
+
160
+ measure { server.port }
161
+ end
162
+ end
@@ -0,0 +1,32 @@
1
+ require_relative '../spec_helper'
2
+
3
+ RSpec.describe 'README.md examples', :docs do
4
+ before { stub_const 'MyApp', Module.new }
5
+
6
+ describe 'Minimal example' do
7
+ it 'has a basic example' do
8
+ module MyApp
9
+ class MainBox
10
+ include LittleBoxes::Box
11
+
12
+ let(:port) { 80 }
13
+ letc(:server) { Server.new }
14
+ end
15
+
16
+ class Server
17
+ include LittleBoxes::Configurable
18
+
19
+ dependency :port
20
+ end
21
+ end
22
+
23
+ box = MyApp::MainBox.new
24
+ # => #<MyBox :server, :logger, :log_path>
25
+
26
+ box.server.port
27
+ # => 80
28
+
29
+ expect(box.server.port).to eq 80
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe "[Integration] Thread safety" do
4
+ let(:configurable) { Struct.new(:config) }
5
+ let(:dependency_block) do
6
+ ->(box) do
7
+ sleep 0.1
8
+ configurable.new
9
+ end
10
+ end
11
+ let(:box) { box_class.new }
12
+
13
+ describe 'LittleBoxes::Box#let' do
14
+ let(:box_class) do
15
+ _dependency_block = dependency_block
16
+
17
+ Class.new do
18
+ include LittleBoxes::Box
19
+ let :dependency, &_dependency_block
20
+ end
21
+ end
22
+
23
+ it 'is thread safe' do
24
+ expect(dependency_block).to receive(:call).once.and_call_original
25
+ 20.times.map { Thread.new { box.dependency } }.each(&:join)
26
+ end
27
+ end
28
+
29
+ describe 'LittleBoxes::Box#letc' do
30
+ let(:box_class) do
31
+ _dependency_block = dependency_block
32
+
33
+ Class.new do
34
+ include LittleBoxes::Box
35
+ letc :dependency, &_dependency_block
36
+ end
37
+ end
38
+
39
+ it 'is thread safe' do
40
+ expect(dependency_block).to receive(:call).once.and_call_original
41
+ 20.times.map { Thread.new { box.dependency } }.each(&:join)
42
+ end
43
+ end
44
+
45
+ describe 'LittleBoxes::Box#get' do
46
+ let(:box_class) do
47
+ _dependency_block = dependency_block
48
+
49
+ Class.new do
50
+ include LittleBoxes::Box
51
+ get :dependency, &_dependency_block
52
+ end
53
+ end
54
+
55
+ it 'does not use a mutex' do
56
+ expect_any_instance_of(Mutex).to_not receive(:synchronize)
57
+ 20.times.map { Thread.new { box.dependency } }.each(&:join)
58
+ end
59
+ end
60
+
61
+ describe 'LittleBoxes::Box#getc' do
62
+ let(:box_class) do
63
+ _dependency_block = dependency_block
64
+
65
+ Class.new do
66
+ include LittleBoxes::Box
67
+ getc :dependency, &_dependency_block
68
+ end
69
+ end
70
+
71
+ it 'does not use a mutex' do
72
+ expect_any_instance_of(Mutex).to_not receive(:synchronize)
73
+ 20.times.map { Thread.new { box.dependency } }.each(&:join)
74
+ end
75
+ end
76
+ end
@@ -1,341 +1,329 @@
1
1
  require_relative '../spec_helper'
2
- require 'ostruct'
3
2
 
4
- describe LittleBoxes::Box do
5
- subject{ described_class.new }
3
+ RSpec.describe 'Box' do
4
+ def define_class name, &block
5
+ stub_const(name.to_s, Class.new).tap do |c|
6
+ c.class_eval(&block)
7
+ end
8
+ end
6
9
 
7
- describe '#customize' do
8
- it 'allows customizing dependencies' do
9
- subject.let(:loglevel) { 0 }
10
- subject.let(:logger) { double('logger', loglevel: loglevel) }
10
+ before do
11
+ define_class :FoldersBox do
12
+ include Box
11
13
 
12
- subject.customize(:logger) do |l|
13
- l.let(:loglevel) { 1 }
14
- end
15
-
16
- expect(subject.logger.loglevel).to eq 1
14
+ letc(:collection) { FoldersCollection.new }
15
+ get(:some_other_logger) { SomeOtherLogger.new }
17
16
  end
18
17
 
19
- it 'supports overriding lets attributes from the outside' do
20
- server_class = Class.new do
21
- include LittleBoxes::Dependant
22
- dependency :logger
23
- end
18
+ define_class :MainBox do
19
+ include Box
24
20
 
25
- subject.let(:logger) { :logger }
21
+ let(:logger) { |c| Logger.new level: c.log_level }
22
+ get(:log_level) { 'INFO' }
23
+ letc(:server) { Server.new }
24
+ getc(:users_collection) { UsersCollection.new }
25
+ letc(:users_api) { UsersApi }
26
26
 
27
- subject.let(:server) { server_class.new }
28
-
29
- subject.customize(:server) do
30
- let(:logger) { :new_logger }
27
+ letc(:task) { Task.new }.then do |task, box|
28
+ task.logger = :specific_logger
29
+ task.log_level = box.log_level
30
+ end
31
+ import FoldersBox
32
+
33
+ eagerc(:http_client) { HttpClient }
34
+ eager(:api_client) { |b| ApiClient.tap { |ac| ac.logger = b.logger } }
35
+ letc(:some_client) { SomeClient }
36
+ box(:folders, FoldersBox)
37
+ box(:files) do
38
+ eagerc(:rest_client) { RestClient }
31
39
  end
32
40
 
33
- expect(subject.server.logger).to be :new_logger
41
+ letc(:client_with_defaults) { |b| b.client_with_defaults_class.new }
42
+ letc(:client_with_defaults_class) { ClientWithDefaults }
43
+ let(:client_with_class_defaults) { |b| b.client_with_class_defaults_class.new }
44
+ letc(:client_with_class_defaults_class) { ClientWithClassDefaults }
34
45
  end
35
- end
36
46
 
37
- describe '#let' do
38
- it 'has freely defined registers with just a lambda' do
39
- subject.let(:loglevel) { 0 }
40
- expect(subject.loglevel).to eq 0
47
+ define_class :Server do
48
+ include Configurable
49
+
50
+ dependency :logger
51
+ public :logger
41
52
  end
42
53
 
43
- it 'memoizes the result' do
44
- n = 0
45
- subject.let(:loglevel) { n = n + 1 }
54
+ define_class :Logger do
55
+ attr_accessor :level
46
56
 
47
- expect(subject.loglevel).to eq 1
48
- expect(subject.loglevel).to eq 1
57
+ def initialize(attrs)
58
+ @level = attrs[:level]
59
+ end
49
60
  end
50
61
 
51
- it 'allows referencing other dependencies within such lambda' do
52
- subject.let(:loglevel) { 0 }
53
- subject.let(:logger) { double('logger', loglevel: loglevel) }
54
- expect(subject.logger.loglevel).to eq 0
62
+ define_class :SomeOtherLogger do
55
63
  end
56
64
 
57
- it 'has instances that have dependencies' do
58
- server_class = Class.new do
59
- include LittleBoxes::Dependant
60
- dependency :logger
61
- end
65
+ define_class :UsersCollection do
66
+ include Configurable
62
67
 
63
- subject.let(:logger) { double('logger') }
64
- subject.let(:server) { server_class.new }
65
- expect(subject.server.logger).to be subject.logger
68
+ dependency :logger
69
+ public :logger
66
70
  end
67
71
 
68
- it 'unknown dependencies raise exception' do
69
- server_class = Class.new do
70
- include LittleBoxes::Dependant
71
- dependency :unknown_dep
72
- end
72
+ define_class :FoldersCollection do
73
+ include Configurable
73
74
 
74
- subject.let(:server) { server_class.new }
75
- expect{ subject.server.unknown_dep }.to raise_error(LittleBoxes::MissingDependency)
75
+ dependency :logger
76
+ public :logger
76
77
  end
77
78
 
78
- it 'has classes that have class dependencies' do
79
- server_class = Class.new do
80
- include LittleBoxes::Dependant
81
- class_dependency :host
82
- end
79
+ define_class :UsersApi do
80
+ include Configurable
83
81
 
84
- subject.let(:host) { 'localhost' }
85
- subject.let(:server_class) { server_class }
86
- expect(subject.server_class.host).to eq 'localhost'
82
+ class_dependency :logger
83
+ public_class_method :logger
87
84
  end
88
85
 
89
- it 'supports defaults' do
90
- server_class = Class.new do
91
- include LittleBoxes::Dependant
92
- dependency :log, default: ->(d){ d.logger }
86
+ define_class :Task do
87
+ include Configurable
88
+
89
+ def logger= value
90
+ @config[:logger] = value
93
91
  end
94
92
 
95
- subject.let(:logger) { :logger }
93
+ dependency :logger
94
+ public :logger
96
95
 
97
- subject.let(:server) { server_class.new }
96
+ def log_level= value
97
+ @config[:log_level] = value
98
+ end
98
99
 
99
- expect(subject.server.log).to be :logger
100
+ dependency :log_level
101
+ public :log_level
100
102
  end
101
103
 
102
- it 'assigns dependencies with lambdas' do
103
- server_class = Class.new do
104
- include LittleBoxes::Dependant
105
-
106
- dependency :logger
107
- end
104
+ define_class :HttpClient do
105
+ include Configurable
108
106
 
109
- subject.let(:server) { server_class.new }
107
+ class_dependency :logger
108
+ public_class_method :logger
109
+ end
110
110
 
111
- subject.let(:logger) { :old }
112
- expect(subject.server.logger).to be :old
111
+ define_class :RestClient do
112
+ include Configurable
113
113
 
114
- subject.let(:logger) { :new }
115
- expect(subject.server.logger).to be :new
114
+ class_dependency :logger
115
+ public_class_method :logger
116
116
  end
117
117
 
118
- it 'supports overriding specific attributes' do
119
- server_class = Class.new do
120
- include LittleBoxes::Dependant
121
- dependency :logger
118
+ define_class :ApiClient do
119
+ class << self
120
+ attr_accessor :logger
122
121
  end
122
+ end
123
123
 
124
- subject.let(:logger) { :logger }
125
- subject.let(:new_logger) { :new_logger }
126
-
127
- subject.let(:server) do
128
- build { server_class.new }
129
- let(:logger) { new_logger }
130
- end
124
+ define_class :SomeClient do
125
+ include Configurable
131
126
 
132
- expect(subject.server.logger).to be :new_logger
127
+ class_dependency :idontexist
128
+ public_class_method :idontexist
133
129
  end
134
- end
135
130
 
136
- describe '#obtain' do
137
- it 'does not memoize the result' do
138
- n = 0
139
- subject.obtain(:loglevel) { n = n + 1 }
131
+ define_class :ClientWithDefaults do
132
+ include Configurable
140
133
 
141
- expect(subject.loglevel).to eq 1
142
- expect(subject.loglevel).to eq 2
134
+ dependency(:dep_with_default) { |b| b.object_id }
135
+ class_dependency(:class_dep_with_default) { |b| b.object_id }
136
+ class_dependency(:another_class_dep_with_default) { |b| b.object_id }
143
137
  end
144
138
 
145
- it 'doesn not inject anything' do
146
- server_class = Class.new do
147
- include LittleBoxes::Dependant
148
- dependency :logger
149
- end
139
+ define_class :ClientWithClassDefaults do
140
+ include Configurable
150
141
 
151
- subject.let(:logger) { double('logger') }
152
- subject.obtain(:server) { server_class.new.tap { |s| s.logger = :old_logger } }
153
- expect(subject.server.logger).to be :old_logger
142
+ class_dependency(:another_class_dep_with_default) { |b| b.object_id }
154
143
  end
155
144
  end
156
145
 
157
- describe '#define' do
158
- it 'does not memoize the result' do
159
- n = 0
160
- subject.define(:loglevel) { n = n + 1 }
161
-
162
- expect(subject.loglevel).to eq 1
163
- expect(subject.loglevel).to eq 2
146
+ let(:box) { MainBox.new }
147
+ define_method(:logger) { box.logger }
148
+ define_method(:server) { box.server }
149
+ define_method(:log_level) { box.log_level }
150
+ define_method(:users_collection) { box.users_collection }
151
+ define_method(:users_api) { box.users_api }
152
+ define_method(:task) { box.task }
153
+ define_method(:http_client) { HttpClient }
154
+ define_method(:rest_client) { RestClient }
155
+ define_method(:api_client) { ApiClient }
156
+ define_method(:some_client) { box.some_client }
157
+
158
+ describe 'box' do
159
+ it 'memoizes' do
160
+ expect(logger).to be logger
164
161
  end
165
162
 
166
- it 'has instances that have dependencies' do
167
- server_class = Class.new do
168
- include LittleBoxes::Dependant
169
- dependency :logger
170
- end
171
-
172
- subject.let(:logger) { double('logger') }
173
- subject.define(:server) { server_class.new }
174
- expect(subject.server.logger).to be subject.logger
163
+ it 'doesn\'t share between instances' do
164
+ expect(logger).not_to be MainBox.new.logger
175
165
  end
166
+ end
176
167
 
177
- it 'unknown dependencies raise exception' do
178
- server_class = Class.new do
179
- include LittleBoxes::Dependant
180
- dependency :unknown_dep
181
- end
182
-
183
- subject.define(:server) { server_class.new }
184
- expect{ subject.server.unknown_dep }.to raise_error(LittleBoxes::MissingDependency)
168
+ describe 'logger' do
169
+ it 'is a new instance every time (get)' do
170
+ expect(log_level).to eq 'INFO'
171
+ expect(log_level).not_to be log_level
185
172
  end
186
173
 
187
- it 'has classes that have class dependencies' do
188
- server_class = Class.new do
189
- include LittleBoxes::Dependant
190
- class_dependency :host
191
- end
174
+ it 'has the log_level (relying on other deps)' do
175
+ expect(logger.level).to eq log_level
176
+ end
192
177
 
193
- subject.let(:host) { 'localhost' }
194
- subject.define(:server_class) { server_class }
195
- expect(subject.server_class.host).to eq 'localhost'
178
+ it 'has memoized log_level (let memoizes)' do
179
+ expect(logger.level).to be logger.level
196
180
  end
181
+ end
197
182
 
198
- it 'supports defaults' do
199
- server_class = Class.new do
200
- include LittleBoxes::Dependant
201
- dependency :log, default: ->(d){ d.logger }
202
- end
183
+ describe 'server (letc)' do
184
+ it 'is configured' do
185
+ expect(server.logger).to be logger
186
+ end
203
187
 
204
- subject.let(:logger) { :logger }
188
+ it 'is memoized' do
189
+ expect(server).to be server
190
+ end
205
191
 
206
- subject.define(:server) { server_class.new }
192
+ it 'respects previously configured dependencies' do
193
+ expect(task.logger).to be :specific_logger
194
+ end
207
195
 
208
- expect(subject.server.log).to be :logger
196
+ it 'has access to the box' do
197
+ expect(task.log_level).to eq box.log_level
209
198
  end
210
199
 
211
- it 'assigns dependencies with lambdas' do
212
- server_class = Class.new do
213
- include LittleBoxes::Dependant
200
+ pending 'test for get, getc, let'
201
+ end
214
202
 
215
- dependency :logger
216
- end
203
+ describe 'users_collection (getc)' do
204
+ it 'doesn\'t memoize' do
205
+ expect(users_collection).not_to be users_collection
206
+ end
217
207
 
218
- subject.define(:server) { server_class.new }
208
+ it 'has a logger' do
209
+ expect(users_collection.logger).to be_a Logger
210
+ end
219
211
 
220
- subject.let(:logger) { :old }
221
- expect(subject.server.logger).to be :old
212
+ it 'is main box\'s logger' do
213
+ expect(users_collection.logger).to be logger
214
+ end
222
215
 
223
- subject.let(:logger) { :new }
224
- expect(subject.server.logger).to be :new
216
+ it 'memoizes the logger' do
217
+ expect(users_collection.logger).to be users_collection.logger
225
218
  end
226
219
  end
227
220
 
228
- describe '#box' do
229
- it 'supports boxes' do
230
- subject.box :loggers do
231
- let(:null) { :null_logger }
232
- let(:file) { :file_logger }
233
- end
234
-
235
- expect(subject.loggers.null).to be :null_logger
221
+ describe 'users_api (class configurable)' do
222
+ it 'has a logger' do
223
+ expect(users_api.logger).to be_a Logger
236
224
  end
237
225
 
238
- it 'supports finding dependencies upwards in the ancestry' do
239
- server_class = Class.new do
240
- include LittleBoxes::Dependant
241
- dependency :logger
242
- end
243
-
244
- subject.let(:logger) { :logger }
245
-
246
- subject.box :servers do
247
- let(:one) { server_class.new }
248
- end
249
-
250
- expect(subject.servers.one.logger).to be :logger
226
+ it 'is main box\'s logger' do
227
+ expect(users_api.logger).to be logger
251
228
  end
229
+ end
252
230
 
253
- it 'defaults within boxes' do
254
- server_class = Class.new do
255
- include LittleBoxes::Dependant
256
- dependency :log, default: ->(a){ a.logger }
257
- end
258
-
259
- subject.let(:logger) { :logger }
260
-
261
- subject.box :servers do
262
- let(:apache) { server_class.new }
263
- end
231
+ describe 'http_client (configured eager loading)' do
232
+ it 'loads on box initialization' do
233
+ box
234
+ expect(http_client.logger).to be_a Logger
235
+ end
264
236
 
265
- expect(subject.servers.apache.log).to be :logger
237
+ it 'doesn\'t eager load the dependencies' do
238
+ expect(box).not_to receive(:logger)
239
+ box
266
240
  end
267
241
  end
268
242
 
269
- describe '#inspect' do
270
- it 'has nice inspect' do
271
- subject.let(:loglevel) { 0 }
272
- subject.let(:logger) { 0 }
273
- expect(subject.inspect).to eq "<LittleBoxes::Box box: loglevel logger>"
243
+ describe 'api_client (configured eager loading)' do
244
+ it 'loads on box initialization' do
245
+ box
246
+ expect(api_client.logger).to be_a Logger
274
247
  end
275
- end
276
-
277
- describe '#path' do
278
- it 'has a path' do
279
- subject.box :second do
280
- box :third do
281
- end
282
- end
283
248
 
284
- expect(subject.second.third.path).to eq [subject, subject.second, subject.second.third]
249
+ it 'doesn\'t eager load the dependencies' do
250
+ expect(box).not_to receive(:logger)
251
+ box
285
252
  end
286
253
  end
287
254
 
288
- describe '#name' do
289
- it 'names boxes' do
290
- subject.name = :first
291
-
292
- subject.box :second do
293
- box :third do
294
- end
295
- end
296
-
297
- expect(subject.second.third.path.map(&:name)).to eq [:first, :second, :third]
255
+ describe 'error handling (unmet dependency)' do
256
+ it 'sends a pretty error' do
257
+ expect { some_client.idontexist }.to raise_error(
258
+ LittleBoxes::DependencyNotFound,
259
+ 'Dependency idontexist not found'
260
+ )
298
261
  end
299
262
  end
300
263
 
301
- describe '#reset' do
302
- it 'has a reset' do
303
- first = subject
264
+ describe 'nested boxes' do
265
+ describe 'given a box' do
266
+ it 'initializes second level box on first level box initialization' do
267
+ expect(FoldersBox).to receive(:new)
268
+ box
269
+ end
304
270
 
305
- first.box :second do
306
- let(:logger) { Object.new }
271
+ it 'allows to navigate to element of second level box' do
272
+ expect(box.folders.collection).to be_a FoldersCollection
307
273
  end
308
274
 
309
- old_logger = first.second.logger
275
+ it 'configures looking up the tree' do
276
+ expect(box.folders.collection.logger).to be(logger)
277
+ end
310
278
 
311
- first.reset
312
- expect(first.second.logger).not_to be old_logger
279
+ it 'has access to parent' do
280
+ expect(box.folders.parent).to be(box)
281
+ end
282
+ end
283
+
284
+ describe 'inline box' do
285
+ it 'eager loads eager loadable stuff on the second level' do
286
+ box
287
+ expect(rest_client.logger).to be logger
288
+ end
313
289
  end
314
290
  end
315
291
 
316
- describe '#root' do
317
- it 'has a root' do
318
- first = subject
292
+ describe 'imported boxes' do
293
+ describe 'given a box' do
294
+ it 'imports entries' do
295
+ expect(box.collection).to be_a(FoldersCollection)
296
+ end
297
+
298
+ it 'keeps memoization options' do
299
+ expect(box.collection).to be(box.collection)
300
+ end
319
301
 
320
- first.box :second do
321
- box(:third) { }
302
+ it 'keeps configuration options' do
303
+ expect(box.collection.logger).to be_a Logger
322
304
  end
323
305
 
324
- expect(first.second.third.root).to eq first
306
+ it 'does not run entries procs' do
307
+ expect(SomeOtherLogger).not_to receive(:new)
308
+ box
309
+ end
325
310
  end
326
311
  end
327
312
 
328
- describe '.new' do
329
- it 'executes block on initialize' do
330
- subject = described_class.new do
331
- let(:loglevel) { 0 }
332
- end
313
+ describe 'defaults' do
314
+ it 'is supported at instance level' do
315
+ client = box.client_with_defaults
316
+ expect(client.dep_with_default).to eq box.object_id
317
+ end
333
318
 
334
- expect(subject.loglevel).to eq 0
319
+ it 'is supported at class level' do
320
+ client_class = box.client_with_defaults_class
321
+ expect(client_class.class_dep_with_default).to eq box.object_id
335
322
  end
336
323
 
337
- it 'can build instances' do
338
- expect(subject).to be_a(described_class)
324
+ it 'is supported at class level and accessible at instance level' do
325
+ client = box.client_with_class_defaults
326
+ expect(client.another_class_dep_with_default).to eq box.object_id
339
327
  end
340
328
  end
341
329
  end