File: README.md

package info (click to toggle)
ruby-dependor 1.0.1-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 264 kB
  • sloc: ruby: 557; makefile: 2; sh: 1
file content (352 lines) | stat: -rw-r--r-- 6,274 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
# Dependor 

[![build status](https://secure.travis-ci.org/psyho/dependor.png)](http://travis-ci.org/psyho/dependor)
[![Code Climate](https://codeclimate.com/github/psyho/dependor.png)](https://codeclimate.com/github/psyho/dependor)
[![Coverage Status](https://coveralls.io/repos/psyho/dependor/badge.png)](https://coveralls.io/r/psyho/dependor)
[![Gem Version](https://badge.fury.io/rb/dependor.png)](http://badge.fury.io/rb/dependor)
[![Dependency Status](https://gemnasium.com/psyho/dependor.png)](https://gemnasium.com/psyho/dependor)

## What is Dependor

Dependor is a set of helpers that make writing Ruby apps that use the dependency injection pattern easier.
It comes as a set of modules, which you can selectively add to your project.
It is designed do play nice with Rails and similar frameworks.

## Manual Dependency Injection

```ruby
class Foo
  def do_foo
    "foo"
  end
end

class Bar
  def initialize(foo)
    @foo = foo
  end

  def do_bar
    @foo.do_foo + "bar"
  end
end

class Injector
  def foo
    Foo.new
  end

  def bar
    Bar.new(foo)
  end
end

class EntryPoint
  def inject
    @injector ||= Injector.new
  end

  def bar
    inject.bar
  end

  def run
    bar.do_bar
  end
end

EntryPoint.new.run
```

## The same thing with Dependor

```ruby
require 'dependor'
require 'dependor/shorty'

class Foo
  def do_foo
    "foo"
  end
end

class Bar
  takes :foo

  def do_bar
    @foo.do_foo + "bar"
  end
end

class Injector
  include Dependor::AutoInject
end

class EntryPoint
  include Dependor::Injectable
  inject_from Injector
  
  inject :bar

  def run
    bar.do_bar
  end
end

EntryPoint.new.run
```

## Dependor::AutoInject

This is the core part of the library.
It looks at the constructor of a class to find out it's dependencies and instantiates it's instances with proper objects injected.
It looks up classes by name.

AutoInject can also use the methods declared on injector as injection sources, which is quite useful for things like configuration.

```ruby
class Injector
  include Dependor::AutoInject

  attr_reader :session

  def initialize(session)
    @session = session
  end

  let(:current_user) { current_user_service.get }
  let(:users_repository) { User }
  let(:comments_repository) { Comment }
end

class CurrentUserService
  takes :session, :users_repository

  def get
    @current_user ||= users_repository.find(session[:current_user_id])
  end
end

class CreatesComments
  takes :current_user, :comments_repository

  def create
    # ...
  end
end

class User < ActiveRecord::Base
end

class Comment < ActiveRecord::Base
end
```

## Dependor::Shorty

This makes the constructor definition less verbose and includes Dependor::Let for shorter method definition syntax.

```ruby
class Foo
  takes :foo, :bar, :baz
  let(:hello) { "world" }
end
```

is equivalent to:

```ruby
class Foo
  attr_reader :foo, :bar, :baz

  def initialize(foo, bar, baz)
    @foo = foo
    @bar = bar
    @baz = baz
  end

  def hello
    "world"
  end
end
```

## Dependor::Constructor

Sometimes you don't want to pollute every class with a `takes` method.
You can then shorten the class declaration with Dependor::Constructor.

```ruby
class Foo
  include Dependor::Constructor(:foo, :bar, :baz)
end
```

is equivalent to:

```ruby
class Foo
  def initialize(foo, bar, baz)
    @foo = foo
    @bar = bar
    @baz = baz
  end
end
```

## Dependor::Let

It allows a simpler syntax to define getter methods.

```ruby
class Foo
  def foo
    do_something_or_other
  end
end
```

becomes:

```ruby
class Foo
  extend Dependor::Let
  let(:foo) { do_something_or_other }
end
```

## Dependor::Injectable

You can include this to make usage of the injector more convenient.
This is used in the entry point of your application, typically a Rails controller.

```ruby
class MyInjector
  def foo
    "foo"
  end
end

class ApplicationController
  extend Dependor::Injectable
  inject_from MyInjector
end

class PostsController < ApplicationController
  inject :foo

  def get
    render text: foo
  end
end
```

Sometimes you might want to pass request, params or session to your injector.
Here is an example, how to do it:

```ruby
require 'dependor/shorty'

class MyInjector
  include Dependor::AutoInject
  
  takes :params, :session, :request

  def foo
    session[:foo]
  end
end

class ApplicationController
  extend Dependor::Injectable

  def injector
    @injector ||= MyInjector.new(params, session, request)
  end
end

class PostsController < ApplicationController
  inject :foo

  def get
    render text: foo
  end
end
```

## Testing

Dependor doesn't add any dependencies to your classes so you can test them any way you like.

Following class:

```ruby
class PostCreator
  takes :post_repository

  def publish(post)
     post_repository.store(post)
  end
end
```

can be tested:

```ruby
let(:post_repository) { stub }
let(:creator) { PostCreator.new(post_repository }

it "stores posts" do
  post = Post.new
  post_repository.expects(:store).with(post)
  creator.publish(post)
end
```

## Dependor::Isolate

Dependor::Isolate provides `isolate` function that creates an instance of given class with dependencies taken from a local context. It can be easily integrated with rspec by requiring 'dependor/rspec'.

Previous example can be rewritten as:

```ruby
require 'dependor/rspec'

let(:post_repository) { stub }
let(:creator) { isolate(PostCreator) }

it "stores posts" do
  post = Post.new
  post_repository.expects(store).with(post)
  creator.publish(post)
end
```

Dependencies are taken from methods available in local context, but they can be specified in paramaters as well:

```ruby
post_repository = stub
creator = isolate(PostCreator, post_repository: post_repository)
```

Or they can be captured from local variables when syntax with block is used:

```ruby
post_repository = stub
creator = isolate{PostCreator}
```

## License

MIT. See the MIT-LICENSE file.

## Author

Adam Pohorecki

## Acknowledgements

Dependor::Shorty is inspired (or rather blatantly copied) from Gary Bernhardt's [Destroy All Software Screencast][das] ["Shorter Class Syntax"][shorter-syntax].

[das]: http://www.destroyallsoftware.com
[shorter-syntax]: https://www.destroyallsoftware.com/screencasts/catalog/shorter-class-syntax