dtognazzini/require_gemfile_example — pull request #8 by dtognazzini (2014-11-08 00:37:50+00:00)
Title: Sharing gem source paths

This shows how I'd like to reuse Bundler's ```path``` across project-local gems.

# The structure
The application depends on blorgh and lorde, from Gemfile.lock:

```
blorgh (0.0.1)
lorde
rails (~> 4.0.10)
throrg
lorde (0.0.1)
rails (~> 4.0.10)
yayaya
throrg (0.0.1)
rails (~> 4.0.10)
```

A dependency tree (all arrows denote "depends on"):
```
require_gemfile_example
| |
v v
blorgh----->lorde
| |
v v
throrg yayaya
```

The top-level subsystems are in [```engines/```](https://github.com/dtognazzini/require_gemfile_example/blob/embeddedGem/engines)

lorde uses a gem internal to just lorde: yayaya. yayaya is in [```engines/lorde/gems/```](https://github.com/dtognazzini/require_gemfile_example/blob/embeddedGem/engines/lorde/gems)

# The mechanics
1. Each level that is composed of internal gems (e.g. require_gemfile_example and lorde) has a sources.gemfile that is required (ala ```require_gemfile```) by the internal gems comprising the "level". Internal gems require this file to pull in gem source specifications for resolving external gems dependencies. _What?_

So....

- [require_gemfile_example/sources.gemfile](https://github.com/dtognazzini/require_gemfile_example/blob/embeddedGem/sources.gemfile) is required by all internal engines/gems [blorgh](https://github.com/dtognazzini/require_gemfile_example/blob/embeddedGem/engines/blorgh/Gemfile#L3), [throrg](https://github.com/dtognazzini/require_gemfile_example/blob/embeddedGem/engines/throrg/Gemfile#L3) and [lorde](https://github.com/dtognazzini/require_gemfile_example/blob/embeddedGem/engines/lorde/Gemfile#L3) (lorde is special, read on). For each of the internal gems (i.e. blorgh, throrg and lorde) this line says "I don't know how to resolve external gems, leave it to the thing (require_gemfile_example) that requires me."
- lorde is also a gem composed of internal gems, so it too defines a [sources.gemfile](https://github.com/dtognazzini/require_gemfile_example/blob/embeddedGem/engines/lorde/sources.gemfile) that is used by its internal gem, [yayaya](https://github.com/dtognazzini/require_gemfile_example/blob/embeddedGem/engines/lorde/gems/yayaya/Gemfile#L3), which says the same thing: "I don't know how to resolve external gems, leave it to the thing (lorde) that requires me." lorde/sources.gemfile only knows how to resolve dependencies local to its own gem sources. It doesn't know how to resolve dependencies external to itself. But... its parent (require_gemfile_example) does know how to resolve these dependencies. lorde/sources.gemfile [delegates](https://github.com/dtognazzini/require_gemfile_example/blob/embeddedGem/engines/lorde/sources.gemfile#L3) external dependency resolution to its parent.
- Each level that is composed of internal gems (e.g. require_gemfile_example and lorde) uses its own sources.gemfile in its Gemfile to avoid copying-and-pasting: require_gemfile_example's [Gemfile](https://github.com/dtognazzini/require_gemfile_example/blob/embeddedGem/Gemfile#L3), lorde's [Gemfile](https://github.com/dtognazzini/require_gemfile_example/blob/embeddedGem/engines/lorde/Gemfile#L3)

2. Each level that is composed of internal gems **and** is required by something else (i.e. lorde) defines a paths.gemfile which specifies ```path``` sources of where to find gem dependencies internal to the "level". _What?_

So....
- lorde is composed of yayaya and lorde is required by both [require_gemfile_example](https://github.com/dtognazzini/require_gemfile_example/blob/embeddedGem/Gemfile#L16) and [blorgh](https://github.com/dtognazzini/require_gemfile_example/blob/embeddedGem/engines/blorgh/blorgh.gemspec#L20). lorde defines a [paths.gemfile](https://github.com/dtognazzini/require_gemfile_example/blob/embeddedGem/engines/lorde/paths.gemfile) that specifies where lorde's dependencies can be found.
- lorde is the only "level" like this in this project. However, if, for some reason, something wanted to aggregate the outer project with another project, require_gemfile_example itself could define a paths.gemfile and this pattern could support this aggregation.

# The benefits

1. **It scales.** The system is both infinitely decomposable and infinitely composable.
2. **Contained blast radius.** Decomposing lorde into a 1000 internal gems doesn't affect any other code (Gemfile, gemspec, sources.gemfile, paths.gemfile) outside of lorde.
3. **Deferred external dependency resolution.** The resolution of dependencies external to the project are deferred to the top level (i.e. require_gemfile_example), which is in the position of authority for deciding how the dependencies of the project as a whole are resolved.