<commit_before>[![Build Status](https://travis-ci.org/senny/untangle.png?branch=master)](https://travis-ci.org/senny/untangle)
[![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/senny/untangle)
[![Dependency Status](https://gemnasium.com/senny/untangle.png)](https://gemnasium.com/senny/untangle)

[![endorse](http://api.coderwall.com/senny/endorsecount.png)](http://coderwall.com/senny)

# Untangle

This gem provides a very lightweight API to specify dependencies
between objects. The key concepts behind it are:

* Make dependencies of your objects visible by declaring them
* Depend on roles, not implementation
* Simplify testing by substituting your dependencies with mocks

### Installation

```shell
gem install untangle
```

### Define Dependencies

**explicit**

Explicit definitions contain a direct reference to the actual
dependency. The reference can either be a String:

```ruby
class Person
  extend Untangle

  dependency :translator, 'I18n'
end
```

or a block:

```ruby
class Person
  extend Untangle

  dependency(:translator) { I18n }
end
```

**implicit**

Implicit definitions have no reference to the actual dependency. The
implementation will be inferred from the name. In the following
example, the dependency would resolve to `Translator`.

```ruby
class Person
  extend Untangle

  dependency :translator
end
```

### Global Dependencies

If you have global dependencies, which are used throughout your app,
you can register them globally. This way you can use `dependency`
implicit definitions without a name that corresponds to the implementation.

```ruby
Untangle.register :translator, 'I18n'

class Blog
  extend Untangle

  dependency :translator
end
```

You can also use these registered dependencies to inject them into a
constructor. This technique does not require that the object under
construction knows about `explicit_dependencies`

```ruby
Untangle.register :translator, 'I18n'
ExplicitDependencies.register :people_repository, 'PeopleRepository'

class MyPrcoess

  # The argument names must match the registered dependencies
  def initialize(translator, people_repository)
  end

end

Untangle.inject(MyProcess, :new)
```

### Testing

A key concept behind untangle are isolated unit-tests. When testing in
complete isolation you need to substitute real dependencies with
mocks. Untangle has a custom injector for exaclty this purpose:


**rspec**

```ruby
require 'untangle/rspec'

class SomeProcess
  extend Untangle

  dependency :billing_service

  def bill(users)
    users.each do |user|
      billing_service.bill(user) if user.subscribed?
    end
  end
end

describe SomeProcess do
  let(:billing_service) { described_class.injector.lookup(:billing_service) }
  it 'should bill subscribed users' do
    subscriber = stub(:subscribed? => true)

    billing_service.should_receive(:bill).with(subscriber)

    subject.bill([subscriber])
  end
end
```<commit_msg>README changes<commit_after>[![Build Status](https://travis-ci.org/senny/untangle.png?branch=master)](https://travis-ci.org/senny/untangle)
[![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/senny/untangle)
[![Dependency Status](https://gemnasium.com/senny/untangle.png)](https://gemnasium.com/senny/untangle)

[![endorse](http://api.coderwall.com/senny/endorsecount.png)](http://coderwall.com/senny)

# Untangle

This gem provides a very lightweight API to specify dependencies
between objects. The key concepts behind it are:

* Make dependencies of your objects visible by declaring them
* Depend on roles, not implementation
* Simplify testing by substituting your dependencies with mocks

### Installation

```shell
gem install untangle
```

### Define Dependencies

**explicit**

Explicit definitions contain a direct reference to the actual
dependency. The reference can either be a String:

```ruby
class Person
  extend Untangle

  dependency :translator, I18n
end
```

or a block to defer evaluation:

```ruby
class Person
  extend Untangle

  dependency(:translator) { I18n }
end
```

**implicit**

Implicit definitions have no reference to the actual dependency. The
implementation will be inferred from the name. In the following
example, the dependency would resolve to `Translator`.

```ruby
class Person
  extend Untangle

  dependency :translator
end
```

### Global Dependencies

If you have global dependencies, which are used throughout your app,
you can register them globally. This way you can use `dependency`
implicit definitions without a name that corresponds to the implementation.

```ruby
Untangle.register :translator, I18n

class Blog
  extend Untangle

  dependency :translator
end
```

You can also use these registered dependencies to inject them into a
constructor. This technique does not require that the object under
construction knows about `explicit_dependencies`

```ruby
Untangle.register :translator, I18n
Untangle.register :people_repository, PeopleRepository

class MyPrcoess

  # The argument names must match the registered dependencies
  def initialize(translator, people_repository)
  end

end

Untangle.inject(MyProcess, :new)
```

### Testing

A key concept behind untangle are isolated unit-tests. When testing in
complete isolation you need to substitute real dependencies with
mocks. Untangle has a custom injector for exaclty this purpose:

**rspec**

```ruby
require 'untangle/rspec'

class SomeProcess
  extend Untangle

  dependency :billing_service

  def bill(users)
    users.each do |user|
      billing_service.bill(user) if user.subscribed?
    end
  end
end

describe SomeProcess do
  let(:billing_service) { described_class.injector.lookup(:billing_service) }
  it 'should bill subscribed users' do
    subscriber = stub(:subscribed? => true)

    billing_service.should_receive(:bill).with(subscriber)

    subject.bill([subscriber])
  end
end
```
{'fraction_non_alphanumeric': 0.09026128266033254, 'fraction_numerical': 0.0027146250424160165, 'mean_word_length': 5.02760736196319, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 12, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2289550', 'n_tokens_mistral': 1775, 'n_tokens_neox': 1661, 'n_words': 592}