• Stars
    star
    109
  • Rank 319,077 (Top 7 %)
  • Language
    Ruby
  • License
    MIT License
  • Created over 13 years ago
  • Updated about 7 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Simple Dependency Injection/Service Locator framework for Rails-like applications.

Dependor

build status Code Climate Coverage Status Gem Version Dependency Status

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

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

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
  extend 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.

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.

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

is equivalent to:

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.

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

is equivalent to:

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.

class Foo
  def foo
    do_something_or_other
  end
end

becomes:

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.

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:

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:

class PostCreator
  takes :post_repository

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

can be tested:

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:

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:

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

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

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 "Shorter Class Syntax".

More Repositories

1

bogus

Fake library for Ruby
Ruby
359
star
2

js-test-driver-rails

A Rails warpper for easier integration of JS Test Driver
Ruby
16
star
3

dyplom

JavaScript
10
star
4

dotfiles

my configs
Vim Script
8
star
5

coderetreat.sckrk.com

Site of Code Retreat organized by SCKRK
CSS
5
star
6

redis_ring

A solution for sharding Redis
Ruby
4
star
7

timetracking

Command line interface to update various timetracking sites
Ruby
3
star
8

pouchlist

PouchDB+AngularJS+RxJS TodoMVC demo
JavaScript
3
star
9

process_pool

ProcessPool with interchangeable job queue backends for Ruby
Ruby
3
star
10

fun.rb

Making Ruby Fun(ctional)
Ruby
3
star
11

rhinestone

A proxy to rubygems.org
Ruby
3
star
12

rt-tickspot-client

RubyTime and TickSpot console client
Ruby
3
star
13

angular-prezentacja

Prezentacja o AngularJS na KRUG
JavaScript
3
star
14

sweetjsify

Sweet.js transform for Browserify
JavaScript
2
star
15

hero-notify

GAE app to check HTC Hero availability
Python
2
star
16

angularjs-demo

JavaScript
2
star
17

aspell_edit_dist

Gem that exposes limit_edit_distance function from Aspell.
C++
2
star
18

typing-test

Application to illustrate the services on rails blog post
Ruby
1
star
19

prezio

html5 presentation generator
Ruby
1
star
20

dzone-chrome

Chrome extension for DZone voting.
JavaScript
1
star
21

erb_rcov

Gem to calculate coverage of views in rails
Ruby
1
star
22

psyho-desktop

my desktop config
Ruby
1
star
23

redis_ring_client

Fat client for redis_ring
Ruby
1
star
24

adam.pohorecki.pl

my blog
HTML
1
star
25

snakes

Clojure
1
star
26

dependor-sinatra

Ruby
1
star
27

test-case

Test case for the Netlog OAuth stuff
Ruby
1
star
28

ace-most-tweets

simple script to find out who tweeted most during ace conf
Ruby
1
star
29

hero-notify-gtk

Hero Notifier for Ubuntu
Python
1
star
30

ruby-webapps

prezentacja na kgd.net
Ruby
1
star
31

gcj

some of my solutions to gcj problems
Python
1
star
32

til

Today I Learned
1
star
33

lpasswd

A very simple interface to change LDAP password
JavaScript
1
star
34

ioc_lighting_talk

1
star
35

homer.py

A utility script to rename TV episode files
Python
1
star
36

homer

A gem to rename tv show episode names
Ruby
1
star