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.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/Gemfile +1 -0
- data/README.md +187 -2
- data/Rakefile +4 -0
- data/lib/little_boxes.rb +10 -4
- data/lib/little_boxes/box.rb +111 -10
- data/lib/little_boxes/configurable.rb +66 -0
- data/lib/little_boxes/entry.rb +30 -0
- data/lib/little_boxes/entry_definition.rb +28 -0
- data/lib/little_boxes/strategy.rb +79 -0
- data/lib/little_boxes/version.rb +1 -1
- data/little_boxes.gemspec +0 -2
- data/spec/benchmarks_spec.rb +162 -0
- data/spec/docs/readme_spec.rb +32 -0
- data/spec/integration/thread_safety_spec.rb +76 -0
- data/spec/little_boxes/box_spec.rb +222 -234
- data/spec/real_benchmarks_spec.rb +164 -0
- data/spec/spec_helper.rb +24 -6
- metadata +17 -41
- data/lib/little_boxes/defined_dependant.rb +0 -9
- data/lib/little_boxes/dependant.rb +0 -44
- data/lib/little_boxes/dependant_registry.rb +0 -44
- data/lib/little_boxes/memoized_dependant.rb +0 -9
- data/lib/little_boxes/null_logger.rb +0 -8
- data/lib/little_boxes/obtained.rb +0 -13
- data/lib/little_boxes/registry.rb +0 -84
- data/spec/little_boxes/dependant_spec.rb +0 -28
@@ -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
|
data/lib/little_boxes/version.rb
CHANGED
data/little_boxes.gemspec
CHANGED
@@ -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
|
5
|
-
|
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
|
-
|
8
|
-
|
9
|
-
|
10
|
-
subject.let(:logger) { double('logger', loglevel: loglevel) }
|
10
|
+
before do
|
11
|
+
define_class :FoldersBox do
|
12
|
+
include Box
|
11
13
|
|
12
|
-
|
13
|
-
|
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
|
-
|
20
|
-
|
21
|
-
include LittleBoxes::Dependant
|
22
|
-
dependency :logger
|
23
|
-
end
|
18
|
+
define_class :MainBox do
|
19
|
+
include Box
|
24
20
|
|
25
|
-
|
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
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
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
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
47
|
+
define_class :Server do
|
48
|
+
include Configurable
|
49
|
+
|
50
|
+
dependency :logger
|
51
|
+
public :logger
|
41
52
|
end
|
42
53
|
|
43
|
-
|
44
|
-
|
45
|
-
subject.let(:loglevel) { n = n + 1 }
|
54
|
+
define_class :Logger do
|
55
|
+
attr_accessor :level
|
46
56
|
|
47
|
-
|
48
|
-
|
57
|
+
def initialize(attrs)
|
58
|
+
@level = attrs[:level]
|
59
|
+
end
|
49
60
|
end
|
50
61
|
|
51
|
-
|
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
|
-
|
58
|
-
|
59
|
-
include LittleBoxes::Dependant
|
60
|
-
dependency :logger
|
61
|
-
end
|
65
|
+
define_class :UsersCollection do
|
66
|
+
include Configurable
|
62
67
|
|
63
|
-
|
64
|
-
|
65
|
-
expect(subject.server.logger).to be subject.logger
|
68
|
+
dependency :logger
|
69
|
+
public :logger
|
66
70
|
end
|
67
71
|
|
68
|
-
|
69
|
-
|
70
|
-
include LittleBoxes::Dependant
|
71
|
-
dependency :unknown_dep
|
72
|
-
end
|
72
|
+
define_class :FoldersCollection do
|
73
|
+
include Configurable
|
73
74
|
|
74
|
-
|
75
|
-
|
75
|
+
dependency :logger
|
76
|
+
public :logger
|
76
77
|
end
|
77
78
|
|
78
|
-
|
79
|
-
|
80
|
-
include LittleBoxes::Dependant
|
81
|
-
class_dependency :host
|
82
|
-
end
|
79
|
+
define_class :UsersApi do
|
80
|
+
include Configurable
|
83
81
|
|
84
|
-
|
85
|
-
|
86
|
-
expect(subject.server_class.host).to eq 'localhost'
|
82
|
+
class_dependency :logger
|
83
|
+
public_class_method :logger
|
87
84
|
end
|
88
85
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
86
|
+
define_class :Task do
|
87
|
+
include Configurable
|
88
|
+
|
89
|
+
def logger= value
|
90
|
+
@config[:logger] = value
|
93
91
|
end
|
94
92
|
|
95
|
-
|
93
|
+
dependency :logger
|
94
|
+
public :logger
|
96
95
|
|
97
|
-
|
96
|
+
def log_level= value
|
97
|
+
@config[:log_level] = value
|
98
|
+
end
|
98
99
|
|
99
|
-
|
100
|
+
dependency :log_level
|
101
|
+
public :log_level
|
100
102
|
end
|
101
103
|
|
102
|
-
|
103
|
-
|
104
|
-
include LittleBoxes::Dependant
|
105
|
-
|
106
|
-
dependency :logger
|
107
|
-
end
|
104
|
+
define_class :HttpClient do
|
105
|
+
include Configurable
|
108
106
|
|
109
|
-
|
107
|
+
class_dependency :logger
|
108
|
+
public_class_method :logger
|
109
|
+
end
|
110
110
|
|
111
|
-
|
112
|
-
|
111
|
+
define_class :RestClient do
|
112
|
+
include Configurable
|
113
113
|
|
114
|
-
|
115
|
-
|
114
|
+
class_dependency :logger
|
115
|
+
public_class_method :logger
|
116
116
|
end
|
117
117
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
dependency :logger
|
118
|
+
define_class :ApiClient do
|
119
|
+
class << self
|
120
|
+
attr_accessor :logger
|
122
121
|
end
|
122
|
+
end
|
123
123
|
|
124
|
-
|
125
|
-
|
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
|
-
|
127
|
+
class_dependency :idontexist
|
128
|
+
public_class_method :idontexist
|
133
129
|
end
|
134
|
-
end
|
135
130
|
|
136
|
-
|
137
|
-
|
138
|
-
n = 0
|
139
|
-
subject.obtain(:loglevel) { n = n + 1 }
|
131
|
+
define_class :ClientWithDefaults do
|
132
|
+
include Configurable
|
140
133
|
|
141
|
-
|
142
|
-
|
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
|
-
|
146
|
-
|
147
|
-
include LittleBoxes::Dependant
|
148
|
-
dependency :logger
|
149
|
-
end
|
139
|
+
define_class :ClientWithClassDefaults do
|
140
|
+
include Configurable
|
150
141
|
|
151
|
-
|
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
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
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 '
|
167
|
-
|
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
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
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
|
188
|
-
|
189
|
-
|
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
|
-
|
194
|
-
|
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
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
end
|
183
|
+
describe 'server (letc)' do
|
184
|
+
it 'is configured' do
|
185
|
+
expect(server.logger).to be logger
|
186
|
+
end
|
203
187
|
|
204
|
-
|
188
|
+
it 'is memoized' do
|
189
|
+
expect(server).to be server
|
190
|
+
end
|
205
191
|
|
206
|
-
|
192
|
+
it 'respects previously configured dependencies' do
|
193
|
+
expect(task.logger).to be :specific_logger
|
194
|
+
end
|
207
195
|
|
208
|
-
|
196
|
+
it 'has access to the box' do
|
197
|
+
expect(task.log_level).to eq box.log_level
|
209
198
|
end
|
210
199
|
|
211
|
-
|
212
|
-
|
213
|
-
include LittleBoxes::Dependant
|
200
|
+
pending 'test for get, getc, let'
|
201
|
+
end
|
214
202
|
|
215
|
-
|
216
|
-
|
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
|
-
|
208
|
+
it 'has a logger' do
|
209
|
+
expect(users_collection.logger).to be_a Logger
|
210
|
+
end
|
219
211
|
|
220
|
-
|
221
|
-
expect(
|
212
|
+
it 'is main box\'s logger' do
|
213
|
+
expect(users_collection.logger).to be logger
|
214
|
+
end
|
222
215
|
|
223
|
-
|
224
|
-
expect(
|
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 '
|
229
|
-
it '
|
230
|
-
|
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 '
|
239
|
-
|
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
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
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
|
-
|
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 '
|
270
|
-
it '
|
271
|
-
|
272
|
-
|
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
|
-
|
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 '
|
289
|
-
it '
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
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 '
|
302
|
-
|
303
|
-
first
|
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
|
-
|
306
|
-
|
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
|
-
|
275
|
+
it 'configures looking up the tree' do
|
276
|
+
expect(box.folders.collection.logger).to be(logger)
|
277
|
+
end
|
310
278
|
|
311
|
-
|
312
|
-
|
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 '
|
317
|
-
|
318
|
-
|
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
|
-
|
321
|
-
box
|
302
|
+
it 'keeps configuration options' do
|
303
|
+
expect(box.collection.logger).to be_a Logger
|
322
304
|
end
|
323
305
|
|
324
|
-
|
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 '
|
329
|
-
it '
|
330
|
-
|
331
|
-
|
332
|
-
|
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
|
-
|
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 '
|
338
|
-
|
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
|