This is a companion discussion topic for the original entry at https://thoughtbot.com/upcase/videos/dependency-management-in-rails
It appears the github link to payload is not available
Actually, it appears that you havenāt accepted the invitation to join our GitHub team. Since you donāt have access, GitHub shows you a 404 page.
@benorenstein This looks cool, but how do you handle the ever growing config/dependencies.rb
file? I guess we have to accept some complexity somewhere!
Also, you donāt seem to require
the classes in that file either, do you just allow Rails autoloading to work? How does that effect test speed? Iāve been trying to use a fast_spec_helper
for my POROs as much as possible to speed things up.
Also, is it actually ok to try to use the payload
gem in an app? It would have to be a public repo on GitHub to add it to a Gemfile
. We add private repo Gems with a url including an x-oauth-basic
key in the URL, so weād need to know Thoughtbotās key to use it.
Really interesting concept. I think if you are not on the SOLID path, this is going to be really confusing to grasp. Hereās an explanation about it what it does - for myself and other object-orientated noobsā¦
It appears the problem arises when you start using lots of small single responsibility objects and end up with an object that has many collaborators and is difficult to manage or use in your Controllers. For example, in your Controller you may have a NotificationPayment
object that requires a Payment
and a PaymentNotifier
to be passed in or injected.
Rather than specify all your dependencies in your Controller (i.e build up a NotificationPayment
with Payment
and PaymentNotifier
) you can use Payload to extract the setup or configuration for your dependencies to this new file called config/dependencies.rb
so that in your Controller you can simply use a one liner (via the dependencies
method) to instantiate whatever it is that you need.
What seemās cool about this is assuming you have lots of different objects, you can āmix and matchā your objects to compose other objects in this dependencies.rb
file, and only have to worry about that in one place.
I know itās not the same, but it kind of feels like using traits
in factory girl, where you can configure your factory setup how you like and call them when you need. I really appreciate that feature in FactoryGirl
.
I think thereās an obvious pre-requisite to using Payload and thatās having lots of collaborating objects interacting with one another via dependency injection (it is called a DI framework afterall). I find that as a noob beginning to use POROās in my code - my objects are not necessarily injecting more than one other object/collaborator. Notwithstanding, Payload may encourage you to use more factories and decorators which might help make things smaller and easier to manage?
Overall Iād say it looks very cool! Iām not sure if I should being using it straight away but certainly if my objects become ācomposedā enough then Iāll start looking at adding it in, unless @jferris suggests otherwise
@aaronmcadam docs say you can group dependencies in a file like config/dependencies/payments.rb
which may help in managing growing complexity?
Aw geezā¦ youāre right. Iām sorry. I expected it would be open source though, would be awesome to try this in a production application
I think you meant to direct that at me
Yeah I think this gem will solve issues with having lots of smaller objects that can work together to form larger dependencies. Itās cool that you can break up the dependency definitions into separate files.
I think a builders
directory is probably the first place to put Builder/Factory objects extracted from a Controller. If you have a critical mass of builders/factories, maybe then an IoC container like payload
is the right option. Looking at some of the definitions of the objects in upcase-exercises
though, they look quite complicated, so Iād think youād have to make sure the benefits really outweigh the complexity. I think youād have to be careful that you actually have the problem of needing to swap dependencies around and actually have the objects that are composable
This looks cool, but how do you handle the ever growing config/dependencies.rb file?
Thereās some work in progress to create namespaces/modules for dependencies, so that you can create little boxes of dependencies instead of having a giant graph.
I guess we have to accept some complexity somewhere!
Definitely. Some complexity is removed when factories can reduce the number of hops you need a dependency for, but mostly the goal is to break up complexity rather than remove it. The idea is to focus on dependency management in one place and let Ruby classes not worry about building instances of other classes.
Also, you donāt seem to require the classes in that file either, do you just allow Rails autoloading to work? How does that effect test speed? Iāve been trying to use a fast_spec_helper for my POROs as much as possible to speed things up.
Weāre using Rails autoloading, yes. We use Spring, so it doesnāt affect our test speed at all. However, Payload doesnāt reference classes until you actually try to use a dependency, so you can still require things by hand if you prefer, in your specs or elsewhere. Iāve considered adding a facility to Payload to handle file loading, but it hasnāt been a pain point for us so far.
Also, is it actually ok to try to use the payload gem in an app? It would have to be a public repo on GitHub to add it to a Gemfile. We add private repo Gems with a url including an x-oauth-basic key in the URL, so weād need to know Thoughtbotās key to use it.
You can actually just add payload
to your Gemfile
, because itās on rubygems.org. Weāre not announcing or releasing it outside of Upcase yet, because weāre still trying to confirm whether or not this approach is viable and whether or not we want to maintain it. We decided to provide early access to Upcase subscribers, particularly since we talk so much about SOLID on the Weekly Iteration. That being said, we may decide not to continue pursuing this approach, so Payload may disappear some day.
Really interesting concept. I think if you are not on the SOLID path, this is going to be really confusing to grasp.
We havenāt found that to be the case so far when bringing on apprentice-level developers. Thereās some ramp up time, but Iād say the difficulty is less than learning things like the asset pipeline, Mongodb, or Capybara. Learning to manage dependencies well is certainly difficult, but that difficulty exists whether you use small classes, large classes, dependeny management, or anything at all.
Aw geezā¦ youāre right. Iām sorry. I expected it would be open source though, would be awesome to try this in a production application
We try not to open source things until we feel comfortable letting people depend on them. Itās always difficult to decide when that is. As mentioned above, though, youāre welcome to try it out if youād like.
Great insights, thanks @jferris! One thing Iām grappling with at the moment is how to test my dependency injector. Iāve been looking in the exercises
repo, but canāt seem to find any coverage. Do you let your feature specs catch that correct dependencies have been passed?
Hereās an example Iāve been working on, Iām not sure whether itās worth testing that the correct dependencies are passed to the constructor, it feels like it does add some value, otherwise anything could end up being passed as dependencies.
Thereās a Builders::ProfilePage
object that builds a ProfilePage
instance: builders_profile_page_spec.rb Ā· GitHub
Yeah, we assume the feature specs catch problems with dependencies not lining up. We try to keep as little business logic as possible in the dependency configuration. So far, this has not caused any problems.
Awesome episode, thanks @jferris and @benorenstein . We watched this at an interesting time - we just got done extracting some functionality to a dependency injection container. Iām curious about your opinion on the given solution vs what weāre trying out at ControlledVocabularyManager/vocabularies_controller.rb at master Ā· OregonDigital/ControlledVocabularyManager Ā· GitHub and ControlledVocabularyManager/vocabulary_injector.rb at master Ā· OregonDigital/ControlledVocabularyManager Ā· GitHub
I think one important to keep in mind is Inversion of Control. In the VocabulariesController
example, control is still not inverted, because VocabulariesController
instantiates VocabularyInjector
and then delegates to it. This is using composition, which is a good way to break up large components or achieve reuse between several components, but it doesnāt invert control.
If you want to remove the concern of dependency management from your controllers, youāll have to find a way to remove the reference to the VocabularyInjector
class.
This is a good point - at this point weāve declared our controllers as the entry point - largely because we havenāt found a good place to inject that concern. The goal is to invert control below that point - weāre not there yet, but this has been a good route there.
Thank you for the insight.